Correct double-quoted string whitespace trimming 99/67899/3
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 4 Feb 2018 15:31:11 +0000 (16:31 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 4 Feb 2018 22:59:55 +0000 (23:59 +0100)
We need to trim leading and trailing whitespace when it comes from
YANG text, so that the resulting string matches expectations.

This patch adds the required trimming.

JIRA: YANGTOOLS-845
Change-Id: I21777583c5d18819e68c089cd9f2cf51179b0530
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java [new file with mode: 0644]

index 586299281f9329b1da4d627e13c446e25b02d88b..9d3f9a5f3fd62f62df90841041328fb8d3457f7c 100644 (file)
@@ -63,6 +63,6 @@ public class AnnotationTest {
         assertEquals(BaseTypes.stringType(), annotation.getType());
         assertEquals(Optional.empty(), annotation.getReference());
         assertEquals(Optional.of("This annotation contains the date and time when the\n"
-                + "           annotated instance was last modified (or created)."), annotation.getDescription());
+                + "annotated instance was last modified (or created)."), annotation.getDescription());
     }
 }
index 0a78be963bcbfa33adae468f3ca8c8d07e687166..b89661f0d9a265e104e4e50934f38c4f934d7439 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.CharMatcher;
 import java.util.Collections;
 import java.util.List;
@@ -18,6 +19,7 @@ import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
 final class ArgumentContextUtils {
+    private static final CharMatcher WHITESPACE_MATCHER = CharMatcher.whitespace();
     private static final CharMatcher ANYQUOTE_MATCHER = CharMatcher.anyOf("'\"");
     private static final Pattern ESCAPED_DQUOT = Pattern.compile("\\\"", Pattern.LITERAL);
     private static final Pattern ESCAPED_BACKSLASH = Pattern.compile("\\\\", Pattern.LITERAL);
@@ -50,10 +52,13 @@ final class ArgumentContextUtils {
                  * in the inner string and trim the result.
                  */
                 checkDoubleQuotedString(innerStr, yangVersion, ref);
+
                 sb.append(ESCAPED_TAB.matcher(
                     ESCAPED_LF.matcher(
                         ESCAPED_BACKSLASH.matcher(
-                            ESCAPED_DQUOT.matcher(innerStr).replaceAll("\\\""))
+                            ESCAPED_DQUOT.matcher(
+                                trimWhitespace(innerStr, stringNode.getSymbol().getCharPositionInLine()))
+                            .replaceAll("\\\""))
                         .replaceAll("\\\\"))
                     .replaceAll("\\\n"))
                     .replaceAll("\\\t"));
@@ -100,4 +105,81 @@ final class ArgumentContextUtils {
             }
         }
     }
+
+    @VisibleForTesting
+    static String trimWhitespace(final String str, final int dquot) {
+        int brk = str.indexOf('\n');
+        if (brk == -1) {
+            // No need to trim whitespace
+            return str;
+        }
+
+        // Okay, we may need to do some trimming, set up a builder and append the first segment
+        final int length = str.length();
+        final StringBuilder sb = new StringBuilder(length);
+
+        // Append first segment, which needs only tail-trimming
+        sb.append(str, 0, trimTrailing(str, 0, brk)).append('\n');
+
+        // With that out of the way, setup our iteration state. The string segment we are looking at is
+        // str.substring(start, end), which is guaranteed not to include any line breaks, i.e. end <= brk unless we are
+        // at the last segment.
+        int start = brk + 1;
+        brk = str.indexOf('\n', start);
+
+        // Loop over inner strings
+        while (brk != -1) {
+            final int end = brk != -1 ? brk : length;
+            trimLeadingAndAppend(sb, dquot, str, start, trimTrailing(str, start, end)).append('\n');
+            start = end + 1;
+            brk = str.indexOf('\n', start);
+        }
+
+        return trimLeadingAndAppend(sb, dquot, str, start, length).toString();
+    }
+
+    private static StringBuilder trimLeadingAndAppend(final StringBuilder sb, final int dquot, final String str,
+            final int start, final int end) {
+        int offset = start;
+        int pos = 0;
+
+        while (pos <= dquot) {
+            if (offset == end) {
+                // We ran out of data, nothing to append
+                return sb;
+            }
+
+            final char ch = str.charAt(offset);
+            if (ch == '\t') {
+                // tabs are to be treated as 8 spaces
+                pos += 8;
+            } else if (WHITESPACE_MATCHER.matches(ch)) {
+                pos++;
+            } else {
+                break;
+            }
+
+            offset++;
+        }
+
+        // We have expanded beyond double quotes, push equivalent spaces
+        while (pos - 1 > dquot) {
+            sb.append(' ');
+            pos--;
+        }
+
+        return sb.append(str, offset, end);
+    }
+
+    private static int trimTrailing(final String str, final int start, final int end) {
+        int ret = end;
+        while (ret > start) {
+            final int prev = ret - 1;
+            if (!WHITESPACE_MATCHER.matches(str.charAt(prev))) {
+                break;
+            }
+            ret = prev;
+        }
+        return ret;
+    }
 }
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java
new file mode 100644 (file)
index 0000000..381e57d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.ArgumentContextUtils.trimWhitespace;
+
+import org.junit.Test;
+
+public class AugmentContextUtilsTest {
+
+    @Test
+    public void testTrimWhitespace() {
+        assertEquals("\n", trimWhitespace("\n", 0));
+        assertEquals("\n", trimWhitespace("\n", 5));
+        assertEquals("\n\n\n\n", trimWhitespace("\n\n\n\n", 0));
+        assertEquals("\n\n\n\n", trimWhitespace("\n\n\n\n", 5));
+        assertEquals("abc\n\n", trimWhitespace("abc \n  \n", 0));
+        assertEquals("abc\n\n", trimWhitespace("abc \n  \n", 1));
+        assertEquals("abc\n  ", trimWhitespace("abc\n   ", 0));
+        assertEquals("abc\n", trimWhitespace("abc\n   ", 2));
+        assertEquals("abc\n\n", trimWhitespace("abc\n   \n", 2));
+        assertEquals("abc\n        ", trimWhitespace("abc\n\t ", 0));
+        assertEquals("abc\n      ", trimWhitespace("abc\n\t ", 2));
+        assertEquals("abc\n    ", trimWhitespace("abc\n\t ", 4));
+        assertEquals("abc\n    ", trimWhitespace("abc\n \t", 4));
+        assertEquals("abc\n   a\n    a\n", trimWhitespace("abc\n\ta\n\t a\n", 4));
+        assertEquals("abc\n\n    a\n", trimWhitespace("abc\n\t\n\t a\n", 4));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n  ", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n   ", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n    ", 3));
+        assertEquals("   \ta\n ", trimWhitespace("   \ta\n     ", 3));
+    }
+}