package com.twitter; import com.twitter.Extractor.Entity; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import twitter4j.MediaEntity; import twitter4j.URLEntity; /** * A class for adding HTML links to hashtag, username and list references in * Tweet text. */ public class Autolink { public static interface LinkAttributeModifier { public void modify(Entity entity, Map<String, String> attributes); } public static interface LinkTextModifier { public CharSequence modify(Entity entity, CharSequence text); } private final String usernameUrlBase; private final String listUrlBase; private final String hashtagUrlBase; private final String cashtagUrlBase; private final String symbolTag = null; private final String textWithSymbolTag = null; private final LinkAttributeModifier linkAttributeModifier = null; private final LinkTextModifier linkTextModifier = null; private final Extractor extractor = new Extractor(); private static CharSequence escapeHTML(CharSequence text) { StringBuilder builder = new StringBuilder(text.length() * 2); for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); switch (c) { case '&': builder.append("&"); break; case '>': builder.append(">"); break; case '<': builder.append("<"); break; case '"': builder.append("""); break; case '\'': builder.append("'"); break; default: builder.append(c); break; } } return builder; } public Autolink() { usernameUrlBase = "com.tweetlanes.android.core.profile://"; listUrlBase = "com.tweetlanes.android.core.profile://"; hashtagUrlBase = "com.tweetlanes.android.core.search://"; cashtagUrlBase = "com.tweetlanes.android.core.search://"; extractor.setExtractURLWithoutProtocol(false); } public void setExtractURLWithoutProtocol(boolean newValue) { extractor.setExtractURLWithoutProtocol(newValue); } String escapeBrackets(String text) { int len = text.length(); if (len == 0) return text; StringBuilder sb = new StringBuilder(len + 16); for (int i = 0; i < len; ++i) { char c = text.charAt(i); if (c == '>') sb.append(">"); else if (c == '<') sb.append("<"); else sb.append(c); } return sb.toString(); } void linkToText(Entity entity, CharSequence text, Map<String, String> attributes, StringBuilder builder) { if (linkAttributeModifier != null) { linkAttributeModifier.modify(entity, attributes); } if (linkTextModifier != null) { text = linkTextModifier.modify(entity, text); } // append <a> tag builder.append("<a"); for (Map.Entry<String, String> entry : attributes.entrySet()) { builder.append(" ").append(escapeHTML(entry.getKey())) .append("=\"").append(escapeHTML(entry.getValue())) .append("\""); } builder.append(">").append(text).append("</a>"); } void linkToTextWithSymbol(Entity entity, CharSequence symbol, CharSequence text, Map<String, String> attributes, StringBuilder builder) { CharSequence taggedSymbol = symbolTag == null || symbolTag.isEmpty() ? symbol : String.format("<%s>%s</%s>", symbolTag, symbol, symbolTag); text = escapeHTML(text); CharSequence taggedText = textWithSymbolTag == null || textWithSymbolTag.isEmpty() ? text : String.format( "<%s>%s</%s>", textWithSymbolTag, text, textWithSymbolTag); linkToText(entity, taggedSymbol.toString() + taggedText, attributes, builder); } void linkToHashtag(Entity entity, String text, StringBuilder builder) { // Get the original hash char from text as it could be a full-width // char. CharSequence hashChar = text.subSequence(entity.getStart(), entity.getStart() + 1); CharSequence hashtag = entity.getValue(); Map<String, String> attrs = new LinkedHashMap<String, String>(); attrs.put("href", hashtagUrlBase + "#" + hashtag); linkToTextWithSymbol(entity, hashChar, hashtag, attrs, builder); } void linkToCashtag(Entity entity, String text, StringBuilder builder) { CharSequence cashtag = entity.getValue(); Map<String, String> attrs = new LinkedHashMap<String, String>(); attrs.put("href", cashtagUrlBase + cashtag); linkToTextWithSymbol(entity, "$", cashtag, attrs, builder); } void linkToMentionAndList(Entity entity, String text, StringBuilder builder) { String mention = entity.getValue(); // Get the original at char from text as it could be a full-width char. CharSequence atChar = text.subSequence(entity.getStart(), entity.getStart() + 1); Map<String, String> attrs = new LinkedHashMap<String, String>(); if (entity.listSlug != null) { mention += entity.listSlug; attrs.put("href", listUrlBase + mention); } else { attrs.put("href", usernameUrlBase + mention); } linkToTextWithSymbol(entity, atChar, mention, attrs, builder); } void linkToURL(Entity entity, String text, StringBuilder builder, URLEntity urlEntity, boolean showFullUrl) { CharSequence url = entity.getValue(); String linkText = escapeHTML(url).toString(); if (urlEntity != null && urlEntity.getExpandedURL() != null && showFullUrl) { linkText = urlEntity.getExpandedURL(); } else if (urlEntity != null && urlEntity.getDisplayURL() != null) { linkText = urlEntity.getDisplayURL(); } else if (entity.displayURL != null) { linkText = entity.displayURL; } //Remove "http://" or "https://" //Then remove "www." linkText = linkText.replaceAll("^https?://", "").replaceAll("^www.", ""); Map<String, String> attrs = new LinkedHashMap<String, String>(); if (urlEntity != null && urlEntity.getExpandedURL() != null) { attrs.put("href", urlEntity.getExpandedURL()); } else { attrs.put("href", url.toString()); } linkToText(entity, linkText, attrs, builder); } String autoLinkEntities(String text, List<Entity> entities, MediaEntity[] mediaEntities, URLEntity[] urlEntities, boolean showFullUrl) { StringBuilder builder = new StringBuilder(text.length() * 2); int beginIndex = 0; int urlCount = 0; for (Entity entity : entities) { builder.append(text.subSequence(beginIndex, entity.start)); switch (entity.type) { case URL: URLEntity urlEntity = null; if (urlEntities != null && urlCount < urlEntities.length) { urlEntity = urlEntities[urlCount]; urlCount += 1; } if (urlEntity == null) { if (entity != null && entity.value != null && mediaEntities != null && mediaEntities.length > 0) { MediaEntity mediaEntity = mediaEntities[0]; if (entity.value .equals(mediaEntity.getURL())) { entity.displayURL = mediaEntity.getDisplayURL(); entity.expandedURL = mediaEntity.getExpandedURL(); } } } linkToURL(entity, text, builder, urlEntity, showFullUrl); break; case HASHTAG: linkToHashtag(entity, text, builder); break; case MENTION: linkToMentionAndList(entity, text, builder); break; case CASHTAG: linkToCashtag(entity, text, builder); break; } beginIndex = entity.end; } builder.append(text.subSequence(beginIndex, text.length())); return builder.toString(); } public String autoLinkAll(String text, MediaEntity[] mediaEntities, URLEntity[] urlEntities, boolean showFullUrl) { text = escapeBrackets(text); // extract entities List<Entity> entities = extractor.extractEntitiesWithIndices(text); return autoLinkEntities(text, entities, mediaEntities, urlEntities, showFullUrl); } }