From 2bb4bff13764071f9775fd21151d19d9bcc339f4 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Thu, 14 May 2020 18:28:46 -0500
Subject: [PATCH] Added vertical & horizontal center alignment

---
 README.md                                     | 23 +++++--
 src/main/java/edu/unl/cse/bohn/StringBox.java | 30 ++++++---
 .../java/edu/unl/cse/bohn/StringBoxTest.java  | 65 +++++++++++++++++--
 3 files changed, 98 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index f5df01b..7773298 100644
--- a/README.md
+++ b/README.md
@@ -206,9 +206,9 @@ stringBox.placeString("foo\nbar baz",-1,-1);
 If two inserted strings overlap, the string inserted last will overwrite a portion of the string written first:
 ```
 stringBox.placeString("foo\nbar", Vertical.TOP, 2, Horizontal.LEFT, 10)
-         .placeString("larry\ncurly\nmoe", Vertical.BOTTOM, 5, Horizontal.LEFT, 6)
+         .placeString("larry\ncurly\nmoe", Vertical.BOTTOM, 6, Horizontal.LEFT, 6)
          .placeString("quux\nxyzzy", Vertical.TOP, 3, Horizontal.RIGHT, 20)
-         .placeString("one\ntwo\nthree", Vertical.BOTTOM, 10, Horizontal.RIGHT, 15);
+         .placeString("one\ntwo\nthree", Vertical.BOTTOM, 11, Horizontal.RIGHT, 15);
 ```
 
 
