package org.wordpress.android.editor; import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.res.AssetManager; import android.util.Patterns; import org.wordpress.android.util.AppLog; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; public class Utils { public static String getHtmlFromFile(Activity activity, String filename) { try { AssetManager assetManager = activity.getAssets(); InputStream in = assetManager.open(filename); return getStringFromInputStream(in); } catch (IOException e) { AppLog.e(AppLog.T.EDITOR, "Unable to load editor HTML (is the assets symlink working?): " + e.getMessage()); return null; } } public static String getStringFromInputStream(InputStream inputStream) throws IOException { InputStreamReader is = new InputStreamReader(inputStream); StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(is); String read = br.readLine(); while (read != null) { sb.append(read); sb.append('\n'); read = br.readLine(); } return sb.toString(); } public static String escapeHtml(String html) { if (html != null) { html = html.replace("\\", "\\\\"); html = html.replace("\"", "\\\""); html = html.replace("'", "\\'"); html = html.replace("\r", "\\r"); html = html.replace("\n", "\\n"); // Escape invisible line separator (U+2028) and paragraph separator (U+2029) characters // https://github.com/wordpress-mobile/WordPress-Editor-Android/issues/405 html = html.replace("\u2028", "\\u2028"); html = html.replace("\u2029", "\\u2029"); } return html; } public static String decodeHtml(String html) { if (html != null) { try { html = URLDecoder.decode(html, "UTF-8"); } catch (UnsupportedEncodingException e) { AppLog.e(AppLog.T.EDITOR, "Unsupported encoding exception while decoding HTML."); } } return html; } public static String escapeQuotes(String text) { if (text != null) { text = text.replace("'", "\\'").replace("\"", "\\\""); } return text; } /** * Splits a delimited string into a set of strings. * @param string the delimited string to split * @param delimiter the string delimiter */ public static Set<String> splitDelimitedString(String string, String delimiter) { Set<String> splitString = new HashSet<>(); StringTokenizer stringTokenizer = new StringTokenizer(string, delimiter); while (stringTokenizer.hasMoreTokens()) { splitString.add(stringTokenizer.nextToken()); } return splitString; } /** * Splits a delimited string of value pairs (of the form identifier=value) into a set of strings. * @param string the delimited string to split * @param delimiter the string delimiter * @param identifiers the identifiers to match for in the string */ public static Set<String> splitValuePairDelimitedString(String string, String delimiter, List<String> identifiers) { String identifierSegment = ""; for (String identifier : identifiers) { if (identifierSegment.length() != 0) { identifierSegment += "|"; } identifierSegment += identifier; } String regex = delimiter + "(?=(" + identifierSegment + ")=)"; return new HashSet<>(Arrays.asList(string.split(regex))); } /** * Accepts a set of strings, each string being a key-value pair (<code>id=5</code>, * <code>name=content-filed</code>). Returns a map of all the key-value pairs in the set. * @param keyValueSet the set of key-value pair strings */ public static Map<String, String> buildMapFromKeyValuePairs(Set<String> keyValueSet) { Map<String, String> selectionArgs = new HashMap<>(); for (String pair : keyValueSet) { int delimLoc = pair.indexOf("="); if (delimLoc != -1) { selectionArgs.put(pair.substring(0, delimLoc), pair.substring(delimLoc + 1)); } } return selectionArgs; } /** * Compares two <code>Sets</code> and returns a <code>Map</code> of elements not contained in both * <code>Sets</code>. Elements contained in <code>oldSet</code> but not in <code>newSet</code> will be marked * <code>false</code> in the returned map; the converse will be marked <code>true</code>. * @param oldSet the older of the two <code>Sets</code> * @param newSet the newer of the two <code>Sets</code> * @param <E> type of element stored in the <code>Sets</code> * @return a <code>Map</code> containing the difference between <code>oldSet</code> and <code>newSet</code>, and whether the * element was added (<code>true</code>) or removed (<code>false</code>) in <code>newSet</code> */ public static <E> Map<E, Boolean> getChangeMapFromSets(Set<E> oldSet, Set<E> newSet) { Map<E, Boolean> changeMap = new HashMap<>(); Set<E> additions = new HashSet<>(newSet); additions.removeAll(oldSet); Set<E> removals = new HashSet<>(oldSet); removals.removeAll(newSet); for (E s : additions) { changeMap.put(s, true); } for (E s : removals) { changeMap.put(s, false); } return changeMap; } /** * Checks the Clipboard for text that matches the {@link Patterns#WEB_URL} pattern. * * @return the URL text in the clipboard, if it exists; otherwise null */ public static String getUrlFromClipboard(Context context) { if (context == null) return null; ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); ClipData data = clipboard != null ? clipboard.getPrimaryClip() : null; if (data == null || data.getItemCount() <= 0) return null; String clipText = String.valueOf(data.getItemAt(0).getText()); return Patterns.WEB_URL.matcher(clipText).matches() ? clipText : null; } }