From 9493443d9a892e405b271efbbd3a7c5e6c18f952 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Georg=20Gra=C3=9Fnick?=
 <georg.grassnick@mailbox.tu-dresden.de>
Date: Wed, 7 Aug 2019 15:50:51 +0200
Subject: [PATCH] Add CategorialPointListContainer

---
 .../de/tudresden/inf/mci/brailleplot/App.java |  1 +
 .../AbstractPointContainer.java               | 41 +++++++++++++-
 .../CategoricalPointListContainer.java        | 43 +++++++++++++++
 ...mpleCategoricalPointListContainerImpl.java | 54 +++++++++++++++++++
 .../examples/csv/0_bar_chart_categorical.csv  |  5 ++
 5 files changed, 143 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/CategoricalPointListContainer.java
 create mode 100644 src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java
 create mode 100644 src/main/resources/examples/csv/0_bar_chart_categorical.csv

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 1ce5f3e2..f621993b 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/App.java
@@ -156,6 +156,7 @@ public final class App {
 
             CsvParser csvParser = new CsvParser(csvReader, ',', '\"');
             PointListContainer<PointList> container = csvParser.parse(CsvType.X_ALIGNED_CATEGORIES, CsvOrientation.VERTICAL);
+            mLogger.debug("Internal data representation:\n {}", container.toString());
             BarChart barChart = new BarChart(container);
 
             // Render diagram
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/AbstractPointContainer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/AbstractPointContainer.java
index 07228cc5..deae7da5 100644
--- a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/AbstractPointContainer.java
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/AbstractPointContainer.java
@@ -11,7 +11,7 @@ import java.util.stream.Stream;
  * Abstract parent class for {@link SimplePointListContainerImpl} and {@link SimplePointListImpl}.
  * @param <T> The type of the elements stored in this container.
  * @author Georg Graßnick
- * @version 2019.07.29
+ * @version 2019.08.02
  */
 public abstract class AbstractPointContainer<T extends MinMaxPos2D<Double>> implements PointContainer<T>, MinMaxPos2D<Double> {
 
@@ -82,4 +82,43 @@ public abstract class AbstractPointContainer<T extends MinMaxPos2D<Double>> impl
         mMaxY = Math.max(element.getMaxY(), mMaxY);
         mMinY = Math.min(element.getMinY(), mMinY);
     }
+
+    private String getRecursiveIndentation(final int depth) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < depth; i++) {
+            sb.append("    ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Generates a String representing the data structure.
+     * This function is called recursively on the child elements of a container, so that in the end,
+     * the caller receives a visual overview of the contents of this container down to the leaf nodes (@link Point2DDouble).
+     * @param depth The recursion depth of the current call.
+     * @return A recursive String representation of the container.
+     */
+    protected String toRecursiveString(final int depth) {
+        StringBuilder sb = new StringBuilder();
+
+        if (depth == 0) {
+            sb.append(this.getClass()).append(":\n");
+        }
+            for (T e: mElements) {
+                sb.append(getRecursiveIndentation(depth + 1)).append(e.getClass()).append(":\n");
+                if (e instanceof AbstractPointContainer) {
+                    @SuppressWarnings("unchecked")
+                    AbstractPointContainer<T> a = ((AbstractPointContainer<T>) e);
+                    sb.append(a.toRecursiveString(depth + 1));
+                } else {
+                    sb.append(getRecursiveIndentation(depth + 2)).append(e.toString()).append("\n");
+                }
+            }
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return toRecursiveString(0);
+    }
 }
diff --git a/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/CategoricalPointListContainer.java b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/CategoricalPointListContainer.java
new file mode 100644
index 00000000..55fb3a68
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/CategoricalPointListContainer.java
@@ -0,0 +1,43 @@
+package de.tudresden.inf.mci.brailleplot.datacontainers;
+
+import java.util.Iterator;
+
+/**
+ * A PointListContainer containing information about different categories.
+ * This will e.g. be useful when dealing with stacked bar charts.
+ * @param <T> The specific type of the {@link PointList}. Currently, there is only one generic type,
+ *           but later on, one might define additional specialized {@link PointList} implementations.
+ * @author Georg Graßnick
+ * @version 2019.08.02
+ */
+public interface CategoricalPointListContainer<T extends PointList> extends PointListContainer<T> {
+
+    /**
+     * Adds a new category.
+     * @param category The category to add.
+     * @return The index, the category is inserted at.
+     */
+    int pushBackCategory(String category);
+
+    /**
+     * Get an iterator over all categories.
+     * @return An {@link Iterator} that iterates all contained categories;
+     */
+     Iterator<String> categoriesIterator();
+
+    /**
+     * Get the number of categories.
+     * @return The number of categories.
+     */
+    int getNumberOfCategories();
+
+    /**
+     * Get a category at a specif index.
+     * Indices are ranged from 0 to {@link CategoricalPointListContainer#getNumberOfCategories()} (exclusive end).
+     * @param index The index of the requested category.
+     * @return The {@link String} representing the category at the requested index.
+     * @throws IndexOutOfBoundsException If the index is {@literal <} 0 or index is {@literal >=} the return value of
+     * {@link CategoricalPointListContainer#getNumberOfCategories()}
+     */
+     String getCategory(int index) throws IndexOutOfBoundsException;
+}
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
new file mode 100644
index 00000000..f83c5f8a
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/mci/brailleplot/datacontainers/SimpleCategoricalPointListContainerImpl.java
@@ -0,0 +1,54 @@
+package de.tudresden.inf.mci.brailleplot.datacontainers;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Low effort implementation of {@link CategoricalPointListContainer}{@literal <}{@link PointList}{@literal >}.
+ * @author Georg Graßnick
+ * @version 2019.08.02
+ */
+public class SimpleCategoricalPointListContainerImpl extends SimplePointListContainerImpl implements CategoricalPointListContainer<PointList> {
+
+    private ArrayList<String> mCategories;
+
+    public SimpleCategoricalPointListContainerImpl() {
+        this(new ArrayList<>(), new ArrayList<>());
+    }
+
+    public SimpleCategoricalPointListContainerImpl(final List<String> initialCategories) {
+        this(new ArrayList<>(), initialCategories);
+    }
+
+    public SimpleCategoricalPointListContainerImpl(final List<PointList> initialElements, final List<String> initialCategories) {
+        super(initialElements);
+        mCategories = new ArrayList<>(Objects.requireNonNull(initialCategories));
+    }
+
+    @Override
+    public int pushBackCategory(final String category) {
+        Objects.requireNonNull(category);
+        mCategories.add(category);
+        return mCategories.size() - 1;
+    }
+
+    @Override
+    public Iterator<String> categoriesIterator() {
+        return mCategories.iterator();
+    }
+
+    @Override
+    public int getNumberOfCategories() {
+        return mCategories.size();
+    }
+
+    @Override
+    public String getCategory(final int index) throws IndexOutOfBoundsException {
+        if (index < 0 || index >= mCategories.size()) {
+            throw new IndexOutOfBoundsException("Index " + index + " out of bounds. Current bounds: 0 and " + mCategories.size());
+        }
+        return mCategories.get(index);
+    }
+}
diff --git a/src/main/resources/examples/csv/0_bar_chart_categorical.csv b/src/main/resources/examples/csv/0_bar_chart_categorical.csv
new file mode 100644
index 00000000..9804ec7a
--- /dev/null
+++ b/src/main/resources/examples/csv/0_bar_chart_categorical.csv
@@ -0,0 +1,5 @@
+, Reihe a , Reihe b , Reihe c, Reihe d
+Kat.1 ,3 ,"2,5" ,1 ,3
+Kat.2 ,4 ,3 ,2 ,5
+Kat.3 ,"4,5" ,3 ,1,"0,2"
+Kat.4 ,"4,5" ,3 ,1,"0,2"
-- 
GitLab