Return-Path:
Whitespace in the content of XML documents is trimmed per default. In most + * cases this is desired. However, sometimes whitespace is indeed important and + * should be treated as part of the value of a property as in the following + * example: + *
+ * <indent> </indent> + *+ * + *
Per default the spaces in the indent
element will be trimmed
+ * resulting in an empty element. To tell XMLConfiguration
that
+ * spaces are relevant the xml:space
attribute can be used, which is
+ * defined in the XML
+ * specification. This will look as follows:
+ *
+ * <indent xml:space="preserve"> </indent> + *+ * The value of the
indent
property will now contain the spaces.
+ *
* XMLConfiguration
implements the {@link FileConfiguration}
* interface and thus provides full support for loading XML documents from
* different sources like files, URLs, or streams. A full description of these
@@ -154,6 +172,12 @@
/** Constant for the default root element name. */
private static final String DEFAULT_ROOT_NAME = "configuration";
+ /** Constant for the name of the space attribute.*/
+ private static final String ATTR_SPACE = "xml:space";
+
+ /** Constant for the xml:space value for preserving whitespace.*/
+ private static final String VALUE_PRESERVE = "preserve";
+
/** Constant for the delimiter for multiple attribute values.*/
private static final char ATTR_VALUE_DELIMITER = '|';
@@ -422,7 +446,7 @@
setSystemID(document.getDoctype().getSystemId());
}
- constructHierarchy(getRoot(), document.getDocumentElement(), elemRefs);
+ constructHierarchy(getRoot(), document.getDocumentElement(), elemRefs, true);
getRootNode().setName(document.getDocumentElement().getNodeName());
if (elemRefs)
{
@@ -437,9 +461,12 @@
* @param node the actual node
* @param element the actual XML element
* @param elemRefs a flag whether references to the XML elements should be set
+ * @param trim a flag whether the text content of elements should be trimmed;
+ * this controls the whitespace handling
*/
- private void constructHierarchy(Node node, Element element, boolean elemRefs)
+ private void constructHierarchy(Node node, Element element, boolean elemRefs, boolean trim)
{
+ boolean trimFlag = shouldTrim(element, trim);
processAttributes(node, element, elemRefs);
StringBuffer buffer = new StringBuffer();
NodeList list = element.getChildNodes();
@@ -451,9 +478,9 @@
Element child = (Element) w3cNode;
Node childNode = new XMLNode(child.getTagName(),
elemRefs ? child : null);
- constructHierarchy(childNode, child, elemRefs);
+ constructHierarchy(childNode, child, elemRefs, trimFlag);
node.addChild(childNode);
- handleDelimiters(node, childNode);
+ handleDelimiters(node, childNode, trimFlag);
}
else if (w3cNode instanceof Text)
{
@@ -461,7 +488,12 @@
buffer.append(data.getData());
}
}
- String text = buffer.toString().trim();
+
+ String text = buffer.toString();
+ if (trimFlag)
+ {
+ text = text.trim();
+ }
if (text.length() > 0 || !node.hasChildren())
{
node.setValue(text);
@@ -506,8 +538,9 @@
*
* @param parent the parent element
* @param child the child element
+ * @param trim flag whether texts of elements should be trimmed
*/
- private void handleDelimiters(Node parent, Node child)
+ private void handleDelimiters(Node parent, Node child, boolean trim)
{
if (child.getValue() != null)
{
@@ -520,7 +553,7 @@
else
{
values = PropertyConverter.split(child.getValue().toString(),
- getListDelimiter());
+ getListDelimiter(), trim);
}
if (values.size() > 1)
@@ -558,6 +591,31 @@
}
/**
+ * Checks whether the content of the current XML element should be trimmed.
+ * This method checks whether a xml:space
attribute is
+ * present and evaluates its value. See
+ * http://www.w3.org/TR/REC-xml/#sec-white-space for more details.
+ *
+ * @param element the current XML element
+ * @param currentTrim the current trim flag
+ * @return a flag whether the content of this element should be trimmed
+ */
+ private boolean shouldTrim(Element element, boolean currentTrim)
+ {
+ Attr attr = element.getAttributeNode(ATTR_SPACE);
+
+ if (attr == null)
+ {
+ return currentTrim;
+ }
+ else
+ {
+ return !VALUE_PRESERVE.equals(attr.getValue());
+ }
+ }
+
+ /**
* Creates the DocumentBuilder
to be used for loading files.
* This implementation checks whether a specific
* DocumentBuilder
has been set. If this is the case, this
Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java?rev=662278&r1=662277&r2=662278&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestXMLConfiguration.java Sun Jun 1 13:06:47 2008
@@ -1387,6 +1387,36 @@
}
/**
+ * Tests whether spaces are preserved when the xml:space attribute is set.
+ */
+ public void testPreserveSpace()
+ {
+ assertEquals("Wrong value of blanc", " ", conf.getString("space.blanc"));
+ assertEquals("Wrong value of stars", " * * ", conf
+ .getString("space.stars"));
+ }
+
+ /**
+ * Tests whether the xml:space attribute can be overridden in nested
+ * elements.
+ */
+ public void testPreserveSpaceOverride()
+ {
+ assertEquals("Not trimmed", "Some text", conf
+ .getString("space.description"));
+ }
+
+ /**
+ * Tests an xml:space attribute with an invalid value. This will be
+ * interpreted as default.
+ */
+ public void testPreserveSpaceInvalid()
+ {
+ assertEquals("Invalid not trimmed", "Some other text", conf
+ .getString("space.testInvalid"));
+ }
+
+ /**
* Prepares a configuration object for testing a reload operation.
*
* @return the initialized configuration