Commits

Ville Saalo committed e39290c

Render email addresses as clickable links.

  • Participants
  • Parent commits 4821a90

Comments (0)

Files changed (2)

File src/com/saibotd/bitbeaker/MarkupHelper.java

 import android.util.Patterns;
 
 public class MarkupHelper {
+	
+	private static final String LINK_TAG = "[[";
+	private static final String HTML_HREF_ATTR_END = "\">";
+	private static final String HTML_LINK_TAG_END = "</a>";
+	
 	/**
 	 * Add Bitbucket's markup support for comments and issue descriptions to TextView
 	 * 
 		input = input.replaceAll("(?<!:)//(.+?)(?<!:)//", "<i>$1</i>"); // italic
 
 		// links
-		// bare urls - begins
-		Matcher m = Patterns.WEB_URL.matcher(input);
-		StringBuffer sb = new StringBuffer();
-		String linktag = "[[";
-		while (m.find()) {
-			int match_start = m.start();
-			int start = (match_start >= linktag.length() ? match_start-linktag.length() : 0);
-			if (!input.substring(start, match_start).equals(linktag)) {
-				String protocol;
-				if ( !m.group(0).startsWith("http://") &&
-				     !m.group(0).startsWith("https://") &&
-				     !m.group(0).startsWith("ftp://")) protocol = "http://";
-				else protocol = "";
-				m.appendReplacement(sb, "<a href=\""+protocol+m.group(0)+"\">"+m.group(0)+"</a>");
-			}
-		}
-		m.appendTail(sb);
-		input = sb.toString(); // bare urls - ends
+		input = getEmailAddressesLinkified(input);
+		input = getBareUrlsLinkified(input);
 		input = input.replaceAll("\\[\\[(.*?)\\|(.*?)\\]\\]", "<a href=\"$1\">$2</a>");
 		input = input.replaceAll("#(\\d+)", "<a href=\"https://bitbucket.org/"+owner+"/"+slug+"/issue/$1\">#$1</a>");
 		input = input.replaceAll("(?<!\\w)@([\\w\\d_]{1,30})", "<a href=\"https://bitbucket.org/$1\">@$1</a>"); // username up to 30 chars: letters, numbers, underscores
 
 		return input;
 	}
+	
+	private static String getBareUrlsLinkified(String input) {
+		Matcher matcher = Patterns.WEB_URL.matcher(input);
+		StringBuffer sb = new StringBuffer();
+		while (matcher.find()) {
+			int matchStart = matcher.start();
+			int matchEnd = matcher.end();
+			// This URL might be surrounded with the [[ and ]] tags, assigning it a custom link text:
+			int possibleLinkTagStart = (matchStart >= LINK_TAG.length() ? matchStart-LINK_TAG.length() : 0);
+			// This URL might be the domain part of an already linkified email address, right before the closing </a> tag:
+			int possibleHtmlLinkEnd = (matchEnd + HTML_LINK_TAG_END.length() > input.length() ? input.length() : matchEnd + HTML_LINK_TAG_END.length());
+			// On the other hand this URL might also be the domain part of an already linkified email address, inside the href-parameter:
+			int possibleHrefAttributeEnd = (matchEnd + HTML_HREF_ATTR_END.length() > input.length() ? input.length() : matchEnd + HTML_HREF_ATTR_END.length());
+			
+			final String address = matcher.group(0);
+			if (!input.substring(possibleLinkTagStart, matchStart).equals(LINK_TAG) &&
+				!input.substring(matchEnd, possibleHrefAttributeEnd).equals(HTML_HREF_ATTR_END) &&
+				!input.substring(matchEnd, possibleHtmlLinkEnd).equals(HTML_LINK_TAG_END)) {
+				String protocol = "";
+				if ( !address.startsWith("http://") &&
+				     !address.startsWith("https://") &&
+				     !address.startsWith("ftp://")) {
+					protocol = "http://";
+				}
+				matcher.appendReplacement(sb, "<a href=\""+protocol+address+"\">"+address+"</a>");
+			}
+		}
+		matcher.appendTail(sb);
+		return sb.toString(); 
+	}
+	
+	private static String getEmailAddressesLinkified(String input) {
+		Matcher matcher = Patterns.EMAIL_ADDRESS.matcher(input);
+		StringBuffer sb = new StringBuffer();
+		while (matcher.find()) {
+			int matchStart = matcher.start();
+			int start = (matchStart >= LINK_TAG.length() ? matchStart-LINK_TAG.length() : 0);
+			final String address = matcher.group(0);
+			if (!input.substring(start, matchStart).equals(LINK_TAG)) {
+				matcher.appendReplacement(sb, "<a href=\"mailto:"+address+"\">"+address+"</a>");
+			}
+		}
+		matcher.appendTail(sb);
+		return sb.toString(); 
+	}
 }

File src/com/saibotd/bitbeaker/tests/MarkupHelperTest.java

 				MarkupHelper.decorate("@Test test asd", "", ""));
 	}
 	
-	public void test_decorate_not_messing_up_email_addresses() {
+	public void test_decorate_email_addresses() {
 		assertEquals("An email address wasn't rendered properly!",
-				"someone@example.com", 
-				MarkupHelper.decorate("someone@example.com", "", ""));
+				"<a href=\"mailto:someone@example.org\">someone@example.org</a>", 
+				MarkupHelper.decorate("someone@example.org", "", ""));
+		
+		assertEquals("An email address at the start of the input wasn't rendered properly!",
+				"<a href=\"mailto:someone@example.net\">someone@example.net</a> test asd foo qwerty", 
+				MarkupHelper.decorate("someone@example.net test asd foo qwerty", "", ""));
+		
+		assertEquals("An email address at the end of the input wasn't rendered properly!",
+				"test asd foo qwerty <a href=\"mailto:someone@example.com\">someone@example.com</a>", 
+				MarkupHelper.decorate("test asd foo qwerty someone@example.com", "", ""));
 		
 		assertEquals("An email address with numbers wasn't rendered properly!",
-				"test01@example.org", 
-				MarkupHelper.decorate("test01@example.org", "", ""));
+				"Mail here: <a href=\"mailto:test01@example.org\">test01@example.org</a>, please!", 
+				MarkupHelper.decorate("Mail here: test01@example.org, please!", "", ""));
+		
+		assertEquals("An email address with dots wasn't rendered properly!",
+				"<a href=\"mailto:first.m.last@example.org\">first.m.last@example.org</a>", 
+				MarkupHelper.decorate("first.m.last@example.org", "", ""));
 	}
 	
 	public void test_decorate_bracketed_urls() {
 	public void test_decorate_both_bracketed_and_plain_urls_times_two() {
 		String expected = "<a href=\"http://a.com\">A</a> <a href=\"http://b.net\">http://b.net</a> <a href=\"http://c.org\">C</a> <a href=\"http://d.biz\">http://d.biz</a>";
 		String actual = MarkupHelper.decorate("[[http://a.com|A]] http://b.net [[http://c.org|C]] http://d.biz", "", "");
-		System.out.println("exp: "+expected);
-		System.out.println("act: "+actual);
 		assertEquals("Test with two bracketed and two plain URL fails!", expected, actual);
 	}
+	
+	public void test_decorate_both_bracketed_and_plain_urls_and_email_address() {
+		String expected = "<a href=\"http://bitbucket.org/\">foo</a> <a href=\"http://www.example.com\">http://www.example.com</a> zok <a href=\"mailto:john.doe@example.com\">john.doe@example.com</a> baz";
+		String actual = MarkupHelper.decorate("[[http://bitbucket.org/|foo]] http://www.example.com zok john.doe@example.com baz", "", ""); 
+		assertEquals("Test with one bracketed and one plain URL fails!", expected, actual);
+	}
 
 	public void test_handleMarkupAsString() throws Exception {
 		Method method = instance.getClass().getDeclaredMethod("handleMarkupAsString", String.class, String.class, String.class);