From 0eb14cc3e78127db858a36d4479dc8e9c0d46184 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Georg=20Gra=C3=9Fnick?=
 <georg.grassnick@mailbox.tu-dresden.de>
Date: Thu, 8 Aug 2019 21:15:43 +0200
Subject: [PATCH] Add own XAlignedCategories parser

---
 .../de/tudresden/inf/mci/brailleplot/App.java |   6 +-
 .../brailleplot/csvparser/CsvDotParser.java   |   2 +-
 .../csvparser/CsvParseAlgorithm.java          |   7 +-
 .../mci/brailleplot/csvparser/CsvParser.java  |  19 +--
 .../CsvXAlignedCategoriesParser.java          | 118 +++++++-----------
 .../csvparser/CsvXAlignedParser.java          |   2 +-
 ...mpleCategoricalPointListContainerImpl.java |  11 ++
 ...v => 0_bar_chart_categorical_vertical.csv} |   0
 8 files changed, 78 insertions(+), 87 deletions(-)
 rename src/main/resources/examples/csv/{0_bar_chart_categorical_horizontal.csv => 0_bar_chart_categorical_vertical.csv} (100%)

diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
index d6fe60d5..fa831a78 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
@@ -11,8 +11,8 @@ import de.tudresden.inf.mci.brailleplot.configparser.Printer;
 import de.tudresden.inf.mci.brailleplot.csvparser.CsvOrientation;
 import de.tudresden.inf.mci.brailleplot.csvparser.CsvParser;
 import de.tudresden.inf.mci.brailleplot.csvparser.CsvType;
+import de.tudresden.inf.mci.brailleplot.datacontainers.CategoricalPointListContainer;
 import de.tudresden.inf.mci.brailleplot.datacontainers.PointList;
-import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer;
 import de.tudresden.inf.mci.brailleplot.diagrams.BarChart;
 import de.tudresden.inf.mci.brailleplot.printabledata.SimpleMatrixDataImpl;
 import de.tudresden.inf.mci.brailleplot.rendering.MasterRenderer;
