Clean up URI parsing code and tests
This commit is contained in:
parent
0d3d9aab32
commit
0f9bc4867a
8 changed files with 370 additions and 292 deletions
|
@ -5,9 +5,6 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Parses and "linkifies" bitcoin links.
|
||||
*/
|
||||
class BitcoinUriParser implements UriParser {
|
||||
private static final Pattern BITCOIN_URI_PATTERN =
|
||||
Pattern.compile("bitcoin:[1-9a-km-zA-HJ-NP-Z]{27,34}(\\?[a-zA-Z0-9$\\-_.+!*'(),%:@&=]*)?");
|
||||
|
@ -16,7 +13,6 @@ class BitcoinUriParser implements UriParser {
|
|||
public int linkifyUri(String text, int startPos, StringBuffer outputBuffer) {
|
||||
Matcher matcher = BITCOIN_URI_PATTERN.matcher(text);
|
||||
|
||||
// Skip not matching uris
|
||||
if (!matcher.find(startPos) || matcher.start() != startPos) {
|
||||
return startPos;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ import java.util.regex.Pattern;
|
|||
/**
|
||||
* Parses and "linkifies" http links.
|
||||
* <p>
|
||||
* This class is in parts inspired by OkHttp's HttpUrl (https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/HttpUrl.java),s
|
||||
* but leaving out much of the parsing part.
|
||||
* This class is in parts inspired by OkHttp's
|
||||
* <a href="https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/HttpUrl.java">HttpUrl</a>.
|
||||
* But much of the parsing parts have been left out.
|
||||
* </p>
|
||||
*/
|
||||
class HttpUriParser implements UriParser {
|
||||
// This string represent character group sub-delim as described in RFC 3986
|
||||
|
@ -19,11 +21,12 @@ class HttpUriParser implements UriParser {
|
|||
private static final Pattern IPv4_PATTERN =
|
||||
Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})(:(\\d{0,5}))?");
|
||||
|
||||
|
||||
@Override
|
||||
public int linkifyUri(String text, int startPos, StringBuffer outputBuffer) {
|
||||
int currentPos = startPos;
|
||||
|
||||
// Test scheme
|
||||
// Scheme
|
||||
String shortScheme = text.substring(currentPos, Math.min(currentPos + 7, text.length()));
|
||||
String longScheme = text.substring(currentPos, Math.min(currentPos + 8, text.length()));
|
||||
if (shortScheme.equalsIgnoreCase("https://")) {
|
||||
|
@ -33,20 +36,17 @@ class HttpUriParser implements UriParser {
|
|||
} else if (longScheme.equalsIgnoreCase("rtsp://")) {
|
||||
currentPos += "rtsp://".length();
|
||||
} else {
|
||||
// Unsupported scheme
|
||||
return startPos;
|
||||
}
|
||||
|
||||
// Test authority
|
||||
// Authority
|
||||
int authorityEnd = text.indexOf('/', currentPos);
|
||||
if (authorityEnd == -1) {
|
||||
authorityEnd = text.length();
|
||||
}
|
||||
|
||||
// Authority: Take a look at user info if available
|
||||
currentPos = matchUserInfoIfAvailable(text, currentPos, authorityEnd);
|
||||
|
||||
// Authority: Take a look at host
|
||||
if (!tryMatchDomainName(text, currentPos, authorityEnd) &&
|
||||
!tryMatchIpv4Address(text, currentPos, authorityEnd, true) &&
|
||||
!tryMatchIpv6Address(text, currentPos, authorityEnd)) {
|
||||
|
@ -54,24 +54,27 @@ class HttpUriParser implements UriParser {
|
|||
}
|
||||
currentPos = authorityEnd;
|
||||
|
||||
// Test path
|
||||
// Path
|
||||
if (currentPos < text.length() && text.charAt(currentPos) == '/') {
|
||||
currentPos = matchUnreservedPCTEncodedSubDelimClassesGreedy(text, currentPos + 1, "/:@");
|
||||
}
|
||||
|
||||
// Test for query
|
||||
// Query
|
||||
if (currentPos < text.length() && text.charAt(currentPos) == '?') {
|
||||
currentPos = matchUnreservedPCTEncodedSubDelimClassesGreedy(text, currentPos + 1, ":@/?");
|
||||
}
|
||||
|
||||
// Test for fragment.
|
||||
// Fragment
|
||||
if (currentPos < text.length() && text.charAt(currentPos) == '#') {
|
||||
currentPos = matchUnreservedPCTEncodedSubDelimClassesGreedy(text, currentPos + 1, ":@/?");
|
||||
}
|
||||
|
||||
// Final link generation
|
||||
String linkifiedUri = String.format("<a href=\"%1$s\">%1$s</a>", text.substring(startPos, currentPos));
|
||||
outputBuffer.append(linkifiedUri);
|
||||
String httpUri = text.substring(startPos, currentPos);
|
||||
outputBuffer.append("<a href=\"")
|
||||
.append(httpUri)
|
||||
.append("\">")
|
||||
.append(httpUri)
|
||||
.append("</a>");
|
||||
|
||||
return currentPos;
|
||||
}
|
||||
|
@ -89,7 +92,7 @@ class HttpUriParser implements UriParser {
|
|||
}
|
||||
|
||||
private boolean tryMatchDomainName(String text, int startPos, int authorityEnd) {
|
||||
// Partly from OkHttp's HttpUrl (https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/HttpUrl.java)
|
||||
// Partly from OkHttp's HttpUrl
|
||||
try {
|
||||
// Check for port
|
||||
int portPos = text.indexOf(':', startPos);
|
||||
|
@ -143,7 +146,6 @@ class HttpUriParser implements UriParser {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Validate segments
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
int segment = Integer.parseInt(matcher.group(1));
|
||||
if (segment > 255) {
|
||||
|
@ -151,12 +153,10 @@ class HttpUriParser implements UriParser {
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure port does not exist if missing
|
||||
if (!portAllowed && matcher.group(5) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate optional port
|
||||
String portString = matcher.group(6);
|
||||
if (portString != null && !portString.isEmpty()) {
|
||||
int port = Integer.parseInt(portString);
|
||||
|
@ -169,7 +169,6 @@ class HttpUriParser implements UriParser {
|
|||
}
|
||||
|
||||
private boolean tryMatchIpv6Address(String text, int startPos, int authorityEnd) {
|
||||
// General validation
|
||||
if (text.codePointAt(startPos) != '[') {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.fsck.k9.message.html;
|
|||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -9,13 +10,7 @@ import java.util.regex.Pattern;
|
|||
import android.text.TextUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Allows conversion of link in text to html link.
|
||||
*/
|
||||
public class UriLinkifier {
|
||||
/**
|
||||
* Regular expression pattern to match uri scheme and parsers for supported uris as defined in RFC 3987
|
||||
*/
|
||||
private static final Pattern URI_SCHEME;
|
||||
private static final Map<String, UriParser> SUPPORTED_URIS;
|
||||
private static final String SCHEME_SEPARATOR = " (";
|
||||
|
@ -32,16 +27,8 @@ public class UriLinkifier {
|
|||
URI_SCHEME = Pattern.compile(allSchemes, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for link-like text in a string and turn it into a link. Append the result to
|
||||
* <tt>outputBuffer</tt>. <tt>text</tt> is not modified.
|
||||
*
|
||||
* @param text
|
||||
* Plain text to be linkified.
|
||||
* @param outputBuffer
|
||||
* Buffer to append linked text to.
|
||||
*/
|
||||
public static void linkifyText(final String text, final StringBuffer outputBuffer) {
|
||||
|
||||
public static void linkifyText(String text, StringBuffer outputBuffer) {
|
||||
int currentPos = 0;
|
||||
Matcher matcher = URI_SCHEME.matcher(text);
|
||||
|
||||
|
@ -51,31 +38,39 @@ public class UriLinkifier {
|
|||
String textBeforeMatch = text.substring(currentPos, startPos);
|
||||
outputBuffer.append(textBeforeMatch);
|
||||
|
||||
if (!textBeforeMatch.isEmpty() &&
|
||||
!SCHEME_SEPARATOR.contains(textBeforeMatch.substring(textBeforeMatch.length() - 1))) {
|
||||
if (!isPrecededByValidSeparator(textBeforeMatch)) {
|
||||
outputBuffer.append(text.charAt(startPos));
|
||||
currentPos = startPos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find responsible parser and let it do it's job
|
||||
String scheme = matcher.group();
|
||||
UriParser parser = SUPPORTED_URIS.get(scheme.toLowerCase());
|
||||
String scheme = matcher.group().toLowerCase(Locale.US);
|
||||
UriParser parser = SUPPORTED_URIS.get(scheme);
|
||||
int newPos = parser.linkifyUri(text, startPos, outputBuffer);
|
||||
|
||||
// Handle invalid uri, at least advance by one to prevent endless loop
|
||||
if (newPos <= startPos) {
|
||||
boolean uriWasNotLinkified = newPos <= startPos;
|
||||
if (uriWasNotLinkified) {
|
||||
outputBuffer.append(text.charAt(startPos));
|
||||
currentPos++;
|
||||
} else {
|
||||
currentPos = (newPos > currentPos) ? newPos : currentPos + 1;
|
||||
}
|
||||
|
||||
if (currentPos >= text.length()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy rest
|
||||
outputBuffer.append(text.substring(currentPos));
|
||||
String textAfterLastMatch = text.substring(currentPos);
|
||||
outputBuffer.append(textAfterLastMatch);
|
||||
}
|
||||
|
||||
private static boolean isPrecededByValidSeparator(String textBeforeMatch) {
|
||||
if (textBeforeMatch.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String characterBeforeMatch = textBeforeMatch.substring(textBeforeMatch.length() - 1);
|
||||
return SCHEME_SEPARATOR.contains(characterBeforeMatch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
package com.fsck.k9.message.html;
|
||||
|
||||
/**
|
||||
* General framework to handle uris when parsing. Allows different handling depending on the scheme identifier.
|
||||
*/
|
||||
|
||||
public interface UriParser {
|
||||
/**
|
||||
* Parse and linkify scheme specific uri beginning from given position. The result will be written to given buffer.
|
||||
* @param text String to parse uri from.
|
||||
* @param startPos Position where uri starts (first letter of scheme).
|
||||
* @param outputBuffer Buffer where linkified variant of uri is written to.
|
||||
* @return Index where parsed uri ends (first non-uri letter). Should be startPos or smaller if no valid uri was found.
|
||||
* Parse and linkify scheme specific URI beginning from given position. The result will be written to given buffer.
|
||||
*
|
||||
* @param text
|
||||
* String to parse URI from.
|
||||
* @param startPos
|
||||
* Position where URI starts (first letter of scheme).
|
||||
* @param outputBuffer
|
||||
* Buffer where linkified variant of URI is written to.
|
||||
*
|
||||
* @return Index where parsed URI ends (first non-URI letter). Should be {@code startPos} or smaller if no valid
|
||||
* URI was found.
|
||||
*/
|
||||
int linkifyUri(String text, int startPos, StringBuffer outputBuffer);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package com.fsck.k9.message.html;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.fsck.k9.message.html.UriParserTestHelper.assertLinkOnly;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
public class BitcoinUriParserTest {
|
||||
BitcoinUriParser parser = new BitcoinUriParser();
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
|
||||
|
||||
@Test
|
||||
public void basicBitcoinUri() throws Exception {
|
||||
assertLinkify("bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitcoinUriWithAmount() throws Exception {
|
||||
assertLinkify("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bitcoinUriWithQueryParameters() throws Exception {
|
||||
assertLinkify("bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2" +
|
||||
"&message=Payment&label=Satoshi&extra=other-param");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriInMiddleOfInput() throws Exception {
|
||||
String prefix = "prefix ";
|
||||
String uri = "bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2";
|
||||
String text = prefix + uri;
|
||||
|
||||
parser.linkifyUri(text, prefix.length(), outputBuffer);
|
||||
|
||||
assertLinkOnly(uri, outputBuffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidScheme() throws Exception {
|
||||
assertNotLinkify("bitcion:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidAddress() throws Exception {
|
||||
assertNotLinkify("bitcoin:[invalid]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidBitcoinUri_shouldReturnStartingPosition() throws Exception {
|
||||
String uri = "bitcoin:[invalid]";
|
||||
|
||||
int newPos = linkify(uri);
|
||||
|
||||
assertEquals(0, newPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidBitcoinUri_shouldNotWriteToOutputBuffer() throws Exception {
|
||||
String uri = "bitcoin:[invalid]";
|
||||
|
||||
linkify(uri);
|
||||
|
||||
assertEquals(0, outputBuffer.length());
|
||||
}
|
||||
|
||||
|
||||
int linkify(String uri) {
|
||||
return parser.linkifyUri(uri, 0, outputBuffer);
|
||||
}
|
||||
|
||||
void assertLinkify(String uri) {
|
||||
linkify(uri);
|
||||
assertLinkOnly(uri, outputBuffer);
|
||||
}
|
||||
|
||||
void assertNotLinkify(String text) {
|
||||
int newPos = linkify(text);
|
||||
assertEquals(0, newPos);
|
||||
}
|
||||
}
|
|
@ -1,216 +1,155 @@
|
|||
package com.fsck.k9.message.html;
|
||||
|
||||
|
||||
import com.fsck.k9.K9RobolectricTestRunner;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.fsck.k9.message.html.UriParserTestHelper.assertLinkOnly;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
|
||||
@RunWith(K9RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class HttpUriParserTest {
|
||||
private HttpUriParser parser;
|
||||
private StringBuffer outputBuffer;
|
||||
private final HttpUriParser parser = new HttpUriParser();
|
||||
private final StringBuffer outputBuffer = new StringBuffer();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
parser = new HttpUriParser();
|
||||
outputBuffer = new StringBuffer();
|
||||
|
||||
@Test
|
||||
public void simpleDomain() {
|
||||
assertLinkify("http://www.google.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleDomain() {
|
||||
String text = "http://www.google.com";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
public void domainWithTrailingSlash() {
|
||||
assertLinkify("http://www.google.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainWithTrailingSlash() {
|
||||
String text = "http://www.google.com/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
public void domainWithoutWww() {
|
||||
assertLinkify("http://google.com/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainWithoutWWW() {
|
||||
public void query() {
|
||||
assertLinkify("http://google.com/give/me/?q=mode&c=information");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fragment() {
|
||||
assertLinkify("http://google.com/give/me#only-the-best");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryAndFragment() {
|
||||
assertLinkify("http://google.com/give/me/?q=mode&c=information#only-the-best");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv4Address() {
|
||||
assertLinkify("http://127.0.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv4AddressWithTrailingSlash() {
|
||||
assertLinkify("http://127.0.0.1/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv4AddressWithEmptyPort() {
|
||||
assertLinkify("http://127.0.0.1:");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv4AddressWithPort() {
|
||||
assertLinkify("http://127.0.0.1:524/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6Address() {
|
||||
assertLinkify("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithPort() {
|
||||
assertLinkify("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithTrailingSlash() {
|
||||
assertLinkify("http://[1080:0:0:0:8:800:200C:417A]/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithEndCompression() {
|
||||
assertLinkify("http://[3ffe:2a00:100:7031::1]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithBeginCompression() {
|
||||
assertLinkify("http://[1080::8:800:200C:417A]/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithCompressionPort() {
|
||||
assertLinkify("http://[::FFFF:129.144.52.38]:80/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithPrependedCompression() {
|
||||
assertLinkify("http://[::192.9.5.5]/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ipv6AddressWithTrailingIp4AndPort() {
|
||||
assertLinkify("http://[::192.9.5.5]:80/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void domainWithTrailingSpace() {
|
||||
String text = "http://google.com/ ";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainWithTrailingSpace() {
|
||||
String text = "http://google.com/ ";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a>", outputBuffer.toString());
|
||||
|
||||
assertLinkOnly("http://google.com/", outputBuffer);
|
||||
assertEquals(text.length() - 1, endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainWithTrailingSpaceNewline() {
|
||||
public void domainWithTrailingNewline() {
|
||||
String text = "http://google.com/\n";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a>", outputBuffer.toString());
|
||||
assertEquals(text.length() - 2, endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainWithTrailingNewline() {
|
||||
String text = "http://google.com/\n";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a>", outputBuffer.toString());
|
||||
|
||||
assertLinkOnly("http://google.com/", outputBuffer);
|
||||
assertEquals(text.length() - 1, endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainsWithQueryAndFragment() {
|
||||
String text = "http://google.com/give/me/?q=mode&c=information#only-the-best";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(
|
||||
"<a href=\"http://google.com/give/me/?q=mode&c=information#only-the-best\">http://google.com/give/me/?q=mode&c=information#only-the-best</a>",
|
||||
outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
public void domainWithTrailingAngleBracket() {
|
||||
String text = "<http://google.com/>";
|
||||
|
||||
@Test
|
||||
public void testDomainsWithQuery() {
|
||||
String text = "http://google.com/give/me/?q=mode&c=information";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(
|
||||
"<a href=\"http://google.com/give/me/?q=mode&c=information\">http://google.com/give/me/?q=mode&c=information</a>",
|
||||
outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
int endPos = parser.linkifyUri(text, 1, outputBuffer);
|
||||
|
||||
@Test
|
||||
public void testDomainsWithFragment() {
|
||||
String text = "http://google.com/give/me#only-the-best";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(
|
||||
"<a href=\"http://google.com/give/me#only-the-best\">http://google.com/give/me#only-the-best</a>",
|
||||
outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDomainsWithQueryAndFragmentWithoutWWWW() {
|
||||
String text = "http://google.com/give/me/?q=mode+c=information#only-the-best\n";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(
|
||||
"<a href=\"http://google.com/give/me/?q=mode+c=information#only-the-best\">http://google.com/give/me/?q=mode+c=information#only-the-best</a>",
|
||||
outputBuffer.toString());
|
||||
assertLinkOnly("http://google.com/", outputBuffer);
|
||||
assertEquals(text.length() - 1, endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv4Address() {
|
||||
String text = "http://127.0.0.1";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
public void uriInMiddleOfInput() throws Exception {
|
||||
String prefix = "prefix ";
|
||||
String uri = "http://google.com/";
|
||||
String text = prefix + uri;
|
||||
|
||||
parser.linkifyUri(text, prefix.length(), outputBuffer);
|
||||
|
||||
assertLinkOnly(uri, outputBuffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv4AddressWithTrailingSlash() {
|
||||
String text = "http://127.0.0.1/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
|
||||
int linkify(String uri) {
|
||||
return parser.linkifyUri(uri, 0, outputBuffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv4AddressWithEmptyPort() {
|
||||
String text = "http://127.0.0.1:";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv4AddressWithPort() {
|
||||
String text = "http://127.0.0.1:524/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6Address() {
|
||||
String text = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithPort() {
|
||||
String text = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressShort() {
|
||||
String text = "http://[1080:0:0:0:8:800:200C:417A]/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithEndCompression() {
|
||||
String text = "http://[3ffe:2a00:100:7031::1]";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithBeginCompression() {
|
||||
String text = "http://[1080::8:800:200C:417A]/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithPrependedCompression() {
|
||||
String text = "http://[::192.9.5.5]/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithCompressionPort() {
|
||||
String text = "http://[::FFFF:129.144.52.38]:80/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithTrailingIp4() {
|
||||
String text = "http://[::192.9.5.5]/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpv6AddressWithTrailingIp4AndPort() {
|
||||
String text = "http://[::192.9.5.5]:80/";
|
||||
int endPos = parser.linkifyUri(text, 0, outputBuffer);
|
||||
assertEquals(String.format("<a href=\"%1$s\">%1$s</a>", text), outputBuffer.toString());
|
||||
assertEquals(text.length(), endPos);
|
||||
void assertLinkify(String uri) {
|
||||
linkify(uri);
|
||||
assertLinkOnly(uri, outputBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,90 +6,113 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.fsck.k9.message.html.UriParserTestHelper.assertLinkOnly;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
|
||||
@RunWith(K9RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class UriLinkifierTest {
|
||||
@Test
|
||||
public void testLinkifyBitcoinAndHttpUri() {
|
||||
String text = "bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU http://example.com/";
|
||||
private StringBuffer outputBuffer = new StringBuffer();
|
||||
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals("<a href=\"bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU\">" +
|
||||
"bitcoin:19W6QZkx8SYPG7BBCS7odmWGRxqRph5jFU" +
|
||||
"</a> " +
|
||||
"<a href=\"http://example.com/\">" +
|
||||
"http://example.com/" +
|
||||
"</a>", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleHttpUri() {
|
||||
String text = "http://www.google.com";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://www.google.com\">http://www.google.com</a>", outputBuffer.toString());
|
||||
}
|
||||
public void emptyText() {
|
||||
String text = "";
|
||||
|
||||
@Test
|
||||
public void testHttpUriWithTrailingSlash() {
|
||||
String text = "http://www.google.com/";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://www.google.com/\">http://www.google.com/</a>",
|
||||
outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpUriWithoutWWW() {
|
||||
String text = "http://google.com/";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a>", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpUriWithTrailingSpace() {
|
||||
String text = "http://google.com/ ";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a> ", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpUriWithTrailingSpaceNewline() {
|
||||
String text = "http://google.com/ \n";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a> \n",
|
||||
outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpUriWithTrailingNewline() {
|
||||
String text = "http://google.com/\n";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals("<a href=\"http://google.com/\">http://google.com/</a>\n", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnorePartialHttpUriScheme() {
|
||||
String text = "myhttp://example.org";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
assertEquals(text, outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartialHttpUriSchemeWithSeparator() {
|
||||
String text = "(http://example.org";
|
||||
StringBuffer outputBuffer = new StringBuffer();
|
||||
public void textWithoutUri_shouldBeCopiedToOutputBuffer() {
|
||||
String text = "some text here";
|
||||
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals(text, outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleUri() {
|
||||
String uri = "http://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(uri, outputBuffer);
|
||||
|
||||
assertLinkOnly(uri, outputBuffer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriPrecededBySpace() {
|
||||
String text = " http://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals(" <a href=\"http://example.org\">http://example.org</a>", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriPrecededByOpeningParenthesis() {
|
||||
String text = "(http://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals("(<a href=\"http://example.org\">http://example.org</a>", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriPrecededBySomeText() {
|
||||
String uri = "Check out my fantastic URI: http://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(uri, outputBuffer);
|
||||
|
||||
assertEquals("Check out my fantastic URI: <a href=\"http://example.org\">http://example.org</a>",
|
||||
outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriWithTrailingText() {
|
||||
String uri = "http://example.org/ is the best";
|
||||
|
||||
UriLinkifier.linkifyText(uri, outputBuffer);
|
||||
|
||||
assertEquals("<a href=\"http://example.org/\">http://example.org/</a> is the best", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriEmbeddedInText() {
|
||||
String uri = "prefix http://example.org/ suffix";
|
||||
|
||||
UriLinkifier.linkifyText(uri, outputBuffer);
|
||||
|
||||
assertEquals("prefix <a href=\"http://example.org/\">http://example.org/</a> suffix", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriWithUppercaseScheme() {
|
||||
String uri = "HTTP://example.org/";
|
||||
|
||||
UriLinkifier.linkifyText(uri, outputBuffer);
|
||||
|
||||
assertEquals("<a href=\"HTTP://example.org/\">HTTP://example.org/</a>", outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriNotPrecededByValidSeparator_shouldNotBeLinkified() {
|
||||
String text = "myhttp://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals(text, outputBuffer.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uriNotPrecededByValidSeparatorFollowedByValidUri() {
|
||||
String text = "myhttp: http://example.org";
|
||||
|
||||
UriLinkifier.linkifyText(text, outputBuffer);
|
||||
|
||||
assertEquals("myhttp: <a href=\"http://example.org\">http://example.org</a>", outputBuffer.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.fsck.k9.message.html;
|
||||
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class UriParserTestHelper {
|
||||
public static void assertContainsLink(String expected, StringBuffer actual) {
|
||||
String linkifiedUri = actual.toString();
|
||||
Document document = Jsoup.parseBodyFragment(linkifiedUri);
|
||||
Element anchorElement = document.select("a").first();
|
||||
assertNotNull("No <a> element found", anchorElement);
|
||||
assertEquals(expected, anchorElement.text());
|
||||
assertEquals(expected, anchorElement.attr("href"));
|
||||
}
|
||||
|
||||
public static void assertLinkOnly(String expected, StringBuffer actual) {
|
||||
String linkifiedUri = actual.toString();
|
||||
Document document = Jsoup.parseBodyFragment(linkifiedUri);
|
||||
Element anchorElement = document.select("a").first();
|
||||
assertNotNull("No <a> element found", anchorElement);
|
||||
assertEquals(expected, anchorElement.text());
|
||||
assertEquals(expected, anchorElement.attr("href"));
|
||||
|
||||
assertAnchorElementIsSoleContent(document, anchorElement);
|
||||
}
|
||||
|
||||
private static void assertAnchorElementIsSoleContent(Document document, Element anchorElement) {
|
||||
assertEquals(document.body(), anchorElement.parent());
|
||||
assertTrue("<a> element is surrounded by text", document.body().textNodes().isEmpty());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue