Repository: decorators-squad/eo-yaml
Pull request #550 (2023-02-06 16:33:34)

#547 regex updates
PR for #547 

Updated regexes to take into account escaped keys (they may contain ``:`` which is misread as YAML symbol);
Added names for the regex groups in ReadYamlScalar, instead of indexes. 
Added unit test to make sure the case of #547 is now valid.


Patch:
diff --git a/README.md b/README.md
index 92e3f9f..ef8184a 100644
--- a/README.md
+++ b/README.md
@@ -23,16 +23,16 @@ To get the latest release from Maven Central, simply add the following to your `
 <dependency>
     <groupId>com.amihaiemil.web</groupId>
     <artifactId>eo-yaml</artifactId>
-    <version>7.0.2</version>
+    <version>7.0.1</version>
 </dependency>
 ```
 
-or download the <a href="https://oss.sonatype.org/service/local/repositories/releases/content/com/amihaiemil/web/eo-yaml/7.0.2/eo-yaml-7.0.2-jar-with-dependencies.jar">fat</a> jar.
+or download the <a href="https://oss.sonatype.org/service/local/repositories/releases/content/com/amihaiemil/web/eo-yaml/7.0.1/eo-yaml-7.0.1-jar-with-dependencies.jar">fat</a> jar.
 
 If you use Gradle, add this to your dependencies:
 
 ```gradle
-implementation group: 'com.amihaiemil.web', name: 'eo-yaml', version: '7.0.2'
+implementation group: 'com.amihaiemil.web', name: 'eo-yaml', version: '7.0.1'
 ```
 
 The releases are also available on [Github Packages](https://github.com/decorators-squad/eo-yaml/packages)!
diff --git a/pom.xml b/pom.xml
index 5fd29e1..30e99cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.amihaiemil.web</groupId>
     <artifactId>eo-yaml</artifactId>
-    <version>7.0.3-SNAPSHOT</version><!--rrv-sed-flag-->
+    <version>7.0.2-SNAPSHOT</version><!--rrv-sed-flag-->
     <packaging>jar</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java b/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
index c686645..1e0c87b 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadPlainScalar.java
@@ -44,39 +44,45 @@ final class ReadPlainScalar extends BaseScalar {
      * Ignore zero or more spaces and a hyphen (-) followed by one
      * or more spaces.
      *
-     * A quoted scalar literal is inside:
+     * A quoted scalar (<b>EscapedScalar</b>) literal is inside:
      *  - ('(?:[^'\\]|\\.)*') : a single (') quoted string or
      *  - ("(?:[^"\\]|\\.)*") : double (") quoted string
      *
-     * A scalar for a mapping are characters after:
-     *  - .*:[ ]+(.*) : Any characters before a colon followed by
+     * A scalar for a mapping (<b>MappingScalar</b>) are characters after
+     * the last unescaped ':':
+     *  - (keys_regex):[ ]+(.*) : Any characters before a colon followed by
      *    one or more spaces.
      *
-     * The sequence scalar is:
+     * The sequence scalar (<b>UnescapedSequenceScalar</b>) is:
      *   - -[ ]+(.*) : Any characters after a hyphen (-) and one more spaces.
      */
     private static final Pattern QUOTED_LITERAL_MAP_SEQ = Pattern.compile("^("
-            + "[ ]*(-[ ]+)"
-                + "(('(?:[^'\\\\]|\\\\.)*')|"
-                + "(\"(?:[^\"\\\\]|\\\\.)*\"))|"
-            + "(.*:[ ]+(.*))|"
-            + "(-[ ]+(.*))"
-            + ")$");
+        + "[ ]*(-[ ]+)"
+        + "(?<EscapedScalar>"
+            + "('(?:[^'\\\\]|\\\\.)*')|"
+            + "(\"(?:[^\"\\\\]|\\\\.)*\")"
+        + ")|"
+        + "(((?<key>[^:'\"]+)|(?<keyQ>\".+\")|(?<keySQ>'.+'))*:[ ]+"
+        + "(?<MappingScalar>.*))|"
+        + "(-[ ]+(?<UnescapedSequenceScalar>.*))"
+        + ")$"
+    );
 
     /**
-     * Regex group index that matches quoted literals.
+     * Name of the regex group for escaped scalars (between "" or '').
      */
-    private static final int QUOTED_LITERAL_GROUP = 3;
+    private static final String ESCAPED_SCALAR = "EscapedScalar";
 
     /**
-     * Regex group index that matches scalar values of mappings.
+     * Name of the regex group for scalars in a mapping.
      */
-    private static final int MAPPING_GROUP = 7;
+    private static final String MAPPING_SCALAR = "MappingScalar";
 
     /**
-     * Regex group index that matches scalar value of non-quotes sequences.
+     * Name of the regex group for unescaped scalars in a sequence.
      */
