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("-->", "-–>")); } } - 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&that", out.toString(), "Amphersands must be escaped"); - assertEquals( - writer, writer.content("small<large"), "XmlWriters should always return themselves"); - assertEquals("<alpha>this&thatsmall<large", out.toString(), "Left angle brackets must be escaped"); - assertEquals( - writer, writer.content("12>1"), "XmlWriters should always return themselves"); - assertEquals("<alpha>this&thatsmall<large12>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&that"); + writer.content("small<large"); + assertThat(out.toString()).isEqualTo("<alpha>this&thatsmall<large"); + writer.content("12>1"); + assertThat(out.toString()).isEqualTo("<alpha>this&thatsmall<large12>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&that'", out.toString(), "Amphersands must be escaped"); - assertEquals( - writer, writer.attribute("two", "small<large"), "XmlWriters should always return themselves"); - assertEquals("<alpha one='this&that' two='small<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&that' two='small<large' three='12>1'", out.toString(), "Choose to escape right angle brackets"); - assertEquals( - writer, writer.attribute("four", "'quote'"), "XmlWriters should always return themselves"); - assertEquals("<alpha one='this&that' two='small<large' three='12>1' four=''quote''", out.toString(), "Apostrophes must be escape"); - assertEquals( - writer, writer.attribute("five", "\"quote\""), "XmlWriters should always return themselves"); - assertEquals("<alpha one='this&that' two='small<large' three='12>1' four=''quote'' five='"quote"'", 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&that' two='small<large' three='12>1' four=''quote'' five='"quote"'"); + } } @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);
