From: Robert Varga Date: Sun, 4 Feb 2018 15:31:11 +0000 (+0100) Subject: Correct double-quoted string whitespace trimming X-Git-Tag: v2.0.2~46 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F99%2F67899%2F3;p=yangtools.git Correct double-quoted string whitespace trimming 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 --- diff --git a/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java b/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java index 586299281f..9d3f9a5f3f 100644 --- a/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java +++ b/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java @@ -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()); } } diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java index 0a78be963b..b89661f0d9 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java @@ -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 index 0000000000..381e57d966 --- /dev/null +++ b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java @@ -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)); + } +}