From 9c30a1eb4eb56702b679b23d08a13caa9139ec86 Mon Sep 17 00:00:00 2001 From: Robin Stocker Date: Wed, 10 Feb 2016 00:24:06 +1100 Subject: [PATCH] Fix unexpected link end with unfinished delimiter pairs (#5) This: http://example.org/"_(foo) Was extracted like this: http://example.org/"_(foo The problem was that all delimiter pairs were checked in one go, instead of just when the delimiter actually occurred. This also changes behavior for unbalanced brackets: A lone closing bracket immediately terminates a link now. --- .../nibor/autolink/internal/UrlScanner.java | 40 +++++++++++++++---- .../org/nibor/autolink/AutolinkUrlTest.java | 17 +++++++- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/nibor/autolink/internal/UrlScanner.java b/src/main/java/org/nibor/autolink/internal/UrlScanner.java index 0dd2ef4..f409dcd 100644 --- a/src/main/java/org/nibor/autolink/internal/UrlScanner.java +++ b/src/main/java/org/nibor/autolink/internal/UrlScanner.java @@ -77,11 +77,11 @@ private int findLast(CharSequence input, int beginIndex) { case ':': case ';': // These may be part of an URL but not at the end - continue loop; + break; case '/': // This may be part of an URL and at the end, but not if the previous character can't be the end of an URL - if (last != i - 1) { - continue loop; + if (last == i - 1) { + last = i; } break; case '(': @@ -89,37 +89,63 @@ private int findLast(CharSequence input, int beginIndex) { break; case ')': round--; + if (round >= 0) { + last = i; + } else { + // More closing than opening brackets, stop now + break loop; + } break; case '[': square++; break; case ']': square--; + if (square >= 0) { + last = i; + } else { + // More closing than opening brackets, stop now + break loop; + } break; case '{': curly++; break; case '}': curly--; + if (curly >= 0) { + last = i; + } else { + // More closing than opening brackets, stop now + break loop; + } break; case '<': angle++; break; case '>': angle--; + if (angle >= 0) { + last = i; + } else { + // More closing than opening brackets, stop now + break loop; + } break; case '"': doubleQuote = !doubleQuote; + if (!doubleQuote) { + last = i; + } break; case '\'': singleQuote = !singleQuote; + if (!singleQuote) { + last = i; + } break; default: last = i; - continue loop; - } - if (round >= 0 && square >= 0 && curly >= 0 && angle >= 0 && !doubleQuote && !singleQuote) { - last = i; } } return last; diff --git a/src/test/java/org/nibor/autolink/AutolinkUrlTest.java b/src/test/java/org/nibor/autolink/AutolinkUrlTest.java index 62e808f..6d5f527 100644 --- a/src/test/java/org/nibor/autolink/AutolinkUrlTest.java +++ b/src/test/java/org/nibor/autolink/AutolinkUrlTest.java @@ -113,11 +113,23 @@ public void matchingPunctuationTricky() { assertLinked("(http://example.org/).", "(|http://example.org/|)."); assertLinked("(http://example.org/.)", "(|http://example.org/|.)"); assertLinked("http://example.org/>", "|http://example.org/|>"); - // not sure about these: - assertLinked("http://example.org/(", "|http://example.org/(|"); + // not sure about these + assertLinked("http://example.org/(", "|http://example.org/|("); + assertLinked("http://example.org/(.", "|http://example.org/|(."); assertLinked("http://example.org/]()", "|http://example.org/|]()"); } + @Test + public void quotes() { + assertLinked("http://example.org/\"_(foo)", "|http://example.org/\"_(foo)|"); + assertLinked("http://example.org/\"_(foo)\"", "|http://example.org/\"_(foo)\"|"); + assertLinked("http://example.org/\"\"", "|http://example.org/\"\"|"); + assertLinked("http://example.org/\"\"\"", "|http://example.org/\"\"|\""); + assertLinked("http://example.org/\".", "|http://example.org/|\"."); + assertLinked("http://example.org/\"a", "|http://example.org/\"a|"); + assertLinked("http://example.org/it's", "|http://example.org/it's|"); + } + @Test public void html() { assertLinked("http://example.org\">", "|http://example.org|\">"); @@ -143,6 +155,7 @@ public void slash() { public void multiple() { assertLinked("http://one.org/ http://two.org/", "|http://one.org/| |http://two.org/|"); assertLinked("http://one.org/ : http://two.org/", "|http://one.org/| : |http://two.org/|"); + assertLinked("(http://one.org/)(http://two.org/)", "(|http://one.org/|)(|http://two.org/|)"); } @Test