@@ -302,11 +302,20 @@ Equivalent to `placeString(string, Vertical.TOP, topRow, Horizontal.LEFT, leftCo
  
 #### `public StringBox placeString(String string, Vertical verticalAlignment, int verticalPosition, Horizontal horizontalAlignment, int horizontalPosition)`
 
-Places a string in the StringBox. If `verticalAlignment` is `TOP` then the string's first line will be in the
-`verticalPosition`'th row, and each subsequent line will be in each subsequent row. If `verticalAlignment` is `BOTTOM`
-then the string's last line will be in the `verticalPosition`'th row, and each preceding line will be in each preceding
-row. If `horizontalAlignment` is `LEFT` then each line will be left-justified to the `horizontalPosition`'th column. If
-`horizontalAlignment` is `RIGHT` then each line will be right-justified to the `horizontalPosition`'th column.
+Places a string in the StringBox.
+
+If `verticalAlignment` is `TOP` then the string's first line will be in the `verticalPosition`'th row, and each
+subsequent line will be in each subsequent row. If `verticalAlignment` is `BOTTOM` then the string's last line will be
+in the row immediately preceding the `verticalPosition`'th row, and each preceding line will be in each preceding row.
+
+If `horizontalAlignment` is `LEFT` then each line will be left-justified to the `horizontalPosition`'th column. If
+`horizontalAlignment` is `RIGHT` then each line will be right-justified to the `horizontalPosition`'th column; that is,
+the rightmost character will be immediately to the left of the `horizontalPosition`'th column.
+
+In cases of `CENTER` alignment: if there are an odd number of lines then the center line will be on the
+`verticalPosition`'th row. Similarly, if there are an odd number of characters in a line then the center character will
+be in the `horizontalPosition`'th column. If there are an even number of rows (or characters) then the row (character)
+immediately below (to the right of) the midpoint will be in the designated row (column).
 
  * **Parameters:**
    * `string` — the string to be placed in the StringBox
diff --git a/src/main/java/edu/unl/cse/bohn/StringBox.java b/src/main/java/edu/unl/cse/bohn/StringBox.java
index bf6696f..9a01717 100644
--- a/src/main/java/edu/unl/cse/bohn/StringBox.java
+++ b/src/main/java/edu/unl/cse/bohn/StringBox.java
@@ -29,12 +29,12 @@ public class StringBox {
     /**
      * Horizontal alignment directives
      */
-    public enum Horizontal {LEFT, RIGHT}
+    public enum Horizontal {LEFT, CENTER, RIGHT}
 
     /**
      * Vertical alignment directives
      */
-    public enum Vertical {TOP, BOTTOM}
+    public enum Vertical {TOP, CENTER, BOTTOM}
 
     private int maximumWidth;
     private int maximumHeight;
@@ -106,13 +106,23 @@ public class StringBox {
     }
 
     /**
-     * <p>Places a string in the StringBox. If <code>verticalAlignment</code> is <code>TOP</code> then the string's
-     * first line will be in the <code>verticalPosition</code>'th row, and each subsequent line will be in each
-     * subsequent row. If <code>verticalAlignment</code> is <code>BOTTOM</code> then the string's last line will be in
-     * the <code>verticalPosition</code>'th row, and each preceding line will be in each preceding row. If
-     * <code>horizontalAlignment</code> is <code>LEFT</code> then each line will be left-justified to the
+     * <p>Places a string in the StringBox.</p>
+     *
+     * <p>If <code>verticalAlignment</code> is <code>TOP</code> then the string's first line will be in the
+     * <code>verticalPosition</code>'th row, and each subsequent line will be in each subsequent row. If
+     * <code>verticalAlignment</code> is <code>BOTTOM</code> then the string's last line will be in the row immediately
+     * preceding the <code>verticalPosition</code>'th row, and each preceding line will be in each preceding row.</p>
+     *
+     * <p>If <code>horizontalAlignment</code> is <code>LEFT</code> then each line will be left-justified to the
      * <code>horizontalPosition</code>'th column. If <code>horizontalAlignment</code> is <code>RIGHT</code> then each
-     * line will be right-justified to the <code>horizontalPosition</code>'th column.</p>
+     * line will be right-justified to the <code>horizontalPosition</code>'th column; that is, the rightmost character
+     * will be immediately to the left of the <code>horizontalPosition</code>'th column.</p>
+     *
+     * <p>In cases of <code>CENTER</code> alignment: if there are an odd number of lines then the center line will be on
+     * the <code>verticalPosition</code>'th row. Similarly, if there are an odd number of characters in a line then the
+     * center character will be in the <code>horizontalPosition</code>'th column. If there are an even number of rows
+     * (or characters) then the row (character) immediately below (to the right of) the midpoint will be in the
+     * designated row (column).</p>
      *
      * @param string              the string to be placed in the StringBox
      * @param verticalAlignment   vertical alignment directive (top/bottom)
@@ -127,7 +137,8 @@ public class StringBox {
         String[] strings = string != null ? string.split(System.lineSeparator()) : nullishString;
         // This would benefit from the switch expression in JDK 12/13
         int topRow = verticalAlignment == Vertical.TOP ? verticalPosition :
-                  verticalAlignment == Vertical.BOTTOM ? verticalPosition - strings.length + 1 :
+                  verticalAlignment == Vertical.BOTTOM ? verticalPosition - strings.length :
+                  verticalAlignment == Vertical.CENTER ? verticalPosition - strings.length / 2 :
                                                          0;
         int firstRow = Math.max(topRow, 0);
         int lastRow = Math.min(topRow + strings.length, maximumHeight);
@@ -280,6 +291,7 @@ public class StringBox {
                     .replace("\n", "\\");
             int leftColumn = horizontalAlignment == Horizontal.LEFT ? horizontalPosition :
                             horizontalAlignment == Horizontal.RIGHT ? horizontalPosition - modifiedString.length() :
+                           horizontalAlignment == Horizontal.CENTER ? horizontalPosition - modifiedString.length() / 2 :
                                                                       0;
             if (leftColumn > rightEdge) {
                 int paddingSize = leftColumn - rightEdge;
diff --git a/src/test/java/edu/unl/cse/bohn/StringBoxTest.java b/src/test/java/edu/unl/cse/bohn/StringBoxTest.java
index 8a3f3b7..a24618f 100644
--- a/src/test/java/edu/unl/cse/bohn/StringBoxTest.java
+++ b/src/test/java/edu/unl/cse/bohn/StringBoxTest.java
@@ -23,8 +23,10 @@ public class StringBoxTest {
         stringRow = new StringBox.StringRow(10);
     }
 
+    @SuppressWarnings("EmptyMethod")
     @After
     public void tearDown() {
+        // intentionally empty
     }
 
     /* Tests for one row */
@@ -142,7 +144,8 @@ public class StringBoxTest {
         int position1 = 3;
         String input2 = "bar";
         int position2 = 5;
-        @SuppressWarnings("SpellCheckingInspection") String expectedOutput = "   fooar";
+        //noinspection SpellCheckingInspection
+        String expectedOutput = "   fooar";
         // Act
         String actualOutput = stringRow.placeSubstringAlignLeft(input2, position2).placeSubstringAlignLeft(input1,
                 position1).toString();
@@ -157,7 +160,8 @@ public class StringBoxTest {
         int position1 = 7;
         String input2 = "bar";
         int position2 = 5;
-        @SuppressWarnings("SpellCheckingInspection") String expectedOutput = "     bafoo";
+        //noinspection SpellCheckingInspection
+        String expectedOutput = "     bafoo";
         // Act
         String actualOutput = stringRow.placeSubstringAlignLeft(input2, position2).placeSubstringAlignLeft(input1,
                 position1).toString();
@@ -189,6 +193,30 @@ public class StringBoxTest {
         assertEquals(expectedOutput, actualOutput);
     }
 
+    @Test
+    public void testCenterOddNumberOfCharacters() {
+        // Arrange
+        String input = "foo";
+        int position = 5;
+        String expectedOutput = "    foo";
+        // Act
+        String actualOutput = stringRow.placeSubstring(input, StringBox.Horizontal.CENTER, position).toString();
+        // Assert
+        assertEquals(expectedOutput, actualOutput);
+    }
+
+    @Test
+    public void testCenterEvenNumberOfCharacter() {
+        // Arrange
+        String input = "foobar";
+        int position = 5;
+        String expectedOutput = "  foobar";
+        // Act
+        String actualOutput = stringRow.placeSubstring(input, StringBox.Horizontal.CENTER, position).toString();
+        // Assert
+        assertEquals(expectedOutput, actualOutput);
+    }
+
     /* Tests for multiple rows */
 
     @Test
@@ -207,13 +235,14 @@ public class StringBoxTest {
     @Test
     public void testNormalCases() {
         // Arrange
-        @SuppressWarnings("SpellCheckingInspection") String[] inputs = {
+        //noinspection SpellCheckingInspection
+        String[] inputs = {
                 String.format("foo%nbar"),
                 String.format("larry%ncurly%nmoe"),
                 String.format("quux%nxyzzy"),
                 String.format("one%ntwo%nthree")
         };
-        int[] rows = {2, 5, 3, 10};
+        int[] rows = {2, 6, 3, 11};
         int[] columns = {10, 5, 20, 15};
         //noinspection SpellCheckingInspection
         String expectedOutput = String.format(
@@ -298,4 +327,32 @@ public class StringBoxTest {
                     "Dimensions 0×3 are too small.", exception.getMessage());
         }
     }
+
+    @Test
+    public void testCenterOddNumberOfLines() {
+        // Arrange
+        String input = String.format("f%no%no");
+        int row = 5;
+        int column = 0;
+        String expectedOutput = String.format("%n%n%n%nf%no%no");
+        // Act
+        String actualOutput = stringBox.placeString(input, StringBox.Vertical.CENTER, row,
+                StringBox.Horizontal.CENTER, column).toString(false);
+        // Assert
+        assertEquals(expectedOutput, actualOutput);
+    }
+
+    @Test
+    public void testCenterEvenNumberOfLines() {
+        // Arrange
+        String input = String.format("f%no%no%nb%na%nr");
+        int row = 5;
+        int column = 0;
+        String expectedOutput = String.format("%n%nf%no%no%nb%na%nr");
+        // Act
+        String actualOutput = stringBox.placeString(input, StringBox.Vertical.CENTER, row,
+                StringBox.Horizontal.CENTER, column).toString(false);
+        // Assert
+        assertEquals(expectedOutput, actualOutput);
+    }
 }
\ No newline at end of file
-- 
GitLab