From e7cb43c3e37e2cd890ba97be1e71676f7fc6c748 Mon Sep 17 00:00:00 2001 From: Brutus5000 Date: Mon, 8 Jun 2020 19:55:58 +0200 Subject: [PATCH] Add LibreOffice calc compatibility for boolean values fixes #159 --- .../java/com/poiji/config/DefaultCasting.java | 26 ++++++++- .../java/com/poiji/parser/BooleanParser.java | 34 +++++++++++ src/main/java/com/poiji/parser/Parsers.java | 3 + .../com/poiji/util/DefaultCastingTest.java | 26 ++++++++- .../DefaultCastingWithErrorLoggingTest.java | 56 ++++++++++++++++++- 5 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/poiji/parser/BooleanParser.java diff --git a/src/main/java/com/poiji/config/DefaultCasting.java b/src/main/java/com/poiji/config/DefaultCasting.java index 4ec74c0..2337133 100644 --- a/src/main/java/com/poiji/config/DefaultCasting.java +++ b/src/main/java/com/poiji/config/DefaultCasting.java @@ -1,6 +1,7 @@ package com.poiji.config; import com.poiji.option.PoijiOptions; +import com.poiji.parser.BooleanParser; import com.poiji.parser.Parsers; import java.math.BigDecimal; @@ -43,6 +44,22 @@ private void logError(String value, Object defaultValue, String sheetName, int r } } + private Boolean primitiveBooleanValue(String value, String sheetName, int row, int col) { + try { + return Parsers.booleans().parse(value); + } catch (BooleanParser.BooleanParseException bpe) { + return onError(value, sheetName, row, col, bpe, false); + } + } + + private Boolean booleanValue(String value, String sheetName, int row, int col, PoijiOptions options) { + try { + return Parsers.booleans().parse(value); + } catch (BooleanParser.BooleanParseException bpe) { + return onError(value, sheetName, row, col, bpe, options.preferNullOverDefault() ? null : false); + } + } + private int primitiveIntegerValue(String value, String sheetName, int row, int col) { try { return Parsers.integers().parse(value).intValue(); @@ -216,15 +233,18 @@ public Object castValue(Class fieldType, String rawValue, int row, int col, P } else if (fieldType == Double.class) { o = doubleValue(value, sheetName, row, col, options); + } else if (fieldType == boolean.class) { + o = primitiveBooleanValue(value, sheetName, row, col); + + } else if (fieldType == Boolean.class) { + o = booleanValue(value, sheetName, row, col, options); + } else if (fieldType == float.class) { o = primitiveFloatValue(value, sheetName, row, col); } else if (fieldType == Float.class) { o = floatValue(value, sheetName, row, col, options); - } else if (fieldType == boolean.class || fieldType == Boolean.class) { - o = Boolean.valueOf(value); - } else if (fieldType == Date.class) { o = dateValue(value, sheetName, row, col, options); diff --git a/src/main/java/com/poiji/parser/BooleanParser.java b/src/main/java/com/poiji/parser/BooleanParser.java new file mode 100644 index 0000000..e94b65c --- /dev/null +++ b/src/main/java/com/poiji/parser/BooleanParser.java @@ -0,0 +1,34 @@ +package com.poiji.parser; + +public class BooleanParser implements Parser { + @Override + public Boolean parse(String value) { + if ("true".equalsIgnoreCase(value.trim())) { + return true; + } + + if ("false".equalsIgnoreCase(value.trim())) { + return false; + } + + /* LibreOffice compatibility: + * + * LibreOffice booleans are stored as formula =TRUE() or =FALSE() which is parsed by POI to 1 or 0. + */ + if ("1".equals(value.trim())) { + return true; + } + + if ("0".equals(value.trim())) { + return false; + } + + throw new BooleanParseException(value); + } + + public static class BooleanParseException extends RuntimeException { + public BooleanParseException(String value) { + super("Can't parse value to Boolean: " + value); + } + } +} diff --git a/src/main/java/com/poiji/parser/Parsers.java b/src/main/java/com/poiji/parser/Parsers.java index 7a06497..88205ed 100644 --- a/src/main/java/com/poiji/parser/Parsers.java +++ b/src/main/java/com/poiji/parser/Parsers.java @@ -29,4 +29,7 @@ public static NumberParser numbers() { return new NumberParser(NumberFormat.getInstance()); } + public static BooleanParser booleans() { + return new BooleanParser(); + } } diff --git a/src/test/java/com/poiji/util/DefaultCastingTest.java b/src/test/java/com/poiji/util/DefaultCastingTest.java index 2f49124..6786031 100644 --- a/src/test/java/com/poiji/util/DefaultCastingTest.java +++ b/src/test/java/com/poiji/util/DefaultCastingTest.java @@ -100,13 +100,37 @@ public void castTextException() { } @Test - public void castBoolean() { + public void castBooleanTrue() { Boolean testVal = (Boolean) casting.castValue(boolean.class, "True", options); assertEquals(true, testVal); } + @Test + public void castBooleanLibreOfficeTrue() { + + Boolean testVal = (Boolean) casting.castValue(boolean.class, "1 ", options); + + assertEquals(true, testVal); + } + + @Test + public void castBooleanFalse() { + + Boolean testVal = (Boolean) casting.castValue(boolean.class, " False", options); + + assertEquals(false, testVal); + } + + @Test + public void castBooleanLibreOfficeFalse() { + + Boolean testVal = (Boolean) casting.castValue(boolean.class, "0", options); + + assertEquals(false, testVal); + } + @Test public void castFloat() { diff --git a/src/test/java/com/poiji/util/DefaultCastingWithErrorLoggingTest.java b/src/test/java/com/poiji/util/DefaultCastingWithErrorLoggingTest.java index 2aafa44..f0c93bf 100644 --- a/src/test/java/com/poiji/util/DefaultCastingWithErrorLoggingTest.java +++ b/src/test/java/com/poiji/util/DefaultCastingWithErrorLoggingTest.java @@ -4,13 +4,13 @@ import com.poiji.config.DefaultCastingError; import com.poiji.option.PoijiOptions; import com.poiji.option.PoijiOptions.PoijiOptionsBuilder; +import com.poiji.parser.BooleanParser; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.math.BigDecimal; -import java.text.ParseException; import java.time.LocalDate; import java.time.format.DateTimeParseException; import java.util.Arrays; @@ -367,6 +367,56 @@ public void castDoubleNullExceptionWithLogging() { assertSingleCastingErrorPresent(sheetName, row, col, value, null, NumberFormatException.class); } + @Test + public void castPrimitiveBooleanExceptionWithLogging() { + + PoijiOptions options = PoijiOptionsBuilder.settings() + .sheetName(sheetName) + .build(); + + String value = "not an boolean"; + + Boolean expectedDefault = false; + + Boolean testVal = (Boolean) casting.castValue(boolean.class, value, row, col, options); + + assertEquals(expectedDefault, testVal); + assertSingleCastingErrorPresent(sheetName, row, col, value, expectedDefault, BooleanParser.BooleanParseException.class); + } + + @Test + public void castBooleanDefaultExceptionWithLogging() { + + PoijiOptions options = PoijiOptionsBuilder.settings() + .sheetName(sheetName) + .build(); + + String value = "not an Boolean"; + + Boolean expectedDefault = false; + + Boolean testVal = (Boolean) casting.castValue(Boolean.class, value, row, col, options); + + assertEquals(expectedDefault, testVal); + assertSingleCastingErrorPresent(sheetName, row, col, value, expectedDefault, BooleanParser.BooleanParseException.class); + } + + @Test + public void castBooleanNullExceptionWithLogging() { + + PoijiOptions options = PoijiOptionsBuilder.settings() + .sheetName(sheetName) + .preferNullOverDefault(true) + .build(); + + String value = "not a Boolean"; + + Boolean testVal = (Boolean) casting.castValue(Boolean.class, value, row, col, options); + + assertNull(testVal); + assertSingleCastingErrorPresent(sheetName, row, col, value, null, BooleanParser.BooleanParseException.class); + } + // Float @Test public void castPrimitiveFloatExceptionWithoutLogging() { @@ -551,7 +601,7 @@ public void castDateNullExceptionWithoutLogging() { Date testVal = (Date) casting.castValue(Date.class, value, options); assertNull(testVal); - assertSingleCastingErrorPresent(sheetName, EMPTY_ROW, EMPTY_COL, value, null, ParseException.class); + assertSingleCastingErrorPresent(sheetName, EMPTY_ROW, EMPTY_COL, value, null, java.text.ParseException.class); } @Test @@ -567,7 +617,7 @@ public void castDateNullExceptionWithLogging() { Date testVal = (Date) casting.castValue(Date.class, value, row, col, options); assertNull(testVal); - assertSingleCastingErrorPresent(sheetName, row, col, value, null, ParseException.class); + assertSingleCastingErrorPresent(sheetName, row, col, value, null, java.text.ParseException.class); } // LocalDate