From 91d578aa2df9fcab996117ce6637e593ebac86f9 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Fri, 14 Apr 2023 13:09:56 +0200
Subject: [PATCH] EHN: throw error when the users uses white spaces as a part
 of numbers

---
 .../caosdb/server/jobs/core/ExecuteQuery.java |  2 +-
 .../java/org/caosdb/server/query/CQLParser.g4 |  2 +-
 .../java/org/caosdb/server/query/POV.java     | 27 ++++++++++++++++---
 .../caosdb/server/utils/ServerMessages.java   |  4 +++
 .../java/org/caosdb/server/query/POVTest.java |  1 +
 .../java/org/caosdb/server/query/TestCQL.java | 13 ++++++++-
 6 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java b/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
index 352a7428..5c9f4c2c 100644
--- a/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
+++ b/src/main/java/org/caosdb/server/jobs/core/ExecuteQuery.java
@@ -46,7 +46,7 @@ public class ExecuteQuery extends FlagJob {
       try {
         queryInstance.execute(getTransaction().getAccess());
       } catch (final ParsingException e) {
-        getContainer().addError(ServerMessages.QUERY_PARSING_ERROR);
+        getContainer().addError(ServerMessages.QUERY_PARSING_ERROR(e.getMessage()));
       } catch (final UnsupportedOperationException e) {
         getContainer().addError(ServerMessages.QUERY_EXCEPTION);
         getContainer()
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index f4d4e50d..eb43e17b 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -610,7 +610,7 @@ value returns [String str]
  */
 number_with_unit
 :
-    ( UNSIGNED_INT | DECIMAL_NUMBER )
+    ( UNSIGNED_INT | DECIMAL_NUMBER | HYPHEN WHITE_SPACE? UNSIGNED_INT)
     (WHITE_SPACE? unit)?
 ;
 
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index 7fa1f590..bdbda62e 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -52,7 +52,8 @@ import org.slf4j.LoggerFactory;
 
 public class POV implements EntityFilterInterface {
   public static final Pattern NUMBER_PATTERN =
-      Pattern.compile("^((?:-\\s*)?[0-9]+(?:\\.[0-9]+)?(?:\\s*[eE]\\s*[+-]?[0-9]+)?)\\s*([^-]*)$");
+      Pattern.compile(
+          "^((?:-\\s*)?[0-9]+(?:\\.[0-9]+)?(?:\\s*[eE]\\s*[+-]?\\s*[0-9]+)?)\\s*([^-]*)$");
   private SubProperty subp = null;
   public static int retry = 10;
   private int retry_count = 0;
@@ -118,12 +119,21 @@ public class POV implements EntityFilterInterface {
 
       // try and parse as integer
       try {
-        final Matcher m = NUMBER_PATTERN.matcher(value);
+        final Matcher m = NUMBER_PATTERN.matcher(this.value);
         if (!m.matches()) {
           throw new NumberFormatException();
         }
         final String vIntStr = m.group(1);
         this.vInt = Integer.parseInt(vIntStr.replaceAll("\\s", ""));
+        if (vIntStr.matches(".*\\s.*")) {
+          // empty space in scientific notation is a common typo
+          throw new Query.ParsingException(
+              "You typed \""
+                  + vIntStr
+                  + "\". Empty spaces are not allowed in numbers. Did you mean \""
+                  + vIntStr.replaceAll("\\s", "")
+                  + "\"?");
+        }
         this.unitStr = m.group(2);
       } catch (final NumberFormatException e) {
         this.vInt = null;
@@ -133,13 +143,22 @@ public class POV implements EntityFilterInterface {
       if (this.vInt == null) {
         try {
           // Doubles are allowed without dots, for example when the integer overflows.
-          final Matcher m = NUMBER_PATTERN.matcher(value);
+          final Matcher m = NUMBER_PATTERN.matcher(this.value);
           if (!m.matches()) {
             throw new NumberFormatException();
           }
           final String vDoubleStr = m.group(1);
 
           this.vDouble = Double.parseDouble(vDoubleStr.replaceAll("\\s", ""));
+          if (vDoubleStr.matches(".*\\s.*")) {
+            // empty space in scientific notation is a common typo
+            throw new Query.ParsingException(
+                "You typed \""
+                    + vDoubleStr
+                    + "\". Empty spaces are not allowed in numbers. Did you mean \""
+                    + vDoubleStr.replaceAll("\\s", "")
+                    + "\"?");
+          }
           this.unitStr = m.group(2);
         } catch (final NumberFormatException e) {
           this.vDouble = null;
@@ -153,7 +172,7 @@ public class POV implements EntityFilterInterface {
           this.unit = getUnit(this.unitStr);
         } catch (final ParserException e) {
           e.printStackTrace();
-          throw new UnsupportedOperationException("Could not parse the unit.");
+          throw new UnsupportedOperationException("Could not parse the unit:");
         }
 
         this.stdUnitSig = this.unit.normalize().getSignature();
diff --git a/src/main/java/org/caosdb/server/utils/ServerMessages.java b/src/main/java/org/caosdb/server/utils/ServerMessages.java
index 835087ce..9e18ca90 100644
--- a/src/main/java/org/caosdb/server/utils/ServerMessages.java
+++ b/src/main/java/org/caosdb/server/utils/ServerMessages.java
@@ -617,4 +617,8 @@ public class ServerMessages {
     return new Message(
         MessageType.Error, MessageCode.MESSAGE_CODE_UNKNOWN, "You cannot delete yourself.");
   }
+
+  public static Message QUERY_PARSING_ERROR(String message) {
+    return new Message(MessageType.Error, MessageCode.MESSAGE_CODE_QUERY_PARSING_ERROR, message);
+  }
 }
diff --git a/src/test/java/org/caosdb/server/query/POVTest.java b/src/test/java/org/caosdb/server/query/POVTest.java
index def9cc65..ac7fcc9b 100644
--- a/src/test/java/org/caosdb/server/query/POVTest.java
+++ b/src/test/java/org/caosdb/server/query/POVTest.java
@@ -52,6 +52,7 @@ class POVTest {
         "-1.2",
         "2e-323",
         "2E-323",
+        "2E- 323",
         "2 e -323"
       })
   void testNumberPatternMatchDouble(String doubleValue) {
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index 0c6307a5..50f96f6f 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -7028,7 +7029,6 @@ public class TestCQL {
         "2E-323",
         "-123",
         "-1e23",
-        "2 e -23",
         "3E15m^2",
         "-3e15m",
         "-3e15 1/s",
@@ -7053,4 +7053,15 @@ public class TestCQL {
     assertEquals("POV(pname,=," + scientific_notation + ")", pov.toString());
     assertTrue(pov.getVDouble() != null || pov.getVInt() != null);
   }
+
+  @ParameterizedTest
+  @ValueSource(strings = {"- 123", "- 1e23", "2 e -23", "2E- 323"})
+  public void testIssue144WhiteSpaceInNumber(String number) {
+    CQLLexer lexer;
+    lexer = new CQLLexer(CharStreams.fromString(this.issue144 + number));
+    final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+    final CQLParser parser = new CQLParser(tokens);
+    assertThrowsExactly(Query.ParsingException.class, () -> parser.cq());
+  }
 }
-- 
GitLab