1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.configuration;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.Reader;
24 import java.io.StringReader;
25 import java.io.StringWriter;
26 import java.io.Writer;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35
36 import javax.xml.parsers.DocumentBuilder;
37 import javax.xml.parsers.DocumentBuilderFactory;
38 import javax.xml.parsers.ParserConfigurationException;
39 import javax.xml.transform.OutputKeys;
40 import javax.xml.transform.Result;
41 import javax.xml.transform.Source;
42 import javax.xml.transform.Transformer;
43 import javax.xml.transform.TransformerException;
44 import javax.xml.transform.TransformerFactory;
45 import javax.xml.transform.TransformerFactoryConfigurationError;
46 import javax.xml.transform.dom.DOMSource;
47 import javax.xml.transform.stream.StreamResult;
48
49 import org.apache.commons.configuration.resolver.DefaultEntityResolver;
50 import org.apache.commons.configuration.resolver.EntityRegistry;
51 import org.apache.commons.configuration.tree.ConfigurationNode;
52 import org.apache.commons.logging.LogFactory;
53 import org.w3c.dom.Attr;
54 import org.w3c.dom.CDATASection;
55 import org.w3c.dom.DOMException;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.Element;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.NodeList;
60 import org.w3c.dom.Text;
61 import org.xml.sax.EntityResolver;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
64 import org.xml.sax.SAXParseException;
65 import org.xml.sax.helpers.DefaultHandler;
66
67
68
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 public class XMLConfiguration extends AbstractHierarchicalFileConfiguration
169 implements EntityResolver, EntityRegistry
170 {
171
172
173
174 private static final long serialVersionUID = 2453781111653383552L;
175
176
177 private static final String DEFAULT_ROOT_NAME = "configuration";
178
179
180 private static final String ATTR_SPACE = "xml:space";
181
182
183 private static final String VALUE_PRESERVE = "preserve";
184
185
186 private static final char ATTR_VALUE_DELIMITER = '|';
187
188
189 private static final String JAXP_SCHEMA_LANGUAGE =
190 "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
191
192
193 private static final String W3C_XML_SCHEMA =
194 "http://www.w3.org/2001/XMLSchema";
195
196
197 private Document document;
198
199
200 private String rootElementName;
201
202
203 private String publicID;
204
205
206 private String systemID;
207
208
209 private DocumentBuilder documentBuilder;
210
211
212 private boolean validating;
213
214
215 private boolean schemaValidation;
216
217
218 private boolean attributeSplittingDisabled;
219
220
221 private EntityResolver entityResolver = new DefaultEntityResolver();
222
223
224
225
226 public XMLConfiguration()
227 {
228 super();
229 setLogger(LogFactory.getLog(XMLConfiguration.class));
230 }
231
232
233
234
235
236
237
238
239
240
241
242 public XMLConfiguration(HierarchicalConfiguration c)
243 {
244 super(c);
245 clearReferences(getRootNode());
246 setRootElementName(getRootNode().getName());
247 setLogger(LogFactory.getLog(XMLConfiguration.class));
248 }
249
250
251
252
253
254
255
256
257 public XMLConfiguration(String fileName) throws ConfigurationException
258 {
259 super(fileName);
260 setLogger(LogFactory.getLog(XMLConfiguration.class));
261 }
262
263
264
265
266
267
268
269
270 public XMLConfiguration(File file) throws ConfigurationException
271 {
272 super(file);
273 setLogger(LogFactory.getLog(XMLConfiguration.class));
274 }
275
276
277
278
279
280
281
282
283 public XMLConfiguration(URL url) throws ConfigurationException
284 {
285 super(url);
286 setLogger(LogFactory.getLog(XMLConfiguration.class));
287 }
288
289
290
291
292
293
294
295
296
297 public String getRootElementName()
298 {
299 if (getDocument() == null)
300 {
301 return (rootElementName == null) ? DEFAULT_ROOT_NAME : rootElementName;
302 }
303 else
304 {
305 return getDocument().getDocumentElement().getNodeName();
306 }
307 }
308
309
310
311
312
313
314
315
316
317
318
319
320
321 public void setRootElementName(String name)
322 {
323 if (getDocument() != null)
324 {
325 throw new UnsupportedOperationException("The name of the root element "
326 + "cannot be changed when loaded from an XML document!");
327 }
328 rootElementName = name;
329 getRootNode().setName(name);
330 }
331
332
333
334
335
336
337
338
339
340 public DocumentBuilder getDocumentBuilder()
341 {
342 return documentBuilder;
343 }
344
345
346
347
348
349
350
351
352
353
354
355 public void setDocumentBuilder(DocumentBuilder documentBuilder)
356 {
357 this.documentBuilder = documentBuilder;
358 }
359
360
361
362
363
364
365
366
367
368 public String getPublicID()
369 {
370 return publicID;
371 }
372
373
374
375
376
377
378
379
380
381 public void setPublicID(String publicID)
382 {
383 this.publicID = publicID;
384 }
385
386
387
388
389
390
391
392
393
394 public String getSystemID()
395 {
396 return systemID;
397 }
398
399
400
401
402
403
404
405
406
407 public void setSystemID(String systemID)
408 {
409 this.systemID = systemID;
410 }
411
412
413
414
415
416
417
418 public boolean isValidating()
419 {
420 return validating;
421 }
422
423
424
425
426
427
428
429
430
431 public void setValidating(boolean validating)
432 {
433 if (!schemaValidation)
434 {
435 this.validating = validating;
436 }
437 }
438
439
440
441
442
443
444
445
446 public boolean isSchemaValidation()
447 {
448 return schemaValidation;
449 }
450
451
452
453
454
455
456
457
458
459
460
461 public void setSchemaValidation(boolean schemaValidation)
462 {
463 this.schemaValidation = schemaValidation;
464 if (schemaValidation)
465 {
466 this.validating = true;
467 }
468 }
469
470
471
472
473
474
475
476 public void setEntityResolver(EntityResolver resolver)
477 {
478 this.entityResolver = resolver;
479 }
480
481
482
483
484
485
486 public EntityResolver getEntityResolver()
487 {
488 return this.entityResolver;
489 }
490
491
492
493
494
495
496
497
498 public boolean isAttributeSplittingDisabled()
499 {
500 return attributeSplittingDisabled;
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551 public void setAttributeSplittingDisabled(boolean attributeSplittingDisabled)
552 {
553 this.attributeSplittingDisabled = attributeSplittingDisabled;
554 }
555
556
557
558
559
560
561
562
563 public Document getDocument()
564 {
565 return document;
566 }
567
568
569
570
571
572 @Override
573 public void clear()
574 {
575 super.clear();
576 setRoot(new Node());
577 document = null;
578 }
579
580
581
582
583
584
585
586 public void initProperties(Document document, boolean elemRefs)
587 {
588 if (document.getDoctype() != null)
589 {
590 setPublicID(document.getDoctype().getPublicId());
591 setSystemID(document.getDoctype().getSystemId());
592 }
593
594 constructHierarchy(getRoot(), document.getDocumentElement(), elemRefs, true);
595 getRootNode().setName(document.getDocumentElement().getNodeName());
596 if (elemRefs)
597 {
598 getRoot().setReference(document.getDocumentElement());
599 }
600 }
601
602
603
604
605
606
607
608
609
610
611
612 private void constructHierarchy(Node node, Element element, boolean elemRefs, boolean trim)
613 {
614 boolean trimFlag = shouldTrim(element, trim);
615 processAttributes(node, element, elemRefs);
616 StringBuilder buffer = new StringBuilder();
617 NodeList list = element.getChildNodes();
618 for (int i = 0; i < list.getLength(); i++)
619 {
620 org.w3c.dom.Node w3cNode = list.item(i);
621 if (w3cNode instanceof Element)
622 {
623 Element child = (Element) w3cNode;
624 Node childNode = new XMLNode(child.getTagName(),
625 elemRefs ? child : null);
626 constructHierarchy(childNode, child, elemRefs, trimFlag);
627 node.addChild(childNode);
628 handleDelimiters(node, childNode, trimFlag);
629 }
630 else if (w3cNode instanceof Text)
631 {
632 Text data = (Text) w3cNode;
633 buffer.append(data.getData());
634 }
635 }
636
637 String text = buffer.toString();
638 if (trimFlag)
639 {
640 text = text.trim();
641 }
642 if (text.length() > 0 || (!node.hasChildren() && node != getRoot()))
643 {
644 node.setValue(text);
645 }
646 }
647
648
649
650
651
652
653
654
655
656 private void processAttributes(Node node, Element element, boolean elemRefs)
657 {
658 NamedNodeMap attributes = element.getAttributes();
659 for (int i = 0; i < attributes.getLength(); ++i)
660 {
661 org.w3c.dom.Node w3cNode = attributes.item(i);
662 if (w3cNode instanceof Attr)
663 {
664 Attr attr = (Attr) w3cNode;
665 List<String> values;
666 if (isAttributeSplittingDisabled())
667 {
668 values = Collections.singletonList(attr.getValue());
669 }
670 else
671 {
672 values = PropertyConverter.split(attr.getValue(),
673 isDelimiterParsingDisabled() ? ATTR_VALUE_DELIMITER
674 : getListDelimiter());
675 }
676
677 for (String value : values)
678 {
679 Node child = new XMLNode(attr.getName(), elemRefs ? element
680 : null);
681 child.setValue(value);
682 node.addAttribute(child);
683 }
684 }
685 }
686 }
687
688
689
690
691
692
693
694
695
696 private void handleDelimiters(Node parent, Node child, boolean trim)
697 {
698 if (child.getValue() != null)
699 {
700 List<String> values;
701 if (isDelimiterParsingDisabled())
702 {
703 values = new ArrayList<String>();
704 values.add(child.getValue().toString());
705 }
706 else
707 {
708 values = PropertyConverter.split(child.getValue().toString(),
709 getListDelimiter(), trim);
710 }
711
712 if (values.size() > 1)
713 {
714 Iterator<String> it = values.iterator();
715
716 Node c = createNode(child.getName());
717 c.setValue(it.next());
718
719 for (ConfigurationNode ndAttr : child.getAttributes())
720 {
721 ndAttr.setReference(null);
722 c.addAttribute(ndAttr);
723 }
724 parent.remove(child);
725 parent.addChild(c);
726
727
728 while (it.hasNext())
729 {
730 c = new XMLNode(child.getName(), null);
731 c.setValue(it.next());
732 parent.addChild(c);
733 }
734 }
735 else if (values.size() == 1)
736 {
737
738
739 child.setValue(values.get(0));
740 }
741 }
742 }
743
744
745
746
747
748
749
750
751
752
753
754
755 private boolean shouldTrim(Element element, boolean currentTrim)
756 {
757 Attr attr = element.getAttributeNode(ATTR_SPACE);
758
759 if (attr == null)
760 {
761 return currentTrim;
762 }
763 else
764 {
765 return !VALUE_PRESERVE.equals(attr.getValue());
766 }
767 }
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782 protected DocumentBuilder createDocumentBuilder()
783 throws ParserConfigurationException
784 {
785 if (getDocumentBuilder() != null)
786 {
787 return getDocumentBuilder();
788 }
789 else
790 {
791 DocumentBuilderFactory factory = DocumentBuilderFactory
792 .newInstance();
793 if (isValidating())
794 {
795 factory.setValidating(true);
796 if (isSchemaValidation())
797 {
798 factory.setNamespaceAware(true);
799 factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
800 }
801 }
802
803 DocumentBuilder result = factory.newDocumentBuilder();
804 result.setEntityResolver(this.entityResolver);
805
806 if (isValidating())
807 {
808
809 result.setErrorHandler(new DefaultHandler()
810 {
811 @Override
812 public void error(SAXParseException ex) throws SAXException
813 {
814 throw ex;
815 }
816 });
817 }
818 return result;
819 }
820 }
821
822
823
824
825
826
827
828 protected Document createDocument() throws ConfigurationException
829 {
830 try
831 {
832 if (document == null)
833 {
834 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
835 Document newDocument = builder.newDocument();
836 Element rootElem = newDocument.createElement(getRootElementName());
837 newDocument.appendChild(rootElem);
838 document = newDocument;
839 }
840
841 XMLBuilderVisitor builder = new XMLBuilderVisitor(document,
842 isDelimiterParsingDisabled() ? (char) 0 : getListDelimiter(),
843 isAttributeSplittingDisabled());
844 builder.processDocument(getRoot());
845 initRootElementText(document, getRootNode().getValue());
846 return document;
847 }
848 catch (DOMException domEx)
849 {
850 throw new ConfigurationException(domEx);
851 }
852 catch (ParserConfigurationException pex)
853 {
854 throw new ConfigurationException(pex);
855 }
856 }
857
858
859
860
861
862
863
864 private void initRootElementText(Document doc, Object value)
865 {
866 Element elem = doc.getDocumentElement();
867 NodeList children = elem.getChildNodes();
868
869
870 for (int i = 0; i < children.getLength(); i++)
871 {
872 org.w3c.dom.Node nd = children.item(i);
873 if (nd.getNodeType() == org.w3c.dom.Node.TEXT_NODE)
874 {
875 elem.removeChild(nd);
876 }
877 }
878
879 if (value != null)
880 {
881
882 elem.appendChild(doc.createTextNode(String.valueOf(value)));
883 }
884 }
885
886
887
888
889
890
891
892
893 @Override
894 protected Node createNode(String name)
895 {
896 return new XMLNode(name, null);
897 }
898
899
900
901
902
903
904
905 @Override
906 public void load(InputStream in) throws ConfigurationException
907 {
908 load(new InputSource(in));
909 }
910
911
912
913
914
915
916
917
918
919
920
921 public void load(Reader in) throws ConfigurationException
922 {
923 load(new InputSource(in));
924 }
925
926
927
928
929
930
931 private void load(InputSource source) throws ConfigurationException
932 {
933 try
934 {
935 URL sourceURL = getDelegate().getURL();
936 if (sourceURL != null)
937 {
938 source.setSystemId(sourceURL.toString());
939 }
940
941 DocumentBuilder builder = createDocumentBuilder();
942 Document newDocument = builder.parse(source);
943 Document oldDocument = document;
944 document = null;
945 initProperties(newDocument, oldDocument == null);
946 document = (oldDocument == null) ? newDocument : oldDocument;
947 }
948 catch (SAXParseException spe)
949 {
950 throw new ConfigurationException("Error parsing " + source.getSystemId(), spe);
951 }
952 catch (Exception e)
953 {
954 this.getLogger().debug("Unable to load the configuraton", e);
955 throw new ConfigurationException("Unable to load the configuration", e);
956 }
957 }
958
959
960
961
962
963
964
965 public void save(Writer writer) throws ConfigurationException
966 {
967 try
968 {
969 Transformer transformer = createTransformer();
970 Source source = new DOMSource(createDocument());
971 Result result = new StreamResult(writer);
972 transformer.transform(source, result);
973 }
974 catch (TransformerException e)
975 {
976 throw new ConfigurationException("Unable to save the configuration", e);
977 }
978 catch (TransformerFactoryConfigurationError e)
979 {
980 throw new ConfigurationException("Unable to save the configuration", e);
981 }
982 }
983
984
985
986
987
988 public void validate() throws ConfigurationException
989 {
990 try
991 {
992 Transformer transformer = createTransformer();
993 Source source = new DOMSource(createDocument());
994 StringWriter writer = new StringWriter();
995 Result result = new StreamResult(writer);
996 transformer.transform(source, result);
997 Reader reader = new StringReader(writer.getBuffer().toString());
998 DocumentBuilder builder = createDocumentBuilder();
999 builder.parse(new InputSource(reader));
1000 }
1001 catch (SAXException e)
1002 {
1003 throw new ConfigurationException("Validation failed", e);
1004 }
1005 catch (IOException e)
1006 {
1007 throw new ConfigurationException("Validation failed", e);
1008 }
1009 catch (TransformerException e)
1010 {
1011 throw new ConfigurationException("Validation failed", e);
1012 }
1013 catch (ParserConfigurationException pce)
1014 {
1015 throw new ConfigurationException("Validation failed", pce);
1016 }
1017 }
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029 protected Transformer createTransformer() throws TransformerException
1030 {
1031 Transformer transformer = TransformerFactory.newInstance()
1032 .newTransformer();
1033
1034 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1035 if (getEncoding() != null)
1036 {
1037 transformer.setOutputProperty(OutputKeys.ENCODING, getEncoding());
1038 }
1039 if (getPublicID() != null)
1040 {
1041 transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
1042 getPublicID());
1043 }
1044 if (getSystemID() != null)
1045 {
1046 transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
1047 getSystemID());
1048 }
1049
1050 return transformer;
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 @Override
1062 public Object clone()
1063 {
1064 XMLConfiguration copy = (XMLConfiguration) super.clone();
1065
1066
1067 copy.document = null;
1068 copy.setDelegate(copy.createDelegate());
1069
1070 clearReferences(copy.getRootNode());
1071
1072 return copy;
1073 }
1074
1075
1076
1077
1078
1079
1080
1081 @Override
1082 protected FileConfigurationDelegate createDelegate()
1083 {
1084 return new XMLFileConfigurationDelegate();
1085 }
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096 @Override
1097 public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
1098 {
1099 if (nodes != null && !nodes.isEmpty())
1100 {
1101 Collection<XMLNode> xmlNodes;
1102 xmlNodes = new ArrayList<XMLNode>(nodes.size());
1103 for (ConfigurationNode node : nodes)
1104 {
1105 xmlNodes.add(convertToXMLNode(node));
1106 }
1107 super.addNodes(key, xmlNodes);
1108 }
1109 else
1110 {
1111 super.addNodes(key, nodes);
1112 }
1113 }
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126 private XMLNode convertToXMLNode(ConfigurationNode node)
1127 {
1128 if (node instanceof XMLNode)
1129 {
1130 return (XMLNode) node;
1131 }
1132
1133 XMLNode nd = (XMLNode) createNode(node.getName());
1134 nd.setValue(node.getValue());
1135 nd.setAttribute(node.isAttribute());
1136 for (ConfigurationNode child : node.getChildren())
1137 {
1138 nd.addChild(convertToXMLNode(child));
1139 }
1140 for (ConfigurationNode attr : node.getAttributes())
1141 {
1142 nd.addAttribute(convertToXMLNode(attr));
1143 }
1144 return nd;
1145 }
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183 public void registerEntityId(String publicId, URL entityURL)
1184 {
1185 if (entityResolver instanceof EntityRegistry)
1186 {
1187 ((EntityRegistry) entityResolver).registerEntityId(publicId, entityURL);
1188 }
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 @Deprecated
1205 public InputSource resolveEntity(String publicId, String systemId)
1206 throws SAXException
1207 {
1208 try
1209 {
1210 return entityResolver.resolveEntity(publicId, systemId);
1211 }
1212 catch (IOException e)
1213 {
1214 throw new SAXException(e);
1215 }
1216 }
1217
1218
1219
1220
1221
1222
1223
1224 public Map<String, URL> getRegisteredEntities()
1225 {
1226 if (entityResolver instanceof EntityRegistry)
1227 {
1228 return ((EntityRegistry) entityResolver).getRegisteredEntities();
1229 }
1230 return new HashMap<String, URL>();
1231 }
1232
1233
1234
1235
1236
1237 class XMLNode extends Node
1238 {
1239
1240
1241
1242 private static final long serialVersionUID = -4133988932174596562L;
1243
1244
1245
1246
1247
1248
1249
1250
1251 public XMLNode(String name, Element elem)
1252 {
1253 super(name);
1254 setReference(elem);
1255 }
1256
1257
1258
1259
1260
1261
1262
1263 @Override
1264 public void setValue(Object value)
1265 {
1266 super.setValue(value);
1267
1268 if (getReference() != null && document != null)
1269 {
1270 if (isAttribute())
1271 {
1272 updateAttribute();
1273 }
1274 else
1275 {
1276 updateElement(value);
1277 }
1278 }
1279 }
1280
1281
1282
1283
1284 @Override
1285 protected void removeReference()
1286 {
1287 if (getReference() != null)
1288 {
1289 Element element = (Element) getReference();
1290 if (isAttribute())
1291 {
1292 updateAttribute();
1293 }
1294 else
1295 {
1296 org.w3c.dom.Node parentElem = element.getParentNode();
1297 if (parentElem != null)
1298 {
1299 parentElem.removeChild(element);
1300 }
1301 }
1302 }
1303 }
1304
1305
1306
1307
1308
1309
1310 private void updateElement(Object value)
1311 {
1312 Text txtNode = findTextNodeForUpdate();
1313 if (value == null)
1314 {
1315
1316 if (txtNode != null)
1317 {
1318 ((Element) getReference()).removeChild(txtNode);
1319 }
1320 }
1321 else
1322 {
1323 if (txtNode == null)
1324 {
1325 String newValue = isDelimiterParsingDisabled() ? value.toString()
1326 : PropertyConverter.escapeDelimiters(value.toString(), getListDelimiter());
1327 txtNode = document.createTextNode(newValue);
1328 if (((Element) getReference()).getFirstChild() != null)
1329 {
1330 ((Element) getReference()).insertBefore(txtNode,
1331 ((Element) getReference()).getFirstChild());
1332 }
1333 else
1334 {
1335 ((Element) getReference()).appendChild(txtNode);
1336 }
1337 }
1338 else
1339 {
1340 String newValue = isDelimiterParsingDisabled() ? value.toString()
1341 : PropertyConverter.escapeDelimiters(value.toString(), getListDelimiter());
1342 txtNode.setNodeValue(newValue);
1343 }
1344 }
1345 }
1346
1347
1348
1349
1350
1351 private void updateAttribute()
1352 {
1353 XMLBuilderVisitor.updateAttribute(getParent(), getName(), getListDelimiter(),
1354 isAttributeSplittingDisabled());
1355 }
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365 private Text findTextNodeForUpdate()
1366 {
1367 Text result = null;
1368 Element elem = (Element) getReference();
1369
1370 NodeList children = elem.getChildNodes();
1371 Collection<org.w3c.dom.Node> textNodes = new ArrayList<org.w3c.dom.Node>();
1372 for (int i = 0; i < children.getLength(); i++)
1373 {
1374 org.w3c.dom.Node nd = children.item(i);
1375 if (nd instanceof Text)
1376 {
1377 if (result == null)
1378 {
1379 result = (Text) nd;
1380 }
1381 else
1382 {
1383 textNodes.add(nd);
1384 }
1385 }
1386 }
1387
1388
1389 if (result instanceof CDATASection)
1390 {
1391 textNodes.add(result);
1392 result = null;
1393 }
1394
1395
1396 for (org.w3c.dom.Node tn : textNodes)
1397 {
1398 elem.removeChild(tn);
1399 }
1400 return result;
1401 }
1402 }
1403
1404
1405
1406
1407
1408 static class XMLBuilderVisitor extends BuilderVisitor
1409 {
1410
1411 private Document document;
1412
1413
1414 private final char listDelimiter;
1415
1416
1417 private boolean isAttributeSplittingDisabled;
1418
1419
1420
1421
1422
1423
1424
1425
1426 public XMLBuilderVisitor(Document doc, char listDelimiter, boolean isAttributeSplittingDisabled)
1427 {
1428 document = doc;
1429 this.listDelimiter = listDelimiter;
1430 this.isAttributeSplittingDisabled = isAttributeSplittingDisabled;
1431 }
1432
1433
1434
1435
1436
1437
1438 public void processDocument(Node rootNode)
1439 {
1440 rootNode.visit(this, null);
1441 }
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453 @Override
1454 protected Object insert(Node newNode, Node parent, Node sibling1, Node sibling2)
1455 {
1456 if (newNode.isAttribute())
1457 {
1458 updateAttribute(parent, getElement(parent), newNode.getName(), listDelimiter,
1459 isAttributeSplittingDisabled);
1460 return null;
1461 }
1462
1463 else
1464 {
1465 Element elem = document.createElement(newNode.getName());
1466 if (newNode.getValue() != null)
1467 {
1468 String txt = newNode.getValue().toString();
1469 if (listDelimiter != 0)
1470 {
1471 txt = PropertyConverter.escapeListDelimiter(txt, listDelimiter);
1472 }
1473 elem.appendChild(document.createTextNode(txt));
1474 }
1475 if (sibling2 == null)
1476 {
1477 getElement(parent).appendChild(elem);
1478 }
1479 else if (sibling1 != null)
1480 {
1481 getElement(parent).insertBefore(elem, getElement(sibling1).getNextSibling());
1482 }
1483 else
1484 {
1485 getElement(parent).insertBefore(elem, getElement(parent).getFirstChild());
1486 }
1487 return elem;
1488 }
1489 }
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501 private static void updateAttribute(Node node, Element elem, String name, char listDelimiter,
1502 boolean isAttributeSplittingDisabled)
1503 {
1504 if (node != null && elem != null)
1505 {
1506 boolean hasAttribute = false;
1507 List<ConfigurationNode> attrs = node.getAttributes(name);
1508 StringBuilder buf = new StringBuilder();
1509 char delimiter = (listDelimiter != 0) ? listDelimiter : ATTR_VALUE_DELIMITER;
1510 for (ConfigurationNode attr : attrs)
1511 {
1512 if (attr.getValue() != null)
1513 {
1514 hasAttribute = true;
1515 if (buf.length() > 0)
1516 {
1517 buf.append(delimiter);
1518 }
1519 String value = isAttributeSplittingDisabled ? attr.getValue().toString()
1520 : PropertyConverter.escapeDelimiters(attr.getValue().toString(),
1521 delimiter);
1522 buf.append(value);
1523 }
1524 attr.setReference(elem);
1525 }
1526
1527 if (!hasAttribute)
1528 {
1529 elem.removeAttribute(name);
1530 }
1531 else
1532 {
1533 elem.setAttribute(name, buf.toString());
1534 }
1535 }
1536 }
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548 static void updateAttribute(Node node, String name, char listDelimiter,
1549 boolean isAttributeSplittingDisabled)
1550 {
1551 if (node != null)
1552 {
1553 updateAttribute(node, (Element) node.getReference(), name, listDelimiter,
1554 isAttributeSplittingDisabled);
1555 }
1556 }
1557
1558
1559
1560
1561
1562
1563
1564 private Element getElement(Node node)
1565 {
1566
1567 return (node.getName() != null && node.getReference() != null) ? (Element) node
1568 .getReference()
1569 : document.getDocumentElement();
1570 }
1571 }
1572
1573
1574
1575
1576
1577
1578 private class XMLFileConfigurationDelegate extends FileConfigurationDelegate
1579 {
1580 @Override
1581 public void load(InputStream in) throws ConfigurationException
1582 {
1583 XMLConfiguration.this.load(in);
1584 }
1585 }
1586 }