Change parser to better recognize the end of links
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 15 Aug 2016 18:41:39 +0000 (20:41 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 15 Aug 2016 19:05:57 +0000 (21:05 +0200)
src/main/java/net/pterodactylus/sone/text/SoneTextParser.java
src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java

index fb4e7cd..b675c12 100644 (file)
@@ -175,9 +175,8 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                        }
                                        lineComplete = false;
 
-                                       Matcher matcher = whitespacePattern.matcher(line);
-                                       int nextSpace = matcher.find(0) ? matcher.start() : line.length();
-                                       String link = line.substring(0, nextSpace);
+                                       int endOfLink = findEndOfLink(line);
+                                       String link = line.substring(0, endOfLink);
                                        String name = link;
                                        logger.log(Level.FINER, String.format("Found link: %s", link));
 
@@ -271,7 +270,7 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                                }
                                                parts.add(new LinkPart(link, name));
                                        }
-                                       line = line.substring(nextSpace);
+                                       line = line.substring(endOfLink);
                                }
                                lastLineEmpty = false;
                        }
@@ -291,6 +290,29 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                return parts;
        }
 
+       private int findEndOfLink(String line) {
+               Matcher matcher = whitespacePattern.matcher(line);
+               if (!matcher.find(0)) {
+                       return line.length();
+               }
+               int nextWhitespace = matcher.start();
+               int openParens = 0;
+               for (int i = 0; i < nextWhitespace; i++) {
+                       switch (line.charAt(i)) {
+                               case '(':
+                                       openParens++;
+                                       break;
+                               case ')':
+                                       openParens--;
+                                       if (openParens < 0) {
+                                               return i;
+                                       }
+                               default:
+                       }
+               }
+               return nextWhitespace;
+       }
+
        private static class NextLink {
 
                private final int position;
index 200a773..8d83c95 100644 (file)
@@ -108,6 +108,21 @@ public class SoneTextParserTest {
                assertThat("Part Text", "Some text. Empty link: http:// – nice!", is(convertText(parts, PlainTextPart.class)));
        }
 
+       @Test
+       public void httpLinkWithoutParensEndsAtNextClosingParen() {
+               SoneTextParser soneTextParser = new SoneTextParser(null, null);
+               Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc) – nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", "Some text (and a link: [http://example.sone/abc|example.sone/abc|example.sone/abc]) – nice!", is(convertText(parts, PlainTextPart.class, LinkPart.class)));
+       }
+
+       @Test
+       public void httpLinkWithOpenedAndClosedParensEndsAtNextClosingParen() {
+               SoneTextParser soneTextParser = new SoneTextParser(null, null);
+               Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc_(def)) – nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", "Some text (and a link: [http://example.sone/abc_(def)|example.sone/abc_(def)|example.sone/abc_(def)]) – nice!", is(convertText(parts, PlainTextPart.class, LinkPart.class)));
+       }
 
        /**
         * Converts all given {@link Part}s into a string, validating that the