This is an automated email from the ASF dual-hosted git repository.

Claudenw pushed a commit to branch cleanup-XmlWriter
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git

commit 37c431c8f4cafb85ba7459be31f3127389be07e8
Author: Claude Warren <[email protected]>
AuthorDate: Tue Jun 23 12:03:53 2026 +0100

    Cleanup XmlWriter resource issues
    Remove IXmlWriter
    Repleace simple RatReport implementations with anonymous implementations.
    Rework tests to ensure that XmlWriter is closed properly.
---
 .../src/main/java/org/apache/rat/Reporter.java     |   3 +-
 .../rat/configuration/XMLConfigurationWriter.java  |  17 +-
 .../org/apache/rat/report/ConfigurationReport.java |  53 --
 .../main/java/org/apache/rat/report/RatReport.java |   4 +-
 .../rat/report/claim/ClaimReporterMultiplexer.java |  90 ---
 .../rat/report/claim/ClaimValidatorReport.java     |  77 ---
 .../rat/report/claim/SimpleXmlClaimReporter.java   |  58 --
 .../org/apache/rat/report/xml/XmlElements.java     | 250 ++++-----
 .../apache/rat/report/xml/XmlReportFactory.java    | 132 ++++-
 .../apache/rat/report/xml/writer/IXmlWriter.java   | 159 ------
 .../apache/rat/report/xml/writer/XmlWriter.java    |  81 ++-
 .../apache/rat/analysis/AnalyserFactoryTest.java   |  20 +-
 .../apache/rat/report/ConfigurationReportTest.java |  27 +-
 .../rat/report/xml/XmlReportFactoryTest.java       |   3 +-
 .../rat/report/xml/writer/XmlWriterUtilsTest.java  |  25 +-
 .../report/xml/writer/impl/base/XmlWriterTest.java | 607 +++++++++------------
 .../java/org/apache/rat/testhelpers/XmlUtils.java  |   4 +-
 17 files changed, 620 insertions(+), 990 deletions(-)

diff --git a/apache-rat-core/src/main/java/org/apache/rat/Reporter.java 
b/apache-rat-core/src/main/java/org/apache/rat/Reporter.java
index d387dd44..6ca704ca 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/Reporter.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/Reporter.java
@@ -42,7 +42,6 @@ import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
 import org.apache.rat.report.RatReport;
 import org.apache.rat.report.claim.ClaimStatistic;
 import org.apache.rat.report.xml.XmlReportFactory;
-import org.apache.rat.report.xml.writer.IXmlWriter;
 import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.utils.StandardXmlFactory;
 import org.w3c.dom.Document;
