From fa06d77df8272afb13814aa500366a6ea45e0e8a Mon Sep 17 00:00:00 2001 From: Vladyslav Chernov Date: Mon, 24 Jul 2023 13:17:40 -0700 Subject: [PATCH 1/5] BAEL-5852: What does the Holder do in Java? --- .../main/java/com/baeldung/holder/Holder.java | 9 ++++++ .../com/baeldung/holder/SupplierService.java | 13 ++++++++ .../holder/SupplierServiceUnitTest.java | 31 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java create mode 100644 core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java create mode 100644 core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java new file mode 100644 index 0000000000..da066ee5c6 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java @@ -0,0 +1,9 @@ +package com.baeldung.holder; + +public class Holder { + public T value; + + public Holder(T value) { + this.value = value; + } +} diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java new file mode 100644 index 0000000000..473a4de423 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java @@ -0,0 +1,13 @@ +package com.baeldung.holder; + +public class SupplierService { + public void getSupplierByZipCode(String zip, Holder resultHolder) { + // Let's pretend we did some work here to get the supplier + // And let's say all zip codes starting with "9" are valid, just for this example + if (zip.startsWith("9")) { + resultHolder.value = true; + } else { + resultHolder.value = false; + } + } +} diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java new file mode 100644 index 0000000000..e1446fc229 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java @@ -0,0 +1,31 @@ +package com.baeldung.holder; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class SupplierServiceUnitTest { + + @Test + public void givenValidZipCode_whenGetSupplierByZipCode_thenTrue() { + SupplierService service = new SupplierService(); + Holder resultHolder = new Holder<>(false); + String zipCode = "98682"; + + service.getSupplierByZipCode(zipCode, resultHolder); + + assertTrue(resultHolder.value); + } + + @Test + public void givenInvalidZipCode_whenGetSupplierByZipCode_thenFalse() { + SupplierService service = new SupplierService(); + Holder resultHolder = new Holder<>(true); + String zipCode = "12345"; + + service.getSupplierByZipCode(zipCode, resultHolder); + + assertFalse(resultHolder.value); + } +} From 0bb7a8db27e93e108459f1c9baa340a898a23176 Mon Sep 17 00:00:00 2001 From: Vladyslav Chernov Date: Sun, 17 Sep 2023 21:57:35 -0700 Subject: [PATCH 2/5] BAEL-5755: Convert an XML file to CSV file --- .../baeldung/xml/xml2csv/Xml2CsvExample.java | 113 +++++++++++++ xml/src/main/resources/xml2csv/data.xml | 26 +++ xml/src/main/resources/xml2csv/style.xsl | 21 +++ .../xml/xml2csv/Xml2CsvExampleUnitTest.java | 155 ++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java create mode 100644 xml/src/main/resources/xml2csv/data.xml create mode 100644 xml/src/main/resources/xml2csv/style.xsl create mode 100644 xml/src/test/java/com/baeldung/xml/xml2csv/Xml2CsvExampleUnitTest.java diff --git a/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java b/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java new file mode 100644 index 0000000000..d2acafc881 --- /dev/null +++ b/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java @@ -0,0 +1,113 @@ +package com.baeldung.xml.xml2csv; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +public class Xml2CsvExample { + + private static final String BASE_PATH = "xml/src/main/resources/xml2csv/"; + + private static final String STYLE_XSL = BASE_PATH + "style.xsl"; + private static final String DATA_XML = BASE_PATH + "data.xml"; + private static final String OUTPUT_CSV_XSTL = BASE_PATH + "output_xstl.csv"; + private static final String OUTPUT_CSV_STAX = BASE_PATH + "output_stax.csv"; + + public static void main(String[] args) { + try { + convertXml2CsvXslt(STYLE_XSL, DATA_XML, OUTPUT_CSV_XSTL); + convertXml2CsvStax(DATA_XML, OUTPUT_CSV_STAX); + } catch (IOException | TransformerException e) { + e.printStackTrace(); + } + } + + protected static void convertXml2CsvXslt(String xslPath, String xmlPath, String csvPath) throws IOException, TransformerException { + StreamSource styleSource = new StreamSource(new File(xslPath)); + Transformer transformer = TransformerFactory.newInstance() + .newTransformer(styleSource); + Source source = new StreamSource(new File(xmlPath)); + Result outputTarget = new StreamResult(new File(csvPath)); + transformer.transform(source, outputTarget); + } + + protected static void convertXml2CsvStax(String xmlFilePath, String csvFilePath) throws IOException, TransformerException { + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + + try (InputStream in = Files.newInputStream(Paths.get(xmlFilePath)); BufferedWriter writer = new BufferedWriter(new FileWriter(csvFilePath))) { + + // Write header to CSV + writer.write("bookstore_id,book_id,category,title,author_id,author_name,price\n"); + + XMLStreamReader reader = inputFactory.createXMLStreamReader(in); + + String currentElement; + StringBuilder csvRow = new StringBuilder(); + StringBuilder bookstoreInfo = new StringBuilder(); + + while (reader.hasNext()) { + int eventType = reader.next(); + + switch (eventType) { + case XMLStreamConstants.START_ELEMENT: + currentElement = reader.getLocalName(); + if ("Bookstore".equals(currentElement)) { + bookstoreInfo.setLength(0); // clear previous bookstore info + bookstoreInfo.append(reader.getAttributeValue(null, "id")) + .append(","); + } + if ("Book".equals(currentElement)) { + csvRow.append(bookstoreInfo) + .append(reader.getAttributeValue(null, "id")) + .append(",") + .append(reader.getAttributeValue(null, "category")) + .append(","); + } + if ("Author".equals(currentElement)) { + csvRow.append(reader.getAttributeValue(null, "id")) + .append(","); + } + break; + + case XMLStreamConstants.CHARACTERS: + if (!reader.isWhiteSpace()) { + csvRow.append(reader.getText() + .trim()) + .append(","); + } + break; + + case XMLStreamConstants.END_ELEMENT: + if ("Book".equals(reader.getLocalName())) { + // remove the last comma and add a newline + csvRow.setLength(csvRow.length() - 1); + csvRow.append("\n"); + writer.write(csvRow.toString()); + + // Reset the StringBuilder for the next row + csvRow.setLength(0); + } + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/xml/src/main/resources/xml2csv/data.xml b/xml/src/main/resources/xml2csv/data.xml new file mode 100644 index 0000000000..385fcf661c --- /dev/null +++ b/xml/src/main/resources/xml2csv/data.xml @@ -0,0 +1,26 @@ + + + + + + Death and the Penguin + Andrey Kurkov + 10.99 + + + Kobzar + Taras Shevchenko + 8.50 + + + + + + + Voroshilovgrad + Serhiy Zhadan + 12.99 + + + + diff --git a/xml/src/main/resources/xml2csv/style.xsl b/xml/src/main/resources/xml2csv/style.xsl new file mode 100644 index 0000000000..ea23880092 --- /dev/null +++ b/xml/src/main/resources/xml2csv/style.xsl @@ -0,0 +1,21 @@ + + + + + bookstore_id,book_id,category,title,author_id,author_name,price + + + + + + + + + + + + + + + + diff --git a/xml/src/test/java/com/baeldung/xml/xml2csv/Xml2CsvExampleUnitTest.java b/xml/src/test/java/com/baeldung/xml/xml2csv/Xml2CsvExampleUnitTest.java new file mode 100644 index 0000000000..3fa44b427c --- /dev/null +++ b/xml/src/test/java/com/baeldung/xml/xml2csv/Xml2CsvExampleUnitTest.java @@ -0,0 +1,155 @@ +package com.baeldung.xml.xml2csv; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.xml.transform.TransformerException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +public class Xml2CsvExampleUnitTest { + + private static final String BASE_PATH = "src/main/resources/xml2csv/"; + + private static final String STYLE_XSL = BASE_PATH + "style.xsl"; + private static final String DATA_XML = BASE_PATH + "data.xml"; + private static final String TEMP_OUTPUT_CSV = BASE_PATH + "tempOutput.xml"; + + @AfterEach + public void teardown() { + new File(TEMP_OUTPUT_CSV).delete(); + } + + @Test + public void whenConvertXml2CsvXslt_thenCsvFileIsCreated() throws IOException, TransformerException { + Xml2CsvExample.convertXml2CsvXslt(STYLE_XSL, DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + assertTrue(csvFile.exists()); + } + + @Test + public void whenConvertXml2CsvStax_thenCsvFileIsCreated() throws IOException, TransformerException { + Xml2CsvExample.convertXml2CsvStax(DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + assertTrue(csvFile.exists()); + } + + @Test + public void whenConvertXml2CsvXslt_thenCsvFileIsNotEmpty() throws IOException, TransformerException { + Xml2CsvExample.convertXml2CsvXslt(STYLE_XSL, DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + BufferedReader reader = new BufferedReader(new FileReader(csvFile)); + String firstLine = reader.readLine(); + assertNotNull(firstLine); + assertFalse(firstLine.isEmpty()); + + reader.close(); + } + + @Test + public void whenConvertXml2CsvStax_thenCsvFileIsNotEmpty() throws IOException, TransformerException { + Xml2CsvExample.convertXml2CsvStax(DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + BufferedReader reader = new BufferedReader(new FileReader(csvFile)); + String firstLine = reader.readLine(); + assertNotNull(firstLine); + assertFalse(firstLine.isEmpty()); + + reader.close(); + } + + @Test + public void whenConvertXml2CsvXsltWithWrongXSL_thenThrowsException() { + String xslWrongPath = BASE_PATH + "wrongFile.xsl"; + + assertThrows(TransformerException.class, () -> Xml2CsvExample.convertXml2CsvXslt(xslWrongPath, DATA_XML, TEMP_OUTPUT_CSV)); + } + + @Test + public void whenConvertXml2CsvXslt_thenCsvMatchesPattern() throws IOException, TransformerException { + String headerPattern = "^bookstore_id,book_id,category,title,author_id,author_name,price$"; + String dataPattern = "^[A-Z0-9]+,[A-Z0-9]+,[a-zA-Z]+,[a-zA-Z0-9\\s]+,[A-Z0-9]+,[a-zA-Z\\s]+,\\d+(\\.\\d{2})?$"; + + Xml2CsvExample.convertXml2CsvXslt(STYLE_XSL, DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + try(BufferedReader reader = new BufferedReader(new FileReader(csvFile))) { + String line; + boolean isFirstLine = true; + while ((line = reader.readLine()) != null) { + if (isFirstLine) { + assertTrue(line.matches(headerPattern), "Header does not match pattern"); + isFirstLine = false; + } else { + assertTrue(line.matches(dataPattern), "Data line does not match pattern"); + } + } + } + } + + @Test + public void whenConvertXml2Stax_thenCsvMatchesPattern() throws IOException, TransformerException { + String headerPattern = "^bookstore_id,book_id,category,title,author_id,author_name,price$"; + String dataPattern = "^[A-Z0-9]+,[A-Z0-9]+,[a-zA-Z]+,[a-zA-Z0-9\\s]+,[A-Z0-9]+,[a-zA-Z\\s]+,\\d+(\\.\\d{2})?$"; + + Xml2CsvExample.convertXml2CsvStax(DATA_XML, TEMP_OUTPUT_CSV); + + File csvFile = new File(TEMP_OUTPUT_CSV); + try(BufferedReader reader = new BufferedReader(new FileReader(csvFile))) { + String line; + boolean isFirstLine = true; + while ((line = reader.readLine()) != null) { + if (isFirstLine) { + assertTrue(line.matches(headerPattern), "Header does not match pattern"); + isFirstLine = false; + } else { + assertTrue(line.matches(dataPattern), "Data line does not match pattern"); + } + } + } + } + + @Test + public void whenConcurrentConversion_thenNoErrors() throws InterruptedException { + int numThreads = 10; + ExecutorService service = Executors.newFixedThreadPool(numThreads); + CountDownLatch latch = new CountDownLatch(numThreads); + + for (int i = 0; i < numThreads; i++) { + final int threadId = i; + service.execute(() -> { + String threadSpecificOutputCsv = BASE_PATH + "tempOutput" + threadId + ".csv"; + try { + Xml2CsvExample.convertXml2CsvXslt(STYLE_XSL, DATA_XML, threadSpecificOutputCsv); + assertTrue(Files.exists(Paths.get(threadSpecificOutputCsv)), "File should exist"); + } catch (IOException | TransformerException e) { + fail("Exception should not be thrown: " + e.getMessage()); + } finally { + new File(threadSpecificOutputCsv).delete(); + latch.countDown(); + } + }); + } + + latch.await(); + } + +} From a16aa0ee61b10cfce6f78ff0146bbedbabd43a90 Mon Sep 17 00:00:00 2001 From: Vladyslav Chernov Date: Fri, 20 Oct 2023 15:17:36 -0700 Subject: [PATCH 3/5] BAEL-5755: remove comments --- .../main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java b/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java index d2acafc881..99e5c7c0cb 100644 --- a/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java +++ b/xml/src/main/java/com/baeldung/xml/xml2csv/Xml2CsvExample.java @@ -51,7 +51,6 @@ public class Xml2CsvExample { try (InputStream in = Files.newInputStream(Paths.get(xmlFilePath)); BufferedWriter writer = new BufferedWriter(new FileWriter(csvFilePath))) { - // Write header to CSV writer.write("bookstore_id,book_id,category,title,author_id,author_name,price\n"); XMLStreamReader reader = inputFactory.createXMLStreamReader(in); @@ -67,7 +66,7 @@ public class Xml2CsvExample { case XMLStreamConstants.START_ELEMENT: currentElement = reader.getLocalName(); if ("Bookstore".equals(currentElement)) { - bookstoreInfo.setLength(0); // clear previous bookstore info + bookstoreInfo.setLength(0); bookstoreInfo.append(reader.getAttributeValue(null, "id")) .append(","); } @@ -94,12 +93,10 @@ public class Xml2CsvExample { case XMLStreamConstants.END_ELEMENT: if ("Book".equals(reader.getLocalName())) { - // remove the last comma and add a newline csvRow.setLength(csvRow.length() - 1); csvRow.append("\n"); writer.write(csvRow.toString()); - // Reset the StringBuilder for the next row csvRow.setLength(0); } break; From cea912e42ff8fc40bac122b0e78625f5ca92811a Mon Sep 17 00:00:00 2001 From: Vladyslav Chernov Date: Sat, 4 Nov 2023 15:33:30 -0700 Subject: [PATCH 4/5] BAEL-5852: remove comments --- .../src/main/java/com/baeldung/holder/SupplierService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java index 473a4de423..66889f99bf 100644 --- a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java +++ b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java @@ -2,8 +2,6 @@ package com.baeldung.holder; public class SupplierService { public void getSupplierByZipCode(String zip, Holder resultHolder) { - // Let's pretend we did some work here to get the supplier - // And let's say all zip codes starting with "9" are valid, just for this example if (zip.startsWith("9")) { resultHolder.value = true; } else { From ee97389e28c04795bd0c45a543a5eebfce05429f Mon Sep 17 00:00:00 2001 From: Vladyslav Chernov Date: Sat, 4 Nov 2023 15:38:37 -0700 Subject: [PATCH 5/5] BAEL-5755: remove BAEL-5852 commits --- .../main/java/com/baeldung/holder/Holder.java | 9 ------ .../com/baeldung/holder/SupplierService.java | 11 ------- .../holder/SupplierServiceUnitTest.java | 31 ------------------- 3 files changed, 51 deletions(-) delete mode 100644 core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java delete mode 100644 core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java delete mode 100644 core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java deleted file mode 100644 index da066ee5c6..0000000000 --- a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/Holder.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.baeldung.holder; - -public class Holder { - public T value; - - public Holder(T value) { - this.value = value; - } -} diff --git a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java b/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java deleted file mode 100644 index 66889f99bf..0000000000 --- a/core-java-modules/core-java-lang-oop-generics/src/main/java/com/baeldung/holder/SupplierService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.holder; - -public class SupplierService { - public void getSupplierByZipCode(String zip, Holder resultHolder) { - if (zip.startsWith("9")) { - resultHolder.value = true; - } else { - resultHolder.value = false; - } - } -} diff --git a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java b/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java deleted file mode 100644 index e1446fc229..0000000000 --- a/core-java-modules/core-java-lang-oop-generics/src/test/java/com/baeldung/holder/SupplierServiceUnitTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.baeldung.holder; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class SupplierServiceUnitTest { - - @Test - public void givenValidZipCode_whenGetSupplierByZipCode_thenTrue() { - SupplierService service = new SupplierService(); - Holder resultHolder = new Holder<>(false); - String zipCode = "98682"; - - service.getSupplierByZipCode(zipCode, resultHolder); - - assertTrue(resultHolder.value); - } - - @Test - public void givenInvalidZipCode_whenGetSupplierByZipCode_thenFalse() { - SupplierService service = new SupplierService(); - Holder resultHolder = new Holder<>(true); - String zipCode = "12345"; - - service.getSupplierByZipCode(zipCode, resultHolder); - - assertFalse(resultHolder.value); - } -}