-    private static final int SEQUENCE_GROUP = 9;
+    private static final String UNESCAPED_SEQUENCE_SCALAR =
+        "UnescapedSequenceScalar";
 
     /**
      * All YAML Lines of the document.
@@ -114,12 +120,12 @@ final class ReadPlainScalar extends BaseScalar {
         String value = this.scalar.trimmed();
         Matcher matcher = this.escapedSequenceScalar(this.scalar);
         if(matcher.matches()) {
-            if (matcher.group(QUOTED_LITERAL_GROUP) != null) {
-                value = matcher.group(QUOTED_LITERAL_GROUP);
-            } else if (matcher.group(MAPPING_GROUP) != null) {
-                value = matcher.group(MAPPING_GROUP).trim();
-            } else if (matcher.group(SEQUENCE_GROUP) != null) {
-                value = matcher.group(SEQUENCE_GROUP).trim();
+            if (matcher.group(ESCAPED_SCALAR) != null) {
+                value = matcher.group(ESCAPED_SCALAR);
+            } else if (matcher.group(MAPPING_SCALAR) != null) {
+                value = matcher.group(MAPPING_SCALAR).trim();
+            } else if (matcher.group(UNESCAPED_SEQUENCE_SCALAR) != null) {
+                value = matcher.group(UNESCAPED_SEQUENCE_SCALAR).trim();
             }
         }
         if("null".equals(value)) {
diff --git a/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java b/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
index 5813b98..6916e81 100644
--- a/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
+++ b/src/main/java/com/amihaiemil/eoyaml/ReadYamlMapping.java
@@ -52,7 +52,7 @@ final class ReadYamlMapping extends BaseYamlMapping {
      * Regex for a key in a mapping.
      */
     private static final Pattern KEY_PATTERN = Pattern.compile(
-        "^-?\\s*(?<key>.+):(|\\s.*)$"
+        "^-?\\s*((?<key>[^:'\"]+)|(?<keyQ>\".+\")|(?<keySQ>'.+')):(|\\s.*)$"
     );
 
     /**
@@ -145,9 +145,20 @@ final class ReadYamlMapping extends BaseYamlMapping {
                 }
                 final Matcher matcher = KEY_PATTERN.matcher(trimmed);
                 if (matcher.matches()) {
+                    //@checkstyle NestedIfDepth (50 lines)
                     final String key = matcher.group("key");
-                    if (!key.isEmpty()) {
+                    if (key != null && !key.isEmpty()) {
                         keys.add(new PlainStringScalar(key));
+                    } else {
+                        final String keyQ = matcher.group("keyQ");
+                        if (keyQ != null && !keyQ.isEmpty()) {
+                            keys.add(new PlainStringScalar(keyQ));
+                        } else {
+                            final String keySQ = matcher.group("keySQ");
+                            if (keySQ != null && !keySQ.isEmpty()) {
+                                keys.add(new PlainStringScalar(keySQ));
+                            }
+                        }
                     }
                 }
             }
@@ -226,13 +237,13 @@ final class ReadYamlMapping extends BaseYamlMapping {
                     || trimmed.matches("^" + Pattern.quote(relaxedKey) + ":[ ]*\\|[+-]?$")
                 ) {
                     value = this.significant.toYamlNode(line);
-                } else if (trimmed.matches(relaxedKey + ":[ ]*\\{}")) {
+                } else if(trimmed.matches(Pattern.quote(relaxedKey) + ":[ ]*\\{}")) {
                     value = new EmptyYamlMapping(new ReadYamlMapping(
                         line.number(),
                         this.all.line(line.number()),
                         this.all
                     ));
-                } else if (trimmed.matches(relaxedKey + ":[ ]*\\[]")) {
+                } else if(trimmed.matches(Pattern.quote(relaxedKey) + ":[ ]*\\[]")) {
                     value = new EmptyYamlSequence(new ReadYamlSequence(
                             this.all.line(line.number()),
                             this.all


Test patch:
diff --git a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
index 349512f..0a69aad 100644
--- a/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
+++ b/src/test/java/com/amihaiemil/eoyaml/RtYamlInputTest.java
@@ -1235,6 +1235,35 @@ public final class RtYamlInputTest {
         );
     }
 
+    /**
+     * Unit test for issue 547.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void shouldReadEscapedScalarsProperly()
+        throws IOException {
+        final String filename = "issue_547_escaped_special_chars.yml";
+
+        final YamlMapping read = new RtYamlInput(
+            new FileInputStream("src/test/resources/" + filename)
+        ).readYamlMapping();
+
+        System.out.println(read);
+
+        final YamlMapping filter = read.yamlMapping("filter");
+        MatcherAssert.assertThat(
+            filter.string("input"),
+            Matchers.equalTo("${ {vegetables: .vegetables} }")
+        );
+        //@checkstyle LineLength (5 lines)
+        MatcherAssert.assertThat(
+            filter.string("output"),
+            Matchers.equalTo(
+                "${ {vegetables: [.vegetables[] | select(.veggieLike == true)]} }"
+            )
+        );
+    }
+
     /**
      * Read a test resource file's contents.
      * @param fileName File to read.
diff --git a/src/test/resources/issue_547_escaped_special_chars.yml b/src/test/resources/issue_547_escaped_special_chars.yml
new file mode 100644
index 0000000..9c34611
--- /dev/null
+++ b/src/test/resources/issue_547_escaped_special_chars.yml
@@ -0,0 +1,3 @@
+filter:
+  input: "${ {vegetables: .vegetables} }"
+  output: "${ {vegetables: [.vegetables[] | select(.veggieLike == true)]} }"
\ No newline at end of file