@@ -88,7 +87,7 @@ public class Reporter {
                 if (configuration.hasSource()) {
                     ByteArrayOutputStream outputStream = new 
ByteArrayOutputStream();
                     Writer outputWriter = new OutputStreamWriter(outputStream, 
StandardCharsets.UTF_8);
-                    try (IXmlWriter writer = new XmlWriter(outputWriter)) {
+                    try (XmlWriter writer = new XmlWriter(outputWriter)) {
                         statistic = new ClaimStatistic();
                         RatReport report = 
XmlReportFactory.createStandardReport(writer, statistic, configuration);
                         report.startReport();
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
 
b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
index 79e7da99..05b92dcc 100644
--- 
a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
+++ 
b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
@@ -41,7 +41,6 @@ import 
org.apache.rat.configuration.builders.MatcherRefBuilder;
 import org.apache.rat.license.ILicense;
 import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
-import org.apache.rat.report.xml.writer.IXmlWriter;
 import org.apache.rat.report.xml.writer.XmlWriter;
 
 /**
@@ -92,7 +91,7 @@ public class XMLConfigurationWriter {
      * @param writer the IXmlWriter to write to.
      * @throws RatException on error.
      */
-    public void write(final IXmlWriter writer) throws RatException {
+    public void write(final XmlWriter writer) throws RatException {
         if (configuration.listFamilies() != LicenseFilter.NONE || 
configuration.listLicenses() != LicenseFilter.NONE) {
             try {
                 writer.openElement(XMLConfig.ROOT);
@@ -141,7 +140,7 @@ public class XMLConfigurationWriter {
         }
     }
 
-    private void writeFamily(final IXmlWriter writer, final ILicenseFamily 
family) throws RatException {
+    private void writeFamily(final XmlWriter writer, final ILicenseFamily 
family) throws RatException {
         try {
             writer.openElement(XMLConfig.FAMILY).attribute(XMLConfig.ATT_ID, 
family.getFamilyCategory().trim())
                     .attribute(XMLConfig.ATT_NAME, family.getFamilyName());
@@ -151,21 +150,21 @@ public class XMLConfigurationWriter {
         }
     }
 
-    private void writeDescriptions(final IXmlWriter writer, final 
Collection<Description> descriptions, final IHeaderMatcher component)
+    private void writeDescriptions(final XmlWriter writer, final 
Collection<Description> descriptions, final IHeaderMatcher component)
             throws RatException {
         for (Description description : descriptions) {
             writeDescription(writer, description, component);
         }
     }
 
-    private void writeChildren(final IXmlWriter writer, final Description 
description, final IHeaderMatcher component)
+    private void writeChildren(final XmlWriter writer, final Description 
description, final IHeaderMatcher component)
             throws RatException {
         writeAttributes(writer, 
description.filterChildren(attributeFilter(component.getDescription())), 
component);
         writeDescriptions(writer, 
description.filterChildren(attributeFilter(component.getDescription()).negate()),
                 component);
     }
 
-    private void writeAttributes(final IXmlWriter writer, final 
Collection<Description> descriptions, final IHeaderMatcher component)
+    private void writeAttributes(final XmlWriter writer, final 
Collection<Description> descriptions, final IHeaderMatcher component)
             throws RatException {
         for (Description d : descriptions) {
             try {
@@ -176,13 +175,13 @@ public class XMLConfigurationWriter {
         }
     }
 
-    private void writeComment(final IXmlWriter writer, final Description 
description) throws IOException {
+    private void writeComment(final XmlWriter writer, final Description 
description) throws IOException {
         if (StringUtils.isNotBlank(description.getDescription())) {
             writer.comment(description.getDescription().replace("-->", 
"-&ndash;>"));
         }
     }
 
-    private void writeAttribute(final IXmlWriter writer, final Description 
description, final IHeaderMatcher component)
+    private void writeAttribute(final XmlWriter writer, final Description 
description, final IHeaderMatcher component)
             throws IOException {
         String paramValue = description.getParamValue(component);
         if (paramValue != null) {
@@ -192,7 +191,7 @@ public class XMLConfigurationWriter {
 
     /* package private for testing */
     @SuppressWarnings("unchecked")
-    void writeDescription(final IXmlWriter writer, final Description desc, 
final IHeaderMatcher comp) throws RatException {
+    void writeDescription(final XmlWriter writer, final Description desc, 
final IHeaderMatcher comp) throws RatException {
         Description description = desc;
         IHeaderMatcher component = comp;
         try {
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/ConfigurationReport.java 
b/apache-rat-core/src/main/java/org/apache/rat/report/ConfigurationReport.java
deleted file mode 100644
index 66ed7629..00000000
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/ConfigurationReport.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.report;
-
-import org.apache.rat.ReportConfiguration;
-import org.apache.rat.api.RatException;
-import org.apache.rat.configuration.XMLConfigurationWriter;
-import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
-import org.apache.rat.report.xml.writer.IXmlWriter;
-
-/**
- * A report that dumps the ReportConfiguration into the XML output.
- */
-public class ConfigurationReport implements RatReport {
-    /** The report configuration to report on. */
-    private final ReportConfiguration configuration;
-    /** The XML writer to write the report with. */
-    private final IXmlWriter writer;
-
-    /**
-     * Constructor.
-     *
-     * @param writer The writer to write the XML data to.
-     * @param configuration the configuration to dump
-     */
-    public ConfigurationReport(final IXmlWriter writer, final 
ReportConfiguration configuration) {
-        this.configuration = configuration;
-        this.writer = writer;
-    }
-
-    @Override
-    public void startReport() throws RatException {
-        if (configuration.listFamilies() != LicenseFilter.NONE || 
configuration.listLicenses() != LicenseFilter.NONE) {
-            new XMLConfigurationWriter(configuration).write(writer);
-        }
-    }
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/report/RatReport.java 
b/apache-rat-core/src/main/java/org/apache/rat/report/RatReport.java
index 4a0c41cb..2a4032c2 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/report/RatReport.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/report/RatReport.java
@@ -25,6 +25,7 @@ import org.apache.rat.api.RatException;
  * Interface that defines a RatReport.
  * A RatReport examines a document and may report issues or modify the 
underlying file.
  */
+@FunctionalInterface
 public interface RatReport {
 
     /**
@@ -43,8 +44,7 @@ public interface RatReport {
      * @param document write any reporting results into this document upon 
analysis.
      * @throws RatException on error.
      */
-    default void report(Document document) throws RatException {
-    }
+    void report(Document document) throws RatException;
 
     /**
      * Signals the end of execution for the report. Will be called after the 
{@code report()} method
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimReporterMultiplexer.java
 
b/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimReporterMultiplexer.java
deleted file mode 100644
index 5f60284a..00000000
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimReporterMultiplexer.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.report.claim;
-
-
-import java.util.List;
-
-import org.apache.rat.api.Document;
-import org.apache.rat.api.RatException;
-import org.apache.rat.document.DocumentAnalyser;
-import org.apache.rat.document.RatDocumentAnalysisException;
-import org.apache.rat.report.RatReport;
-import org.apache.rat.report.xml.XmlElements;
-import org.apache.rat.report.xml.writer.IXmlWriter;
-
-/**
- * Executes a RatReport that multiplexes the running of multiple RatReports
- */
-public class ClaimReporterMultiplexer implements RatReport {
-    /** The document analyser to use */
-    private final DocumentAnalyser analyser;
-    /** A list of reports that are being updated */
-    private final List<? extends RatReport> reporters;
-    /** If {@code true} this is a dry run do not generate report */
-    private final boolean dryRun;
-    /** The XML Writer this multiplexer writes to */
-    private final IXmlWriter writer;
-
-    /**
-     * A multiplexer to run multiple claim reports.
-     * @param dryRun true if this is a dry run.
-     * @param analyser the analyser to use.
-     * @param reporters the reports to execute.
-     */
-    public ClaimReporterMultiplexer(final IXmlWriter writer, final boolean 
dryRun, final DocumentAnalyser analyser,
-                                    final List<? extends RatReport> reporters) 
{
-        this.analyser  = analyser;
-        this.reporters = reporters;
-        this.dryRun = dryRun;
-        this.writer = writer;
-    }
-
-    @Override
-    public void report(final Document document) throws RatException {
-        if (!dryRun) {
-            if (analyser != null) {
-                try {
-                    analyser.analyse(document);
-                } catch (RatDocumentAnalysisException e) {
-                    throw new RatException(e.getMessage(), e);
-                }
-            }
-            for (RatReport report : reporters) {
-                report.report(document);
-            }
-        }
-    }
-
-    @Override
-    public void startReport() throws RatException {
-        new XmlElements(writer).ratReport();
-        for (RatReport report : reporters) {
-            report.startReport();
-        }
-    }
-
-    @Override
-    public void endReport() throws RatException {
-        for (RatReport report : reporters) {
-            report.endReport();
-        }
-        new XmlElements(writer).closeElement();
-    }
-}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimValidatorReport.java
 
b/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimValidatorReport.java
deleted file mode 100644
index ad955a61..00000000
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimValidatorReport.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.rat.report.claim;
-
-import org.apache.rat.ReportConfiguration;
-import org.apache.rat.api.Document;
-import org.apache.rat.api.RatException;
-import org.apache.rat.config.results.ClaimValidator;
-import org.apache.rat.report.RatReport;
-import org.apache.rat.report.xml.XmlElements;
-import org.apache.rat.report.xml.writer.IXmlWriter;
-
-/**
- * A RatReport that publishes the results of the ClaimValidator.
- */
-public class ClaimValidatorReport implements RatReport {
-    /**
-     * The XmlElements tool to work with.
-     */
-    private final XmlElements elements;
-    /**
-     * The claim statistics to report.
-     */
-    private final ClaimStatistic statistic;
-    /**
-     * The validator for the statistics.
-     */
-    private final ClaimValidator validator;
-
-    /**
-     * Constructor.
-     * @param writer the XMLWriter to write with.
-     * @param statistic the Claim statistics to report.
-     * @param configuration the configuration for the run.
-     */
-    public ClaimValidatorReport(final IXmlWriter writer, final ClaimStatistic 
statistic, final ReportConfiguration configuration) {
-        this.elements = new XmlElements(writer);
-        this.statistic = statistic;
-        this.validator = configuration.getClaimValidator();
-    }
-
-    @Override
-    public void endReport() throws RatException {
-        elements.statistics();
-        for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) 
{
-            int count = statistic.getCounter(counter);
-            elements.statistic(counter.displayName(), count, 
counter.getDescription(), validator.isValid(counter, count));
-        }
-        for (String category : statistic.getLicenseFamilyCategories()) {
-            elements.licenseCategory(category, 
statistic.getLicenseCategoryCount(category));
-        }
-        for (String category : statistic.getLicenseNames()) {
-            elements.licenseName(category, 
statistic.getLicenseNameCount(category));
-        }
-        for (Document.Type type : statistic.getDocumentTypes()) {
-            elements.documentType(type.name(), statistic.getCounter(type));
-        }
-
-        elements.closeElement();
-    }
-}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/SimpleXmlClaimReporter.java
 
b/apache-rat-core/src/main/java/org/apache/rat/report/claim/SimpleXmlClaimReporter.java
deleted file mode 100644
index cf9228f6..00000000
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/claim/SimpleXmlClaimReporter.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.report.claim;
-
-import java.util.Iterator;
-
-import org.apache.rat.api.Document;
-import org.apache.rat.api.MetaData;
-import org.apache.rat.api.RatException;
-import org.apache.rat.license.ILicense;
-import org.apache.rat.report.RatReport;
-import org.apache.rat.report.xml.XmlElements;
-import org.apache.rat.report.xml.writer.IXmlWriter;
-
-/**
- * A claim reporter to write the XML document.
- */
-public class SimpleXmlClaimReporter implements RatReport {
-
-
-    /** the writer to write to */
-    private final XmlElements xmlElements;
-
-    /**
-     * Constructor.
-     * @param writer The writer to write the report to.
-     */
-    public SimpleXmlClaimReporter(final IXmlWriter writer) {
-        this.xmlElements = new XmlElements(writer);
-    }
-
-    @Override
-    public void report(final Document document) throws RatException {
-        final MetaData metaData = document.getMetaData();
-        xmlElements.document(document);
-        for (Iterator<ILicense> iter = metaData.licenses().iterator(); 
iter.hasNext();) {
-            final ILicense license = iter.next();
-            xmlElements.license(license, metaData.isApproved(license));
-        }
-        xmlElements.closeElement();
-    }
-}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlElements.java 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlElements.java
index 35d14b10..326f0289 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlElements.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlElements.java
@@ -28,18 +28,20 @@ import org.apache.rat.VersionInfo;
 import org.apache.rat.api.Document;
 import org.apache.rat.api.MetaData;
 import org.apache.rat.api.RatException;
+import org.apache.rat.config.results.ClaimValidator;
 import org.apache.rat.license.ILicense;
-import org.apache.rat.report.xml.writer.IXmlWriter;
+import org.apache.rat.report.claim.ClaimStatistic;
+import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.utils.CasedString;
 
 /**
  * Creates the elements in the XML report.
  */
-public class XmlElements {
+public final class XmlElements {
     /**
      * Converts an enum name to snake case.
-     * @param name the enum name to convert.
-     * @return a camel cased name.
+     * @param name the Atrribute to normalize
+     * @return a pascal cased name.
      */
     private static String normalizeName(final String name) {
         CasedString casedName = new CasedString(CasedString.StringCase.SNAKE, 
name.toLowerCase(Locale.ROOT));
@@ -90,7 +92,7 @@ public class XmlElements {
          * Gets the XML element name.
          * @return the XML element name.
          */
-        public String getElementName() {
+        public String elementName() {
             return elementName;
         }
     }
@@ -126,106 +128,120 @@ public class XmlElements {
         /** The encoding for a text document */
         ENCODING,
         /** Denotes a skipped directory */
-        IS_DIRECTORY
-    }
-
-    /** The XMLWriter that we write to */
-    private final IXmlWriter writer;
+        IS_DIRECTORY;
 
-    /**
-     * Constructor.
-     * @param xmlWriter The writer to use.
-     */
-    public XmlElements(final IXmlWriter xmlWriter) {
-        this.writer = xmlWriter;
+        String attributeName() {
+            return normalizeName(this.name());
+        }
     }
 
+    private XmlElements() {
+        // do not instantiate
+    }
     /**
      * Create the RAT report element. Includes the timestamp and the version 
element.
-     * @return this.
+     * Does not close the report.
      * @throws RatException on error
      */
-    public XmlElements ratReport() throws RatException {
-        return write(Elements.RAT_REPORT).
-                write(Attributes.TIMESTAMP, 
DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(Calendar.getInstance()))
-                .version();
+    public static void ratReport(final XmlWriter writer) throws RatException {
+        try {
+            writer.openElement(Elements.RAT_REPORT.elementName)
+                    .attribute(Attributes.TIMESTAMP.attributeName(),
+                            
DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(Calendar.getInstance()));
+            version(writer);
+        } catch (IOException e) {
+            throw new RatException(e);
+        }
     }
 
     /**
      * Creates the version element with all version attributes populated. 
Closes the version element.
-     * @return this.
      * @throws RatException on error
      */
-    public XmlElements version() throws RatException {
+    public static void version(final XmlWriter writer) throws RatException {
         VersionInfo versionInfo = new VersionInfo();
-        return write(Elements.VERSION)
-                .write(Attributes.PRODUCT, versionInfo.getTitle())
-                .write(Attributes.VENDOR, versionInfo.getVendor())
-                .write(Attributes.VERSION, versionInfo.getVersion())
-                .closeElement();
+        try {
+            writer.openElement(Elements.VERSION.elementName)
+                    .attribute(Attributes.PRODUCT.attributeName(), 
versionInfo.getTitle())
+                    .attribute(Attributes.VENDOR.attributeName(), 
versionInfo.getVendor())
+                    .attribute(Attributes.VERSION.attributeName(), 
versionInfo.getVersion())
+                    .closeElement();
+                    } catch (IOException e) {
+                throw new RatException(e);
+            }
     }
 
     /**
      * Creates a license element. Closes the element before exit.
      * @param license the license for the element.
      * @param approved {@code true} if the license is approved.
-     * @return this
      * @throws RatException on error.
      */
-    public XmlElements license(final ILicense license, final boolean approved) 
throws RatException {
-        write(Elements.LICENSE).write(Attributes.ID, license.getId())
-                .write(Attributes.NAME, license.getName())
-                .write(Attributes.APPROVAL, 
Boolean.valueOf(approved).toString())
-                .write(Attributes.FAMILY, 
license.getLicenseFamily().getFamilyCategory());
-        if (StringUtils.isNotBlank(license.getNote())) {
-            try {
-                write(Elements.NOTES).cdata(license.getNote()).closeElement();
-            } catch (IOException e) {
-                throw new RatException("Can not write CDATA for 'notes' 
element", e);
-            }
-        }
-        return closeElement();
-    }
+    public static void license(final XmlWriter writer, final ILicense license, 
final boolean approved) throws RatException {
+        try {
+            writer.openElement(Elements.LICENSE.elementName)
+                    .attribute(Attributes.ID.attributeName(), license.getId())
+                    .attribute(Attributes.NAME.attributeName(), 
license.getName())
+                    .attribute(Attributes.APPROVAL.attributeName(), 
Boolean.valueOf(approved).toString())
+                    .attribute(Attributes.FAMILY.attributeName(), 
license.getLicenseFamily().getFamilyCategory());
 
-    /**
-     * Writes a CDATA block
-     * @param data the data to write.
-     * @return this
-     * @throws IOException on error.
-     */
-    private XmlElements cdata(final String data) throws IOException {
-        writer.cdata(data);
-        return this;
+            if (StringUtils.isNotBlank(license.getNote())) {
+                
writer.openElement(Elements.NOTES.elementName).cdata(license.getNote()).closeElement();
+            }
+            writer.closeElement();
+                    } catch (IOException e) {
+                throw new RatException(e);
+            }
     }
 
     /**
      * Creates a document element with attributes. Does NOT close the document 
element.
      * @param document the document to write.
-     * @return this
      * @throws RatException on error.
      */
-    public XmlElements document(final Document document) throws RatException {
+    public static void document(final XmlWriter writer, final Document 
document) throws RatException {
         final MetaData metaData = document.getMetaData();
-        XmlElements result = write(Elements.RESOURCE)
-                .write(Attributes.NAME, document.getName().localized("/"))
-                .write(Attributes.TYPE, metaData.getDocumentType().toString())
-                .write(Attributes.MEDIA_TYPE, 
metaData.getMediaType().toString());
-        if (Document.Type.STANDARD == metaData.getDocumentType() || 
metaData.hasCharset()) {
-            result = result.write(Attributes.ENCODING, 
metaData.getCharset().displayName());
-        }
-        if (document.isIgnored()) {
-            result = result.write(Attributes.IS_DIRECTORY, 
Boolean.toString(document.isDirectory()));
-        }
-        return result;
+        try {
+            writer.openElement(Elements.RESOURCE.elementName)
+                    .attribute(Attributes.NAME.attributeName(), 
document.getName().localized("/"))
+                    .attribute(Attributes.TYPE.attributeName(), 
metaData.getDocumentType().toString())
+                    .attribute(Attributes.MEDIA_TYPE.attributeName(), 
metaData.getMediaType().toString());
+            if (Document.Type.STANDARD == metaData.getDocumentType() || 
metaData.hasCharset()) {
+                writer.attribute(Attributes.ENCODING.attributeName(), 
metaData.getCharset().displayName());
+            }
+            if (document.isIgnored()) {
+                writer.attribute(Attributes.IS_DIRECTORY.attributeName(), 
Boolean.toString(document.isDirectory()));
+            }
+                    } catch (IOException e) {
+                throw new RatException(e);
+            }
     }
 
     /**
      * Creates a statistics element.
-     * @return this
      * @throws RatException on error.
      */
-    public XmlElements statistics() throws RatException {
-        return write(Elements.STATISTICS);
+    public static void statistics(final XmlWriter writer, final ClaimStatistic 
statistic, final ClaimValidator validator) throws RatException {
+        try {
+            writer.openElement(Elements.STATISTICS.elementName);
+            for (ClaimStatistic.Counter counter : 
ClaimStatistic.Counter.values()) {
+                int count = statistic.getCounter(counter);
+                XmlElements.statistic(writer, counter.displayName(), count, 
counter.getDescription(), validator.isValid(counter, count));
+            }
+            for (String category : statistic.getLicenseFamilyCategories()) {
+                XmlElements.licenseCategory(writer, category, 
statistic.getLicenseCategoryCount(category));
+            }
+            for (String category : statistic.getLicenseNames()) {
+                XmlElements.licenseName(writer, category, 
statistic.getLicenseNameCount(category));
+            }
+            for (Document.Type type : statistic.getDocumentTypes()) {
+                XmlElements.documentType(writer, type.name(), 
statistic.getCounter(type));
+            }
+
+            writer.closeElement();
+        } catch (IOException e) {
+            throw new RatException(e);
+        }
     }
 
     /**
@@ -237,27 +253,17 @@ public class XmlElements {
      * @return this
      * @throws RatException on error.
      */
-    public XmlElements statistic(final String name, final int count, final 
String description, final boolean isOk) throws RatException {
-        return write(Elements.STATISTIC)
-                .write(Attributes.NAME, name)
-                .write(Attributes.COUNT, Integer.toString(count))
-                .write(Attributes.APPROVAL, Boolean.toString(isOk))
-                .write(Attributes.DESCRIPTION, description)
-                .closeElement();
-    }
-
-    /**
-     * Creates a statistic element. Closes the element before returning.
-     * @param name the name of the statistics element.
-     * @param count the count for the element.
-     * @return this
-     * @throws RatException on error.
-     */
-    public XmlElements licenseCategory(final String name, final int count) 
throws RatException {
-        return write(Elements.LICENSE_CATEGORY)
-                .write(Attributes.NAME, name)
-                .write(Attributes.COUNT, Integer.toString(count))
-                .closeElement();
+    public static void statistic(final XmlWriter writer, final String name, 
final int count, final String description, final boolean isOk) throws 
RatException {
+        try {
+            writer.openElement(Elements.STATISTIC.elementName)
+                    .attribute(Attributes.NAME.attributeName(), name)
+                    .attribute(Attributes.COUNT.attributeName(), 
Integer.toString(count))
+                    .attribute(Attributes.APPROVAL.attributeName(), 
Boolean.toString(isOk))
+                    .attribute(Attributes.DESCRIPTION.attributeName(), 
description)
+                    .closeElement();
+                    } catch (IOException e) {
+                throw new RatException(e);
+            }
     }
 
     /**
@@ -267,11 +273,15 @@ public class XmlElements {
      * @return this
      * @throws RatException on error.
      */
-    public XmlElements licenseName(final String name, final int count) throws 
RatException {
-        return write(Elements.LICENSE_NAME)
-                .write(Attributes.NAME, name)
-                .write(Attributes.COUNT, Integer.toString(count))
+    public static void licenseCategory(final XmlWriter writer, final String 
name, final int count) throws RatException {
+        try {
+        writer.openElement(Elements.LICENSE_CATEGORY.elementName)
+                .attribute(Attributes.NAME.attributeName(), name)
+                .attribute(Attributes.COUNT.attributeName(), 
Integer.toString(count))
                 .closeElement();
+                    } catch (IOException e) {
+                throw new RatException(e);
+            }
     }
 
     /**
@@ -281,56 +291,32 @@ public class XmlElements {
      * @return this
      * @throws RatException on error.
      */
-    public XmlElements documentType(final String name, final int count) throws 
RatException {
-        return write(Elements.DOCUMENT_TYPE)
-                .write(Attributes.NAME, name)
-                .write(Attributes.COUNT, Integer.toString(count))
-                .closeElement();
-    }
-
-    /**
-     * Closes the currently open element.
-     * @return this
-     * @throws RatException on error.
-     */
-    public XmlElements closeElement() throws RatException {
+    public static void licenseName(final XmlWriter writer, final String name, 
final int count) throws RatException {
         try {
-            writer.closeElement();
-            return this;
+            writer.openElement(Elements.LICENSE_NAME.elementName)
+                    .attribute(Attributes.NAME.attributeName(), name)
+                    .attribute(Attributes.COUNT.attributeName(), 
Integer.toString(count))
+                    .closeElement();
         } catch (IOException e) {
-            throw new RatException("Cannot close currently open element", e);
+            throw new RatException(e);
         }
-
     }
 
     /**
-     * Write an element. The element is not closed.
-     * @param element the element to write.
-     * @return this
-     * @throws RatException on error.
-     */
-    private XmlElements write(final Elements element) throws RatException {
-        try {
-            writer.openElement(element.getElementName());
-            return this;
-        } catch (IOException e) {
-            throw new RatException("Cannot open start element: " + 
element.elementName, e);
-        }
-    }
-
-    /**
-     * Write an attribute.
-     * @param attribute the attribute name.
-     * @param value the attribute value.
+     * Creates a statistic element. Closes the element before returning.
+     * @param name the name of the statistics element.
+     * @param count the count for the element.
      * @return this
      * @throws RatException on error.
      */
-    public XmlElements write(final Attributes attribute, final String value) 
throws RatException {
+    public static void documentType(final XmlWriter writer, final String name, 
final int count) throws RatException {
         try {
-            writer.attribute(normalizeName(attribute.name()), value);
-            return this;
+            writer.openElement(Elements.DOCUMENT_TYPE.elementName)
+                    .attribute(Attributes.NAME.attributeName(), name)
+                    .attribute(Attributes.COUNT.attributeName(), 
Integer.toString(count))
+                    .closeElement();
         } catch (IOException e) {
-            throw new RatException("Cannot open add attribute: " + attribute, 
e);
+            throw new RatException(e);
         }
     }
 }
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
index 2bdce3b0..a34b1ee6 100644
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
+++ 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/XmlReportFactory.java
@@ -18,21 +18,26 @@
  */
 package org.apache.rat.report.xml;
 
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.rat.ReportConfiguration;
 import org.apache.rat.analysis.AnalyserFactory;
+import org.apache.rat.api.Document;
+import org.apache.rat.api.MetaData;
+import org.apache.rat.api.RatException;
+import org.apache.rat.configuration.XMLConfigurationWriter;
+import org.apache.rat.document.DocumentAnalyser;
+import org.apache.rat.document.RatDocumentAnalysisException;
+import org.apache.rat.license.ILicense;
 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
-import org.apache.rat.report.ConfigurationReport;
 import org.apache.rat.report.RatReport;
 import org.apache.rat.report.claim.ClaimAggregator;
-import org.apache.rat.report.claim.ClaimReporterMultiplexer;
 import org.apache.rat.report.claim.ClaimStatistic;
-import org.apache.rat.report.claim.ClaimValidatorReport;
 import org.apache.rat.report.claim.LicenseAddingReport;
-import org.apache.rat.report.claim.SimpleXmlClaimReporter;
-import org.apache.rat.report.xml.writer.IXmlWriter;
+import org.apache.rat.report.xml.writer.XmlWriter;
 
 /**
  * A factory to create reports from a writer and a configuration.
@@ -53,7 +58,7 @@ public final class XmlReportFactory {
      * @param configuration The report configuration.
      * @return a RatReport instance.
      */
-    public static RatReport createStandardReport(final IXmlWriter writer, 
final ClaimStatistic statistic, final ReportConfiguration configuration) {
+    public static RatReport createStandardReport(final XmlWriter writer, final 
ClaimStatistic statistic, final ReportConfiguration configuration) {
         final List<RatReport> reporters = new ArrayList<>();
         if (statistic != null) {
             reporters.add(new ClaimAggregator(statistic));
@@ -63,13 +68,118 @@ public final class XmlReportFactory {
         }
 
         if (configuration.listFamilies() != LicenseFilter.NONE || 
configuration.listLicenses() != LicenseFilter.NONE) {
-
-            reporters.add(new ConfigurationReport(writer, configuration));
+            reporters.add(configuration(writer, configuration));
         }
 
-        reporters.add(new SimpleXmlClaimReporter(writer));
-        reporters.add(new ClaimValidatorReport(writer, statistic, 
configuration));
+        reporters.add(simple(writer));
+        reporters.add(validator(writer, statistic, configuration));
+
+        return multiplexer(writer, configuration.isDryRun(), 
AnalyserFactory.createConfiguredAnalyser(configuration), reporters);
+    }
+
+    /**
+     * Creates a simple claim reporter.
+     * @param writer The XmlWriter to use
+     * @return the RatReport that will write to the writer.
+     */
+    public static RatReport simple(final XmlWriter writer) {
+        return document -> {
+            final MetaData metaData = document.getMetaData();
+            XmlElements.document(writer, document);
+            for (Iterator<ILicense> iter = metaData.licenses().iterator(); 
iter.hasNext();) {
+                final ILicense license = iter.next();
+                XmlElements.license(writer, license, 
metaData.isApproved(license));
+            }
+            try {
+                writer.closeElement();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        };
+    }
+
+    /**
+     * Creates a simple claim validator reporter
+     * @param writer The XmlWriter to use
+     * @return the RatReport that will write to the writer.
+     */
+    public static RatReport validator(final XmlWriter writer, final 
ClaimStatistic statistic, final ReportConfiguration configuration) {
+        return new RatReport() {
+            @Override
+            public void report(final Document document) throws RatException {
+            }
+
+            @Override
+            public void endReport() throws RatException {
+                XmlElements.statistics(writer, statistic, 
configuration.getClaimValidator());
+            }
+        };
+    }
+
+    /**
+     * Creates a configuration report
+     *
+     * @param writer The writer to write the XML data to.
+     * @param configuration the configuration to write
+     */
+    public static RatReport configuration(final XmlWriter writer, final 
ReportConfiguration configuration) {
+        return new RatReport() {
+            @Override
+            public void startReport() throws RatException {
+                if (configuration.listFamilies() != LicenseFilter.NONE || 
configuration.listLicenses() != LicenseFilter.NONE) {
+                    new XMLConfigurationWriter(configuration).write(writer);
+                }
+            }
+
+            @Override
+            public void report(final Document document) throws RatException {
+            }
+        };
+    }
+
+
+    /**
+     * Creates a RatReport that multiplexes the running of multiple RatReports
+     */
+    public static RatReport multiplexer(final XmlWriter writer, final boolean 
dryRun, final DocumentAnalyser analyser,
+                                        final List<? extends RatReport> 
reporters) {
+        return new RatReport() {
+            @Override
+            public void report(final Document document) throws RatException {
+                if (!dryRun) {
+                    if (analyser != null) {
+                        try {
+                            analyser.analyse(document);
+                        } catch (RatDocumentAnalysisException e) {
+                            throw new RatException(e.getMessage(), e);
+                        }
+                    }
+                    for (RatReport report : reporters) {
+                        report.report(document);
+                    }
+                }
+            }
+
+            @Override
+            public void startReport() throws RatException {
+                XmlElements.ratReport(writer);
+                for (RatReport report : reporters) {
+                    report.startReport();
+                }
+            }
 
-        return new ClaimReporterMultiplexer(writer, configuration.isDryRun(), 
AnalyserFactory.createConfiguredAnalyser(configuration), reporters);
+            @Override
+            public void endReport() throws RatException {
+                for (RatReport report : reporters) {
+                    report.endReport();
+                }
+                try {
+                    writer.closeElement();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
     }
+
 }
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/IXmlWriter.java
 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/IXmlWriter.java
deleted file mode 100644
index a227b9c5..00000000
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/IXmlWriter.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- */
-package org.apache.rat.report.xml.writer;
-
-import java.io.IOException;
-
-import org.apache.rat.report.xml.XmlElements;
-import org.w3c.dom.Document;
-
-/**
- * Simple interface for creating basic XML documents.
- * Performs basic validation and escaping.
- * Not namespace aware.
- */
-public interface IXmlWriter extends AutoCloseable {
-
-    /**
-     * Starts a document by writing a prologue.
-     * Calling this method is optional.
-     * When writing a document fragment, it should <em>not</em> be called.
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called after the first element has been written
-     * or once a prolog has already been written
-     */
-    IXmlWriter startDocument() throws IOException;
-
-    /**
-     * Writes the start of an element.
-     *
-     * @param elementName the name of the element, not null
-     * @return this object
-     * @throws InvalidXmlException if the name is not valid for a xml element
-     * @throws OperationNotAllowedException
-     * if called after the first element has been closed
-     */
-    IXmlWriter openElement(CharSequence elementName) throws IOException;
-
-    /**
-     * Writes the start of an element.
-     *
-     * @param element the element, not null
-     * @return this object
-     * @throws InvalidXmlException if the name is not valid for a xml element
-     * @throws OperationNotAllowedException
-     * if called after the first element has been closed
-     */
-    default IXmlWriter openElement(XmlElements.Elements element) throws 
IOException {
-        return openElement(element.getElementName());
-    }
-
-    /**
-     * Writes a comment.
-     *
-     * @param text the comment text
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called after the first element has been closed
-     */
-    IXmlWriter comment(CharSequence text) throws IOException;
-
-    /**
-     * Writes an attribute of an element.
-     * Note that this is only allowed directly after {@link 
#openElement(CharSequence)}
-     * or this method.
-     *
-     * @param name the attribute name, not null
-     * @param value the attribute value, not null
-     * @return this object
-     * @throws InvalidXmlException if the name is not valid for a xml attribute
-     * or if a value for the attribute has already been written
-     * @throws OperationNotAllowedException if called after {@link 
#content(CharSequence)}
-     * or {@link #closeElement()} or before any call to {@link 
#openElement(CharSequence)}
-     */
-    IXmlWriter attribute(CharSequence name, CharSequence value) throws 
IOException;
-
-    /**
-     * Writes content.
-     * Note that this method does not support CDATA.
-     * This method automatically escapes characters.
-     *
-     * @param content the content to write
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called before any call to {@link #openElement(CharSequence)}
-     * or after the first element has been closed
-     */
-    IXmlWriter content(CharSequence content) throws IOException;
-
-    /**
-     * Writes CDATA content.
-     * This method DOES NOT automatically escape characters.
-     * It will remove enclosed CDATA closing strings (e.g. {@code ]]>})
-     *
-     * @param content the content to write
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called before any call to {@link #openElement(CharSequence)}
-     * or after the first element has been closed
-     */
-    IXmlWriter cdata(CharSequence content) throws IOException;
-
-    /**
-     * Closes the last element written.
-     *
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called before any call to {@link #openElement(CharSequence)}
-     * or after the first element has been closed
-     */
-    IXmlWriter closeElement() throws IOException;
-
-    /**
-     * Closes all open elements back to and including the named element.
-     *
-     * @param name  the last element to close
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called before any call to {@link #openElement(CharSequence)}
-     * or after the first element has been closed
-     */
-    IXmlWriter closeElement(CharSequence name) throws IOException;
-
-    /**
-     * Closes all pending elements.
-     * When appropriate, resources are also flushed and closed.
-     * No exception is raised when called upon a document whose
-     * root element has already been closed.
-     *
-     * @return this object
-     * @throws OperationNotAllowedException
-     * if called before any call to {@link #openElement(CharSequence)}
-     */
-    IXmlWriter closeDocument() throws IOException;
-
-    /**
-     * Append an XML document into this one.
-     * @param document the document to append
-     * @return this object
-     * @throws IOException on error.
-     */
-    IXmlWriter append(Document document) throws IOException;
-}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/XmlWriter.java 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/XmlWriter.java
index 9a49fbdd..5c6b6144 100644
--- 
a/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/XmlWriter.java
+++ 
b/apache-rat-core/src/main/java/org/apache/rat/report/xml/writer/XmlWriter.java
@@ -47,7 +47,7 @@ import org.w3c.dom.Document;
  * </p>
  */
 @SuppressWarnings({"checkstyle:MagicNumber", "checkstyle:JavadocVariable"})
-public final class XmlWriter implements IXmlWriter {
+public final class XmlWriter implements AutoCloseable {
     private final Appendable appendable;
     private final ArrayDeque<CharSequence> elementNames;
     private final Set<CharSequence> currentAttributes = new HashSet<>();
@@ -86,8 +86,7 @@ public final class XmlWriter implements IXmlWriter {
      * @throws OperationNotAllowedException if called after the first element 
has
      * been written or once a prolog has already been written
      */
-    @Override
-    public IXmlWriter startDocument() throws IOException {
+    public XmlWriter startDocument() throws IOException {
         if (elementsWritten) {
             throw new OperationNotAllowedException("Document already started");
         }
@@ -108,8 +107,7 @@ public final class XmlWriter implements IXmlWriter {
      * @throws OperationNotAllowedException if called after the first element 
has
      * been closed
      */
-    @Override
-    public IXmlWriter openElement(final CharSequence elementName) throws 
IOException {
+    public XmlWriter openElement(final CharSequence elementName) throws 
IOException {
         validateRootOpen();
         if (!XMLChar.isValidName(elementName.toString())) {
             throw new InvalidXmlException("'" + elementName + "' is not a 
valid element name");
@@ -124,8 +122,15 @@ public final class XmlWriter implements IXmlWriter {
         return this;
     }
 
-    @Override
-    public IXmlWriter comment(final CharSequence text) throws IOException {
+    /**
+     * Writes a comment.
+     *
+     * @param text the comment text
+     * @return this object
+     * @throws OperationNotAllowedException
+     * if called after the first element has been closed
+     */
+    public XmlWriter comment(final CharSequence text) throws IOException {
         maybeCloseElement();
         appendable.append("<!-- ");
         writeEscaped(text, false);
@@ -146,8 +151,7 @@ public final class XmlWriter implements IXmlWriter {
      * {@link #content(CharSequence)} or {@link #closeElement()} or before any 
call
      * to {@link #openElement(CharSequence)}
      */
-    @Override
-    public IXmlWriter attribute(final CharSequence name, final CharSequence 
value) throws IOException {
+    public XmlWriter attribute(final CharSequence name, final CharSequence 
value) throws IOException {
         if (elementNames.isEmpty()) {
             validateRootOpen();
             throw new OperationNotAllowedException("Close called before an 
element has been opened.");
@@ -182,15 +186,34 @@ public final class XmlWriter implements IXmlWriter {
         maybeCloseElement();
     }
 
-    @Override
-    public IXmlWriter content(final CharSequence content) throws IOException {
+    /**
+     * Writes content.
+     * Note that this method does not support CDATA.
+     * This method automatically escapes characters.
+     *
+     * @param content the content to write
+     * @return this object
+     * @throws OperationNotAllowedException
+     * if called before any call to {@link #openElement(CharSequence)}
+     * or after the first element has been closed
+     */public XmlWriter content(final CharSequence content) throws IOException 
{
         prepareForData();
         writeEscaped(content, false);
         return this;
     }
 
-    @Override
-    public IXmlWriter cdata(final CharSequence content) throws IOException {
+    /**
+     * Writes CDATA content.
+     * This method DOES NOT automatically escape characters.
+     * It will remove enclosed CDATA closing strings (e.g. {@code ]]>})
+     *
+     * @param content the content to write
+     * @return this object
+     * @throws OperationNotAllowedException
+     * if called before any call to {@link #openElement(CharSequence)}
+     * or after the first element has been closed
+     */
+    public XmlWriter cdata(final CharSequence content) throws IOException {
         prepareForData();
         StringBuilder sb = new StringBuilder(content);
         int found;
@@ -242,8 +265,7 @@ public final class XmlWriter implements IXmlWriter {
      * @throws OperationNotAllowedException if called before any call to
      * {@link #openElement} or after the first element has been closed
      */
-    @Override
-    public IXmlWriter closeElement() throws IOException {
+    public XmlWriter closeElement() throws IOException {
         if (elementNames.isEmpty()) {
             validateRootOpen();
             throw new OperationNotAllowedException("Close called before an 
element has been opened.");
@@ -267,8 +289,7 @@ public final class XmlWriter implements IXmlWriter {
      * @throws OperationNotAllowedException if called before any call to
      * {@link #openElement} or after the first element has been closed
      */
-    @Override
-    public IXmlWriter closeElement(final CharSequence name) throws IOException 
{
+    public XmlWriter closeElement(final CharSequence name) throws IOException {
         Objects.requireNonNull(name);
         if (elementNames.isEmpty()) {
             validateRootOpen();
@@ -298,8 +319,7 @@ public final class XmlWriter implements IXmlWriter {
      * @throws OperationNotAllowedException if called before any call to
      * {@link #openElement}
      */
-    @Override
-    public IXmlWriter closeDocument() throws IOException {
+    public XmlWriter closeDocument() throws IOException {
         if (elementNames.isEmpty() && !elementsWritten) {
             throw new OperationNotAllowedException("Close called before an 
element has been opened.");
         }
@@ -311,13 +331,28 @@ public final class XmlWriter implements IXmlWriter {
 
     @Override
     public void close() throws IOException {
-        closeDocument();
-        if (appendable instanceof Closeable closeable) {
-            closeable.close();
+        IOException thrown = null;
+        try {
+            closeDocument();
+        } catch (IOException e) {
+            thrown = e;
+        } finally {
+            if (appendable instanceof Closeable closeable) {
+                try {
+                    closeable.close();
+                } catch (IOException e) {
+                    if (thrown == null) {
+                        thrown = e;
+                    }
+                }
+            }
+        }
+        if (thrown != null) {
+            throw thrown;
         }
     }
 
-    public IXmlWriter append(final Document document) throws IOException {
+    public XmlWriter append(final Document document) throws IOException {
         validateRootOpen();
         elementsWritten = true;
         maybeCloseElement();
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
 
b/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
index 13319f35..22a4945f 100644
--- 
a/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
+++ 
b/apache-rat-core/src/test/java/org/apache/rat/analysis/AnalyserFactoryTest.java
@@ -36,7 +36,7 @@ import org.apache.rat.document.DocumentAnalyser;
 import org.apache.rat.document.DocumentName;
 import org.apache.rat.document.FileDocument;
 import org.apache.rat.document.RatDocumentAnalysisException;
-import org.apache.rat.report.claim.SimpleXmlClaimReporter;
+import org.apache.rat.report.xml.XmlReportFactory;
 import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.test.utils.Resources;
 import org.apache.rat.testhelpers.TestingDocument;
@@ -53,8 +53,6 @@ public class AnalyserFactoryTest {
 
     private final DocumentName basedir;
 
-    private StringWriter out;
-    private SimpleXmlClaimReporter reporter;
     private DocumentAnalyser analyser;
 
     AnalyserFactoryTest() {
@@ -63,8 +61,6 @@ public class AnalyserFactoryTest {
 
     @BeforeEach
     public void setUp() {
-        out = new StringWriter();
-        reporter = new SimpleXmlClaimReporter(new XmlWriter(out));
         ReportConfiguration config = new ReportConfiguration();
         config.addLicense(UnknownLicense.INSTANCE);
         analyser = AnalyserFactory.createConfiguredAnalyser(config);
@@ -175,13 +171,20 @@ public class AnalyserFactoryTest {
         
assertThat(document.getMetaData().getMediaType().toString()).isEqualTo("application/gzip");
     }
 
+    private String buildReport(final Document document) throws Exception {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            XmlReportFactory.simple(writer).report(document);
+        }
+        return out.toString();
+    }
+
     @Test
     public void RAT147_unix_Test() throws Exception {
         final Document document = new FileDocument(basedir,
                 
Resources.getResourceFile("/jira/RAT147/unix-newlines.txt.bin"), 
DocumentNameMatcher.MATCHES_ALL);
         analyser.analyse(document);
-        reporter.report(document);
-        String result = out.toString();
+        String result = buildReport(document);
         TextUtils.assertPatternInTarget(
                 "<resource name='/jira/RAT147/unix-newlines.txt.bin' 
type='STANDARD'",
                 result);
@@ -192,8 +195,7 @@ public class AnalyserFactoryTest {
         final Document document = new FileDocument(basedir,
                 
Resources.getResourceFile("/jira/RAT147/windows-newlines.txt.bin"), 
DocumentNameMatcher.MATCHES_ALL);
         analyser.analyse(document);
-        reporter.report(document);
-        String result = out.toString();
+        String result = buildReport(document);
         TextUtils.assertPatternInTarget(
                 "<resource name='/jira/RAT147/windows-newlines.txt.bin' 
type='STANDARD'",
                 result);
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/report/ConfigurationReportTest.java
 
b/apache-rat-core/src/test/java/org/apache/rat/report/ConfigurationReportTest.java
index af0d1333..cc964ffe 100644
--- 
a/apache-rat-core/src/test/java/org/apache/rat/report/ConfigurationReportTest.java
+++ 
b/apache-rat-core/src/test/java/org/apache/rat/report/ConfigurationReportTest.java
@@ -35,7 +35,7 @@ import org.apache.rat.ReportConfiguration;
 import org.apache.rat.configuration.MatcherBuilderTracker;
 import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
-import org.apache.rat.report.xml.writer.IXmlWriter;
+import org.apache.rat.report.xml.XmlReportFactory;
 import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.testhelpers.XmlUtils;
 import org.junit.jupiter.api.BeforeEach;
@@ -45,30 +45,31 @@ import org.w3c.dom.Node;
 
 public class ConfigurationReportTest {
 
-    private ConfigurationReport report;
-    private StringWriter sw;
-    private IXmlWriter writer;
-
+    private ReportConfiguration reportConfiguration;
     private final String[] FAMILY_IDS = { "AL", "BSD-3", "CDDL1", "GPL", 
"MIT", "OASIS", "W3C", "W3CD", };
 
     @BeforeEach
     public void setup() {
-        ReportConfiguration reportConfiguration = new ReportConfiguration();
+        reportConfiguration = new ReportConfiguration();
         reportConfiguration.listFamilies(LicenseFilter.ALL);
         reportConfiguration.listLicenses(LicenseFilter.ALL);
         reportConfiguration.setFrom(Defaults.builder().build());
+    }
 
-        sw = new StringWriter();
-        writer = new XmlWriter(sw);
-        report = new ConfigurationReport(writer, reportConfiguration);
+    private String executeReport() throws Exception {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.startDocument();
+            RatReport report = XmlReportFactory.configuration(writer, 
reportConfiguration);
+            report.startReport();
+            report.endReport();
+        }
+        return out.toString();
     }
 
     @Test
     public void testAllFamiliesAndMatchers() throws Exception {
-        report.startReport();
-        report.endReport();
-        writer.closeDocument();
-        String result = sw.toString();
+        String result = executeReport();
         assertTrue(XmlUtils.isWellFormedXml(result), "Is well formed");
 
         XPath xPath = XPathFactory.newInstance().newXPath();
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
index 3de15b01..fb041648 100644
--- 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
+++ 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/XmlReportFactoryTest.java
@@ -38,7 +38,6 @@ import org.apache.rat.license.ILicense;
 import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.report.RatReport;
 import org.apache.rat.report.claim.ClaimStatistic;
-import org.apache.rat.report.xml.writer.IXmlWriter;
 import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.test.utils.Resources;
 import org.apache.rat.testhelpers.TestingLicense;
@@ -54,7 +53,7 @@ public class XmlReportFactoryTest {
             .setLicenseFamilyName("Testing family").build();
 
     private StringWriter out;
-    private IXmlWriter writer;
+    private XmlWriter writer;
 
     @BeforeEach
     public void setUp() throws Exception {
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/XmlWriterUtilsTest.java
 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/XmlWriterUtilsTest.java
index f0c8c921..2a87de6d 100644
--- 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/XmlWriterUtilsTest.java
+++ 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/XmlWriterUtilsTest.java
@@ -28,25 +28,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class XmlWriterUtilsTest {
 
-    private StringWriter out;
-    private IXmlWriter writer;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        out = new StringWriter();
-        writer = new XmlWriter(out);
-        writer.openElement("alpha");
-    }
 
     @Test
     public void writeTrue() throws Exception {
-        XmlUtils.writeAttribute(writer, "name", true);
-        assertEquals("<alpha name='true'", out.toString(), "Attribute written 
as True");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            XmlUtils.writeAttribute(writer, "name", true);
+            assertEquals("<alpha name='true'", out.toString(), "Attribute 
written as True");
+        }
     }
 
     @Test
     public void writeFalse() throws Exception {
-        XmlUtils.writeAttribute(writer, "name", false);
-        assertEquals("<alpha name='false'", out.toString(), "Attribute written 
as False");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            XmlUtils.writeAttribute(writer, "name", false);
+            assertEquals("<alpha name='false'", out.toString(), "Attribute 
written as False");
+        }
     }
 }
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/impl/base/XmlWriterTest.java
 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/impl/base/XmlWriterTest.java
index 4f5ea5a0..f15ab8e7 100644
--- 
a/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/impl/base/XmlWriterTest.java
+++ 
b/apache-rat-core/src/test/java/org/apache/rat/report/xml/writer/impl/base/XmlWriterTest.java
@@ -18,12 +18,10 @@
  */ 
 package org.apache.rat.report.xml.writer.impl.base;
 
-import org.apache.rat.report.xml.writer.IXmlWriter;
 import org.apache.rat.report.xml.writer.InvalidXmlException;
 import org.apache.rat.report.xml.writer.OperationNotAllowedException;
 import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.testhelpers.XmlUtils;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.w3c.dom.Document;
 
@@ -33,72 +31,59 @@ import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.NoSuchElementException;
 
-import static org.assertj.core.api.Assertions.fail;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class XmlWriterTest {
 
-    private XmlWriter writer;
-    private StringWriter out;
-    
-    @BeforeEach
-    public void setUp() {
-        out = new StringWriter();
-        writer = new XmlWriter(out);
-    }
-
     @Test
     void returnValues() throws Exception {
-        assertEquals( 
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-        assertEquals(
-                writer, writer.attribute("beta", "b"), "XmlWriters should 
always return themselves");
-        assertEquals(
-                writer, writer.content("gamma"), "XmlWriters should always 
return themselves");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+        assertThat(writer.openElement("alpha")).as("XmlWriters should always 
return themselves").isEqualTo(writer);
+            assertThat(writer.attribute("beta", "b")).as("XmlWriters should 
always return themselves").isEqualTo(writer);
+            assertThat(writer.content("gamma")).as("XmlWriters should always 
return themselves").isEqualTo(writer);
+            assertThat(writer.closeElement()).as("XmlWriters should always 
return themselves").isEqualTo(writer);
+        }
     }
 
     @Test
     void openElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta", out.toString(), "Alpha element tag closed 
and beta started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/>", out.toString(), "Beta tag ended");
-        assertEquals(
-                writer, writer.openElement("gamma"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/><gamma", out.toString(), "Gamma tag 
started");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.openElement("beta");
+            assertThat(out.toString()).isEqualTo("<alpha><beta");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha><beta/>");
+            writer.openElement("gamma");
+            assertThat(out.toString()).isEqualTo("<alpha><beta/><gamma");
+        }
     }
     
     @Test
     void invalidElementName() throws Exception {
-        assertTrue( isValidElementName("alpha"), "All strings ok");
-        assertTrue(isValidElementName("alpha77"), "Strings and digits ok");
-        assertFalse(isValidElementName("5alpha77"), "Must no start with 
digit");
-        assertFalse(isValidElementName("alph<a77"), "Greater than not ok");
-        assertFalse(isValidElementName("alph<a77"), "Less than not ok");
-        assertFalse(isValidElementName("alph'a77"), "Quote not ok");
-        assertTrue(isValidElementName("alph-a77"), "Dash ok");
-        assertTrue(isValidElementName("alph_a77"), "Underscore ok");
-        assertTrue(isValidElementName("alph.a77"), "Dot ok");
-        assertTrue(isValidElementName("alpha:77"), "Colon ok");
-        assertFalse(isValidElementName("-a77"), "Start with dash not ok");
-        assertTrue(isValidElementName("_a77"), "Start with underscore ok");
-        assertFalse(isValidElementName(".a77"), "Start with dot not ok");
-        assertTrue(isValidElementName(":a77"), "Start with colon ok");
+        assertThat(isValidElementName("alpha")).as("All strings ok").isTrue();
+        assertThat(isValidElementName("alpha77")).as("Strings and digits 
ok").isTrue();
+        assertThat(isValidElementName("5alpha77")).as("Must no start with 
digit").isFalse();
+        assertThat(isValidElementName("alph<a77")).as("Greater than not 
ok").isFalse();
+        assertThat(isValidElementName("alph<a77")).as("Less than not 
ok").isFalse();
+        assertThat(isValidElementName("alph'a77")).as("Quote not 
ok").isFalse();
+        assertThat(isValidElementName("alph-a77")).as("Dash ok").isTrue();
+        assertThat(isValidElementName("alph_a77")).as("Underscore 
ok").isTrue();
+        assertThat(isValidElementName("alph.a77")).as("Dot ok").isTrue();
+        assertThat(isValidElementName("alpha:77")).as("Colon ok").isTrue();
+        assertThat(isValidElementName("-a77")).as("Start with dash not 
ok").isFalse();
+        assertThat(isValidElementName("_a77")).as("Start with underscore 
ok").isTrue();
+        assertThat(isValidElementName(".a77")).as("Start with dot not 
ok").isFalse();
+        assertThat(isValidElementName(":a77")).as("Start with colon 
ok").isTrue();
     }
     
     private boolean isValidElementName(String elementName) throws Exception {
         boolean result = true;
-        try {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
             writer.openElement(elementName);
         } catch (InvalidXmlException e) {
             result = false;
@@ -108,190 +93,176 @@ class XmlWriterTest {
 
     @Test
     void callOpenElementAfterLastElementClosed() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
-        try {
-            writer.openElement("delta");
-            fail("Cannot open new elements once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha/>");
+            assertThatThrownBy(() -> writer.openElement("delta"))
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                            .isInstanceOf(OperationNotAllowedException.class);
         }
     }    
 
     @Test
     void callCloseElementAfterLastElementClosed() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
-        try {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
             writer.closeElement();
-            fail("Cannot close elements once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+            assertThat(out.toString()).isEqualTo("<alpha/>");
+            assertThatThrownBy(writer::closeElement)
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                    .isInstanceOf(OperationNotAllowedException.class);
         }
     }
 
     @Test
     void closeFirstElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha/>");
+        }
     }
     
     @Test
     void closeElementWithContent() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta", out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/>", out.toString(), "Element beta is 
closed");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/></alpha>", out.toString(), "Element beta 
is closed");
-        try {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").openElement("beta");
+            assertThat(out.toString()).isEqualTo("<alpha><beta");
+
+                   writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha><beta/>");
             writer.closeElement();
-            fail("Cannot close elements once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+            assertThat(out.toString()).isEqualTo("<alpha><beta/></alpha>");
+            assertThatThrownBy(writer::closeElement)
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                    .isInstanceOf(OperationNotAllowedException.class);
         }
     }
     
     @Test
     void closeElementBeforeFirstElement() throws Exception {
-        try {
-            writer.closeElement();
-            fail("Cannot close elements before the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements before the first element has been 
closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            assertThatThrownBy(writer::closeElement)
+                    .hasMessageContaining("Close called before an element has 
been opened.")
+                    .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
         }
+
     }
     
     @Test
     void contentAfterElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.content("foo bar"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>foo bar", out.toString(), "Alpha tag closed. 
Content written");
-        assertEquals(
-                writer, writer.content(" and more foo bar"), "XmlWriters 
should always return themselves");
-        assertEquals("<alpha>foo bar and more foo bar", out.toString(), "Alpha 
tag closed. Content written");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>foo bar and more foo bar<beta", out.toString());
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>foo bar and more foo bar<beta/>", out.toString(), 
"Element beta is closed");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>foo bar and more foo bar<beta/></alpha>", 
out.toString(), "Element beta is closed");
-        try {
-            writer.content("A Sentence Too far");
-            fail("Cannot write content once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.content("foo bar");
+            assertThat(out.toString()).isEqualTo("<alpha>foo bar");
+            writer.content(" and more foo bar");
+            assertThat(out.toString()).isEqualTo("<alpha>foo bar and more foo 
bar");
+            writer.openElement("beta");
+            assertThat(out.toString()).isEqualTo("<alpha>foo bar and more foo 
bar<beta");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha>foo bar and more foo 
bar<beta/>");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha>foo bar and more foo 
bar<beta/></alpha>");
+            assertThatThrownBy(() -> writer.content("A Sentence Too far"))
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                            .isInstanceOf(OperationNotAllowedException.class);
         }
     }
 
     @Test
     void contentAfterLastElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
-        try {
-            writer.content("A Sentence Too far");
-            fail("Cannot write content once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha/>");
+            assertThatThrownBy(() -> writer.content("A Sentence Too far"))
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                            .isInstanceOf(OperationNotAllowedException.class);
         }
     }
     
     @Test
     void writeContentBeforeFirstElement() throws Exception {
-        try {
-            writer.content("Too early");
-            fail("Cannot close elements before the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements before the first element has been 
closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            assertThatThrownBy(() -> writer.content("Too early"))
+                    .hasMessageContaining("An element must be opened before 
content can be written.")
+                            .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
         }
     }
     
     @Test
     void contentEscaping() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.content("this&that"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>this&amp;that", out.toString(), "Amphersands must 
be escaped");
-        assertEquals(
-                writer, writer.content("small<large"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha>this&amp;thatsmall&lt;large", out.toString(), 
"Left angle brackets must be escaped");
-        assertEquals(
-                writer, writer.content("12>1"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>this&amp;thatsmall&lt;large12&gt;1", 
out.toString(), "Choose to escape right angle brackets");
-
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            assertThat(out.toString()).isEqualTo("<alpha");
+            writer.content("this&that");
+            assertThat(out.toString()).isEqualTo("<alpha>this&amp;that");
+            writer.content("small<large");
+            
assertThat(out.toString()).isEqualTo("<alpha>this&amp;thatsmall&lt;large");
+            writer.content("12>1");
+            
assertThat(out.toString()).isEqualTo("<alpha>this&amp;thatsmall&lt;large12&gt;1");
+        }
     }
 
     @Test
     void attributeAfterLastElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString(), "Element alpha is closed");
-        try {
-            writer.attribute("foo", "bar");
-            fail("Cannot write content once the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements once the first element has been closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha/>");
+            assertThatThrownBy(() -> writer.attribute("foo", "bar"))
+                    .hasMessageContaining("Root element already closed. Cannot 
open new element.")
+                            .isInstanceOf(OperationNotAllowedException.class);
         }
     }
     
     @Test
     void attributeContentBeforeFirstElement() throws Exception {
-        try {
-            writer.attribute("foo", "bar");
-            fail("Cannot close elements before the first element has been 
closed");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements before the first element has been 
closed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            assertThatThrownBy(() -> writer.attribute("foo", "bar"))
+                    .hasMessageContaining("Close called before an element has 
been opened.")
+                    .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
         }
     }
     
     @Test
     void invalidAttributeName() throws Exception {
-        writer.openElement("alpha");
-        assertTrue(isValidAttributeName("alpha"), "All string ok");
-        assertTrue(isValidAttributeName("alpha77"), "Strings and digits ok");
-        assertFalse(isValidAttributeName("5alpha77"), "Must not start with 
digit");
-        assertTrue(isValidAttributeName("alpha:77"), "Colon ok");
-        assertFalse(isValidAttributeName("alph<a77"),"Greater than not ok");
-        assertFalse(isValidAttributeName("alph<a77"), "Less than not ok");
-        assertFalse(isValidAttributeName("alph'a77"), "Quote not ok");
+        assertThat(isValidAttributeName("alpha")).as("All string ok").isTrue();
+        assertThat(isValidAttributeName("alpha77")).as("Strings and digits 
ok").isTrue();
+        assertThat(isValidAttributeName("5alpha77")).as("Must not start with 
digit").isFalse();
+        assertThat(isValidAttributeName("alpha:77")).as("Colon ok").isTrue();
+        assertThat(isValidAttributeName("alph<a77")).as("Greater than not 
ok").isFalse();
+        assertThat(isValidAttributeName("alph<a77")).as("Less than not 
ok").isFalse();
+        assertThat(isValidAttributeName("alph'a77")).as("Quote not 
ok").isFalse();
     }
     
     private boolean isValidAttributeName(String name) throws Exception {
         boolean result = true;
-        try {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
             writer.attribute(name, "");
         } catch (InvalidXmlException e) {
             result = false;
@@ -301,235 +272,198 @@ class XmlWriterTest {
     
     @Test
     void escapeAttributeContent() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.attribute("one", "this&that"), "XmlWriters 
should always return themselves");
-        assertEquals("<alpha one='this&amp;that'", out.toString(), 
"Amphersands must be escaped");
-        assertEquals(
-                writer, writer.attribute("two", "small<large"), "XmlWriters 
should always return themselves");
-        assertEquals("<alpha one='this&amp;that' two='small&lt;large'", 
out.toString(), "Left angle brackets must be escaped");
-        assertEquals(
-                writer, writer.attribute("three", "12>1"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha one='this&amp;that' two='small&lt;large' 
three='12&gt;1'", out.toString(), "Choose to escape right angle brackets");
-        assertEquals(
-                writer, writer.attribute("four", "'quote'"), "XmlWriters 
should always return themselves");
-        assertEquals("<alpha one='this&amp;that' two='small&lt;large' 
three='12&gt;1' four='&apos;quote&apos;'", out.toString(), "Apostrophes must be 
escape");
-        assertEquals(
-                writer, writer.attribute("five", "\"quote\""), "XmlWriters 
should always return themselves");
-        assertEquals("<alpha one='this&amp;that' two='small&lt;large' 
three='12&gt;1' four='&apos;quote&apos;' five='&quot;quote&quot;'", 
out.toString(), "Double quotes must be escape");
-
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").attribute("one", 
"this&that").attribute("two", "small<large")
+                    .attribute("three", "12>1").attribute("four", 
"'quote'").attribute("five", "\"quote\"");
+            assertThat(out.toString()).isEqualTo("<alpha one='this&amp;that' 
two='small&lt;large' three='12&gt;1' four='&apos;quote&apos;' 
five='&quot;quote&quot;'");
+        }
     }
     
     @Test
     void attributeInContent() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.content("foo bar"), "XmlWriters should always 
return themselves");
-        try {
-            writer.attribute("name", "value");
-            fail("attributes after body content are not allowed");
-        } catch (InvalidXmlException e) {
-            // attributes after body content are not allowed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").content("foo bar");
+            assertThatThrownBy(() -> writer.attribute("name", "value"))
+                    .hasMessageContaining("Attributes can only be written in 
elements")
+                            .isInstanceOf(InvalidXmlException.class);
         }
     }
   
     @Test
     void outOfRangeCharacter() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-         CharSequence cs = new CharSequence() {
-             @Override
-             public int length() {
-                 return 1;
-             }
-
-             @Override
-             public char charAt(int index) {
-                 return Character.highSurrogate(0x110000);
-             }
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            CharSequence cs = new CharSequence() {
+                @Override
+                public int length() {
+                    return 1;
+                }
 
-             @Override
-             public CharSequence subSequence(int start, int end) {
-                 return null;
-             }
-         };
+                @Override
+                public char charAt(int index) {
+                    return Character.highSurrogate(0x110000);
+                }
 
-        assertEquals(writer, writer.content(cs), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha>\\uDC00", this.out.toString(), "Replace illegal 
characters with \\u encoding");
+                @Override
+                public CharSequence subSequence(int start, int end) {
+                    return null;
+                }
+            };
+            writer.openElement("alpha").content(cs);
+            assertThat(out.toString()).isEqualTo("<alpha>\\uDC00");
+        }
     }
     
     @Test
     void attributeAfterElementClosed() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta", out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/>", out.toString(), "Beta element closed");
-        try {
-            writer.attribute("name", "value");
-            fail("attributes after closed element are not allowed");
-        } catch (InvalidXmlException e) {
-            // attributes after body content are not allowed
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").openElement("beta").closeElement();
+            assertThat(out.toString()).isEqualTo("<alpha><beta/>");
+            assertThatThrownBy(() -> writer.attribute("name", "value"))
+                    .hasMessageContaining("Attributes can only be written in 
elements")
+                            .isInstanceOf(InvalidXmlException.class);
         }
     }
     
     @Test
     void closeDocumentBeforeOpen() throws Exception {
-        try {
-            writer.closeDocument();
-            fail("Cannot close document before the first element has been 
opened");
-        } catch (OperationNotAllowedException e) {
-            // Cannot open new elements before the first element has been 
opened
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            assertThatThrownBy(writer::closeDocument)
+                    .hasMessageContaining("Close called before an element has 
been opened.")
+                    .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
         }
+
     }
     
     @Test
     void closeDocumentAfterRootElementClosed() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha/>", out.toString());
-        try {
-            writer.closeDocument();
-        } catch (OperationNotAllowedException e) {
-            fail("No exception should be thrown when called after the root 
element is closed.");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").closeElement().closeDocument();
         }
     }   
     
     @Test
     void closeSimpleDocument() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta", out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.closeDocument(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta/></alpha>", out.toString(), "Beta element 
started");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").openElement("beta").closeDocument();
+            assertThat(out.toString()).isEqualTo("<alpha><beta/></alpha>");
+        }
     }
     
     @Test
     void closeComplexDocument() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta", out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.attribute("name", "value"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha><beta name='value'", out.toString(), "Beta 
element started");
-        assertEquals(
-                writer, writer.closeElement(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta name='value'/>", out.toString(), "Beta 
element started");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta name='value'/><beta", out.toString(), "Beta 
element started");
-        assertEquals(
-                writer, writer.attribute("name", "value"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha><beta name='value'/><beta name='value'", 
out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.openElement("gamma"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta name='value'/><beta name='value'><gamma", 
out.toString(), "Beta element started");
-        assertEquals(
-                writer, writer.closeDocument(), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha><beta name='value'/><beta 
name='value'><gamma/></beta></alpha>", out.toString(), "Beta element started");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").openElement("beta").attribute("name", 
"value").closeElement()
+                    .openElement("beta")
+            .attribute("name", "value")
+                    .openElement("gamma").closeDocument();
+            assertThat(out.toString()).isEqualTo("<alpha><beta 
name='value'/><beta name='value'><gamma/></beta></alpha>");
+        }
     }
     
     @Test
     void writeProlog() throws Exception {
-        assertEquals(
-                writer, writer.startDocument(), "XmlWriters should always 
return themselves");
-        assertEquals("<?xml version='1.0'?>", out.toString(), "Prolog 
written");
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.startDocument();
+            assertThat(out.toString()).isEqualTo("<?xml version='1.0'?>");
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
+        }
     }
     
     @Test
     void writeAfterElement() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        try {
-            writer.startDocument();
-            fail("Operation not allowed once an element has been written");
-        } catch (OperationNotAllowedException e) {
-            // Operation not allowed once an element has been written
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha");
+            
assertThatThrownBy(writer::startDocument).hasMessageContaining("Document 
already started")
+                    .isInstanceOf(OperationNotAllowedException.class);
         }
     }
     
     @Test
-    void writePrologTwo() throws Exception {
-        assertEquals(
-                writer, writer.startDocument(), "XmlWriters should always 
return themselves");
-        assertEquals("<?xml version='1.0'?>", out.toString(), "Prolog 
written");
-        try {
+    void writePrologTwice() throws Exception {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
             writer.startDocument();
-            fail("Operation not allow once a prolog has been written");
-        } catch (OperationNotAllowedException e) {
-            // Operation not allowed once a prolog has been written
+            assertThat(out.toString()).isEqualTo("<?xml version='1.0'?>");
+            
assertThatThrownBy(writer::startDocument).hasMessageContaining("Only one prolog 
allowed")
+                    .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
         }
     }
     
     @Test
     void duplicateAttributes() throws Exception {
-        assertEquals(
-                writer, writer.openElement("alpha"), "XmlWriters should always 
return themselves");
-         assertEquals("<alpha", out.toString(), "Alpha element started");
-        assertEquals(
-                writer, writer.attribute("one", "1"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha one='1'", out.toString(), "Attribute written");
-        assertEquals(
-                writer, writer.openElement("beta"), "XmlWriters should always 
return themselves");
-        assertEquals("<alpha one='1'><beta", out.toString(), "Beta element 
started");
-        assertEquals(
-                writer, writer.attribute("one", "1"), "XmlWriters should 
always return themselves");
-        assertEquals("<alpha one='1'><beta one='1'", out.toString(), "Beta 
element started");
-        try {
-            writer.attribute("one", "2");
-            fail("Each attribute may only be written once");
-        } catch (InvalidXmlException e) {
-            // Each attribute may only be written once
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.openElement("alpha").attribute("one", 
"1").openElement("beta").attribute("one", "1");
+            assertThat(out.toString()).isEqualTo("<alpha one='1'><beta 
one='1'");
+            assertThatThrownBy(() -> writer.attribute("one", "2"))
+                    .hasMessageContaining("Each attribute can only be written 
once")
+                    .isInstanceOf(InvalidXmlException.class);
         }
     }
 
     @Test
-    void writeCDataBeforeElement() {
-        assertThrows(OperationNotAllowedException.class, () -> 
writer.startDocument().cdata("Just cdata").closeDocument());
+    void writeCDataBeforeElement() throws IOException {
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.startDocument();
+            assertThatThrownBy(() -> writer.cdata("Just cdata"))
+                    .hasMessageContaining("An element must be opened before 
content can be written.")
+                    .isInstanceOf(OperationNotAllowedException.class);
+        } catch (OperationNotAllowedException expected) {
+            assertThat(expected).hasMessageContaining("Close called before an 
element has been opened.");
+        }
     }
 
     @Test
     void writeCData() throws Exception {
-        writer.startDocument().openElement("test").cdata("Just 
cdata").closeDocument();
-        assertEquals("<?xml version='1.0'?><test><![CDATA[ Just cdata 
]]></test>", out.toString());
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.startDocument().openElement("test").cdata("Just 
cdata").closeDocument();
+            assertThat(out.toString()).isEqualTo("<?xml 
version='1.0'?><test><![CDATA[ Just cdata ]]></test>");
+        }
     }
 
     @Test
     void writeCDataEmbeddedCData() throws Exception {
-        writer.startDocument().openElement("test").cdata("Some <![CDATA[ cdata 
]]> text").closeDocument();
-        assertEquals("<?xml version='1.0'?><test><![CDATA[ Some \\u3C![CDATA[ 
cdata {rat:CDATA close} text ]]></test>", out.toString());
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            writer.startDocument().openElement("test").cdata("Some <![CDATA[ 
cdata ]]> text").closeDocument();
+            assertThat(out.toString()).isEqualTo("<?xml 
version='1.0'?><test><![CDATA[ Some \\u3C![CDATA[ cdata {rat:CDATA close} text 
]]></test>");
+        }
     }
 
     @Test
     void closeElementBeforeOpened() throws IOException {
-        IXmlWriter underTest = writer.startDocument().openElement("test");
-        assertThrows(NoSuchElementException.class, () -> 
underTest.closeElement("missing"));
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            XmlWriter underTest = writer.startDocument().openElement("test");
+            assertThatThrownBy(() -> underTest.closeElement("missing"))
+                            .isInstanceOf(NoSuchElementException.class);
+        }
     }
 
     @Test
     void closeElement() throws Exception {
-        
writer.startDocument().openElement("root").openElement("hello").openElement("world").content("hello
 world").closeElement("hello").openElement("test").closeDocument();
-        assertEquals("<?xml version='1.0'?><root><hello><world>hello 
world</world></hello><test/></root>", out.toString());
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            
writer.startDocument().openElement("root").openElement("hello").openElement("world").content("hello
 world").closeElement("hello").openElement("test").closeDocument();
+            assertThat(out.toString()).isEqualTo("<?xml 
version='1.0'?><root><hello><world>hello world</world></hello><test/></root>");
+        }
     }
 
     @Test
@@ -545,7 +479,10 @@ class XmlWriterTest {
                 "</base>");
         byte[] rawDocument = "<?xml version='1.0'?><root><hello><world>hello 
world</world></hello><test/></root>".getBytes(StandardCharsets.UTF_8);
         Document document = XmlUtils.toDom(new 
ByteArrayInputStream(rawDocument));
-        
writer.startDocument().openElement("base").append(document).closeDocument();
-        assertEquals(expected, out.toString());
+        StringWriter out = new StringWriter();
+        try (XmlWriter writer = new XmlWriter(out)) {
+            
writer.startDocument().openElement("base").append(document).closeDocument();
+        }
+        assertThat(out.toString()).isEqualTo(expected);
     }
 }
diff --git 
a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/XmlUtils.java 
b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/XmlUtils.java
index c7e3cfcc..7f53c9a6 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/XmlUtils.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/XmlUtils.java
@@ -46,7 +46,7 @@ import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 
-import org.apache.rat.report.xml.writer.IXmlWriter;
+import org.apache.rat.report.xml.writer.XmlWriter;
 import org.apache.rat.utils.DefaultLog;
 import org.apache.rat.utils.StandardXmlFactory;
 import org.w3c.dom.Document;
@@ -143,7 +143,7 @@ public final class XmlUtils {
         return StandardXmlFactory.documentBuilder().parse(inputStream);
     }
 
-    public static void writeAttribute(final IXmlWriter writer, final String 
name, final boolean booleanValue)
+    public static void writeAttribute(final XmlWriter writer, final String 
name, final boolean booleanValue)
             throws IOException {
         final String value = Boolean.toString(booleanValue);
         writer.attribute(name, value);

Reply via email to