@@ -151,11 +151,11 @@ public final class App {
 
             // Parse csv data
             ClassLoader classloader = Thread.currentThread().getContextClassLoader();
-            InputStream csvStream = classloader.getResourceAsStream("examples/csv/0_bar_chart.csv");
+            InputStream csvStream = classloader.getResourceAsStream("examples/csv/0_bar_chart_categorical_vertical.csv");
             Reader csvReader = new BufferedReader(new InputStreamReader(csvStream));
 
             CsvParser csvParser = new CsvParser(csvReader, ',', '\"');
-            PointListContainer<PointList> container = csvParser.parse(CsvType.X_ALIGNED, CsvOrientation.VERTICAL);
+            CategoricalPointListContainer<PointList> container = csvParser.parse(CsvType.X_ALIGNED_CATEGORIES, CsvOrientation.VERTICAL);
             mLogger.debug("Internal data representation:\n {}", container.toString());
             BarChart barChart = new BarChart(container);
 
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvDotParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvDotParser.java
index 351d4667..ee3dfab1 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvDotParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvDotParser.java
@@ -16,7 +16,7 @@ import java.util.Objects;
  * @author SVGPlott-Team, Georg Graßnick
  * @version 2019.07.29
  */
-public class CsvDotParser extends CsvParseAlgorithm {
+public class CsvDotParser extends CsvParseAlgorithm<PointListContainer<PointList>> {
 
     /**
      * Parses scattered point data in horizontal data sets, alternating mX and mY. The
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParseAlgorithm.java b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParseAlgorithm.java
index 9ed81867..1d6109db 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParseAlgorithm.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParseAlgorithm.java
@@ -14,10 +14,11 @@ import java.util.Objects;
 /**
  * An algorithm for parsing CSV data. Contains implementations for two
  * orientations of the data in the file.
+ * @param <T> The type of PointContainer, that is parsed to.
  * @author SVGPlott-Team, Georg Graßnick
  * @version 2019.07.29
  */
-public abstract class CsvParseAlgorithm {
+public abstract class CsvParseAlgorithm<T extends PointListContainer<PointList>> {
 
     protected final Logger mLogger = LoggerFactory.getLogger(getClass());
 
@@ -27,7 +28,7 @@ public abstract class CsvParseAlgorithm {
      * @param csvData The parsed input String.
      * @return A {@link PointListContainer}{@literal <PointList>} representing the data.
      */
-    public abstract PointListContainer<PointList> parseAsHorizontalDataSets(List<? extends List<String>> csvData);
+    public abstract T parseAsHorizontalDataSets(List<? extends List<String>> csvData);
 
     /**
      * If the data sets are oriented horizontally, i.e. in rows, parse the rows into
@@ -35,7 +36,7 @@ public abstract class CsvParseAlgorithm {
      * @param csvData The parsed input String.
      * @return A {@link PointListContainer}{@literal <}{@link PointList}{@literal >} representing the data.
      */
-    public abstract PointListContainer<PointList> parseAsVerticalDataSets(List<? extends List<String>> csvData);
+    public abstract T parseAsVerticalDataSets(List<? extends List<String>> csvData);
 
     /**
      * Adds a {@link Point2DDouble} to a {@link PointList} in a {@link PointListContainer}{@literal <}{@link PointList}{@literal >},
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParser.java
index d98a8345..98f67fae 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvParser.java
@@ -8,7 +8,6 @@ import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 import java.io.IOException;
 import java.io.Reader;
 import java.util.ArrayList;
@@ -20,7 +19,7 @@ import java.util.Objects;
  * @author SVGPlott-Team, Georg Graßnick
  * @version 2019.07.29
  */
-public class CsvParser {
+public final class CsvParser {
 
     private final Logger mLogger = LoggerFactory.getLogger(CsvParser.class);
 
@@ -56,27 +55,29 @@ public class CsvParser {
 
     /**
      * Chooses the right parsing algorithm.
+     * Casting in this method is not guaranteed to be safe, so use at your own risk.
      * @param csvType CsvType
      * @param csvOrientation CsvOrientation
      * @return PointListList
      */
-    public PointListContainer<PointList> parse(final CsvType csvType, final CsvOrientation csvOrientation) {
-        CsvParseAlgorithm csvParseAlgorithm;
+    @SuppressWarnings("unchecked")
+    public <T extends PointListContainer<PointList>> T parse(final CsvType csvType, final CsvOrientation csvOrientation) {
+        CsvParseAlgorithm<T> csvParseAlgorithm;
 
         mLogger.debug("Parsing data as \"{}\", orientation \"{}\"", csvType, csvOrientation);
 
         switch (csvType) {
         case DOTS:
-            csvParseAlgorithm = new CsvDotParser();
+            csvParseAlgorithm = ((CsvParseAlgorithm<T>) new CsvDotParser());
             break;
         case X_ALIGNED:
-            csvParseAlgorithm = new CsvXAlignedParser();
+            csvParseAlgorithm = ((CsvParseAlgorithm<T>) new CsvXAlignedParser());
             break;
         case X_ALIGNED_CATEGORIES:
-            csvParseAlgorithm = new CsvXAlignedCategoriesParser();
+            csvParseAlgorithm = ((CsvParseAlgorithm<T>) new CsvXAlignedCategoriesParser());
             break;
         default:
-            return null;
+            throw new UnsupportedOperationException();
         }
 
         switch (csvOrientation) {
@@ -85,7 +86,7 @@ public class CsvParser {
         case VERTICAL:
             return csvParseAlgorithm.parseAsVerticalDataSets(mCsvData);
         default:
-            return null;
+            throw new UnsupportedOperationException();
         }
     }
 }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedCategoriesParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedCategoriesParser.java
index d696d7f8..d468e89a 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedCategoriesParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedCategoriesParser.java
@@ -1,8 +1,8 @@
 package de.tudresden.inf.mci.brailleplot.csvparser;
 
+import de.tudresden.inf.mci.brailleplot.datacontainers.CategoricalPointListContainer;
 import de.tudresden.inf.mci.brailleplot.datacontainers.PointList;
-import de.tudresden.inf.mci.brailleplot.datacontainers.PointListContainer;
-import de.tudresden.inf.mci.brailleplot.datacontainers.SimplePointListContainerImpl;
+import de.tudresden.inf.mci.brailleplot.datacontainers.SimpleCategoricalPointListContainerImpl;
 import de.tudresden.inf.mci.brailleplot.datacontainers.SimplePointListImpl;
 import de.tudresden.inf.mci.brailleplot.point.Point2DDouble;
 
@@ -17,13 +17,13 @@ import java.util.Objects;
  * @author SVGPlott-Team, Georg Graßnick
  * @version 2019.07.29
  */
-public class CsvXAlignedCategoriesParser extends CsvParseAlgorithm {
+public class CsvXAlignedCategoriesParser extends CsvParseAlgorithm<CategoricalPointListContainer<PointList>> {
 
     @Override
-    public PointListContainer<PointList> parseAsHorizontalDataSets(final List<? extends List<String>> csvData) {
+    public CategoricalPointListContainer<PointList> parseAsHorizontalDataSets(final List<? extends List<String>> csvData) {
         Objects.requireNonNull(csvData);
 
-        PointListContainer<PointList> container = new SimplePointListContainerImpl();
+        CategoricalPointListContainer<PointList> container = new SimpleCategoricalPointListContainerImpl();
 
         Iterator<? extends List<String>> rowIterator = csvData.iterator();
 
@@ -33,6 +33,12 @@ public class CsvXAlignedCategoriesParser extends CsvParseAlgorithm {
 
         Iterator<String> lineIterator = rowIterator.next().iterator();
 
+        // Move the iterator to the first category name
+        if (!lineIterator.hasNext()) {
+            return container;
+        }
+
+        lineIterator.next();
 
         if (!lineIterator.hasNext()) {
             return container;
@@ -53,11 +59,6 @@ public class CsvXAlignedCategoriesParser extends CsvParseAlgorithm {
             if (!lineIterator.hasNext()) {
                 continue;
             }
-
-            if (!categoriesIt.hasNext()) {
-                continue;
-            }
-
             String category = categoriesIt.next();
             PointList pointList = new SimplePointListImpl(category);
             container.pushBack(pointList);
@@ -90,73 +91,50 @@ public class CsvXAlignedCategoriesParser extends CsvParseAlgorithm {
     }
 
     @Override
-    public PointListContainer<PointList> parseAsVerticalDataSets(final List<? extends List<String>> csvData) {
+    public CategoricalPointListContainer<PointList> parseAsVerticalDataSets(final List<? extends List<String>> csvData) {
         Objects.requireNonNull(csvData);
 
-        PointListContainer<PointList> container = new SimplePointListContainerImpl();
-        Iterator<? extends List<String>> rowIterator = csvData.iterator();
-
-
-        if (!rowIterator.hasNext()) {
-            return container;
-        }
-
-        Iterator<String> lineIterator = rowIterator.next().iterator();
-
-        // Move the iterator to the first title
-        /*if (!lineIterator.hasNext()) {
-            return container;
-        }
-
-        lineIterator.next();*/
+        CategoricalPointListContainer<PointList> container = new SimpleCategoricalPointListContainerImpl();
+        Iterator<? extends List<String>> rowIt = csvData.iterator();
 
-        if (!lineIterator.hasNext()) {
-            return container;
-        }
+        int rowNum = 0; // Keep track of the row number, so that we can include the erroneous row number in the exception.
+        while (rowIt.hasNext()) {
+            Iterator<String> lineIt = rowIt.next().iterator();
+            rowNum++;
 
-        // Add a PointList for each title
-        while (lineIterator.hasNext()) {
-            PointList pointList = new SimplePointListImpl();
-            pointList.setName(lineIterator.next());
-            container.pushBack(pointList);
-        }
-
-        // Add the data
-        Iterator<PointList> it = container.iterator();
-        int categoryCounter = 0;
-        while (rowIterator.hasNext()) {
-            lineIterator = rowIterator.next().iterator();
-            if (!lineIterator.hasNext()) {
-                categoryCounter++;
-                continue;
-            }
-
-            // Find out the category title
-            String currentCategory = lineIterator.next();
-            if (it.hasNext()) {
-                it.next().setName(currentCategory);
-            }
-
-            // Find out the mY values and add the points to the respective lists
-            int currentDataSet = 0;
-            while (lineIterator.hasNext()) {
-                Number yValue;
-                try {
-                    yValue = Constants.NUMBER_FORMAT.parse(lineIterator.next());
-                } catch (ParseException e) {
-                    currentDataSet++;
-                    continue;
+            // Check if we are in the first line, were all the categories are defined ...
+            if (rowNum == 1) {
+                while (lineIt.hasNext()) {
+                    container.pushBackCategory(lineIt.next());
                 }
-
-                Point2DDouble newPoint = new Point2DDouble(categoryCounter, yValue.doubleValue());
-                addPointToPointListList(container, currentDataSet, newPoint);
-                currentDataSet++;
+            // ... or if we are in a row, were the actual data sets are defined
+            } else {
+                // Get the name for the values of a data set
+                if (!lineIt.hasNext()) {
+                    throw new MalformedCsvException("Line: " + rowNum + ": Data set must contain a name");
+                }
+                String name = lineIt.next().trim();
+                PointList pl = new SimplePointListImpl(name);
+
+                // Parse all values
+                // Set the x value of each Point to the index of the category, they belong to
+                int columnNum = 0;
+                while (lineIt.hasNext()) {
+                    columnNum++;
+                    String value = lineIt.next().trim();
+
+                    Number val;
+                    try {
+                        val = Constants.NUMBER_FORMAT.parse(value);
+                    } catch (final ParseException pe) {
+                        throw new MalformedCsvException("Line: " + rowNum + ": Could not parse value", pe);
+                    }
+                    Point2DDouble p = new Point2DDouble(columnNum, val.doubleValue());
+                    pl.pushBack(p);
+                }
+                container.pushBack(pl);
             }
-
-            categoryCounter++;
         }
-        // TODO First add points to PointList, then add PointList to PointListContainer, so that there is no need for a calculateExtrema call
-        container.calculateExtrema();
         return container;
     }
 
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedParser.java b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedParser.java
index 1b1b54d7..c38e69a4 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedParser.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/csvparser/CsvXAlignedParser.java
@@ -17,7 +17,7 @@ import java.util.Objects;
  * @author SVGPlott-Team, Georg Graßnick
  * @version 2019.07.29
  */
-public class CsvXAlignedParser extends CsvParseAlgorithm {
+public class CsvXAlignedParser extends CsvParseAlgorithm<PointListContainer<PointList>> {
 
     @Override
     public PointListContainer<PointList> parseAsHorizontalDataSets(final List<? extends List<String>> csvData) {
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java
index f83c5f8a..834e3b3a 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java
@@ -51,4 +51,15 @@ public class SimpleCategoricalPointListContainerImpl extends SimplePointListCont
         }
         return mCategories.get(index);
     }
+
+    @Override
+    protected String toRecursiveString(final int depth) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Categories:\n");
+        for (String s : mCategories) {
+            sb.append(s).append("\n");
+        }
+        sb.append(super.toRecursiveString(depth));
+        return sb.toString();
+    }
 }
diff --git a/src/main/resources/examples/csv/0_bar_chart_categorical_horizontal.csv b/src/main/resources/examples/csv/0_bar_chart_categorical_vertical.csv
similarity index 100%
rename from src/main/resources/examples/csv/0_bar_chart_categorical_horizontal.csv
rename to src/main/resources/examples/csv/0_bar_chart_categorical_vertical.csv
-- 
GitLab