package in.partake.base; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.UUID; import java.util.regex.Pattern; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.node.ArrayNode; import org.codehaus.jackson.node.JsonNodeFactory; import play.Logger; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.twitter.Regex; public final class Util { private static final Random random = new Random(); private static final Pattern REMOVETAG_PATTERN = Pattern.compile("(<!--.+?-->)|(<.+?>)", Pattern.DOTALL | Pattern.MULTILINE); private static final String ALNUM = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // ---------------------------------------------------------------------- // UUID public static boolean isUUID(String str) { if (str == null) return false; try { UUID.fromString(str); return true; } catch (IllegalArgumentException e) { return false; } } // ---------------------------------------------------------------------- // Numeric public static long ensureMin(long value, long min) { if (value < min) return min; return value; } public static int ensureMin(int value, int min) { if (value < min) return min; return value; } public static int ensureRange(int value, int min, int max) { assert min <= max; if (value < min) return min; if (max < value) return max; return value; } // ---------------------------------------------------------------------- // Text public static <T extends JSONable> ArrayNode toJSONArray(List<T> list) { if (list == null) return null; ArrayNode array = new ArrayNode(JsonNodeFactory.instance); for (JSONable jsonable : list) array.add(jsonable.toJSON()); return array; } public static <T extends SafeJSONable> ArrayNode toSafeJSONArray(List<T> list) { if (list == null) return null; ArrayNode array = new ArrayNode(JsonNodeFactory.instance); for (SafeJSONable jsonable : list) array.add(jsonable.toSafeJSON()); return array; } public static Boolean parseBooleanParameter(String value) { if ("true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "checked".equalsIgnoreCase(value)) return true; if ("false".equalsIgnoreCase(value) || "off".equalsIgnoreCase(value)) return false; return null; } public static boolean parseBooleanParameter(String value, boolean defaultValue) { Boolean result = parseBooleanParameter(value); if (result != null) return result; else return defaultValue; } public static Map<UUID, List<String>> parseEnqueteAnswers(JsonNode map) { Iterator<Entry<String, JsonNode>> iter = map.getFields(); Map<UUID, List<String>> enqueteAnswers = Maps.newHashMap(); while (iter.hasNext()) { Entry<String, JsonNode> field = iter.next(); if (!Util.isUUID(field.getKey()) || !field.getValue().isArray()) { continue; } List<String> answers = Lists.newArrayList(); for (JsonNode node : field.getValue()) { answers.add(node.asText()); } enqueteAnswers.put(UUID.fromString(field.getKey()), answers); } return enqueteAnswers; } public static boolean isValidHashtag(String hashTag) { return Regex.AUTO_LINK_HASHTAGS.matcher(hashTag).matches(); } public static String randomString(int length) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < length; ++i) { builder.append(ALNUM.charAt(random.nextInt(ALNUM.length()))); } return builder.toString(); } public static int codePointCount(String s) { return s.codePointCount(0, s.length()); } public static String substring(String source, int startCodePoints) { final int endCodePoints = source.codePointCount(0, source.length()); return substring(source, startCodePoints, endCodePoints); } public static String substring(String source, int startCodePoints, int endCodePoints) { final int startIndex = source.offsetByCodePoints(0, startCodePoints); final int endIndex = source.offsetByCodePoints(startIndex, endCodePoints - startCodePoints); return source.substring(startIndex, endIndex); } public static String shorten(String message, int maxLength) { if (message.codePointCount(0, message.length()) <= maxLength) { return message; } return substring(substring(message, 0, Math.max(maxLength - 3, 0)) + "...", 0, Math.max(maxLength, 0)); } /** * 文字列から'#'と後続の文字列を取り除いたものを返す。 * URLから # + fragment を取り除区などの用途を想定。 * * @param str 改行を含まない加工対象文字列 * @return '#'と後続の文字列を取り除いた文字列 */ public static String removeURLFragment(String str) { if (str == null) { return null; } return str.replaceAll("#.*", ""); } public static void writeFromFile(BufferedWriter writer, File inFile, String encode) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inFile), encode)); try { String line; while ((line = reader.readLine()) != null) { if (line.startsWith("@charset") || line.isEmpty()) continue; writer.write(line); writer.newLine(); } } finally { try { reader.close(); } catch (IOException ignore) { Logger.warn("Reader#close throw IOException, but it's ignored.", ignore); } } } // ---------------------------------------------------------------------- // Image public static boolean isImageContentType(String s) { if (s == null) { return false; } if ("image/jpeg".equals(s)) { return true; } if ("image/png".equals(s)) { return true; } if ("image/gif".equals(s)) { return true; } if ("image/pjpeg".equals(s)){ return true; } return false; } /** * file の内容を byte array に変換する */ public static byte[] getContentOfFile(File file) throws IOException { if (file == null) return new byte[0]; InputStream is = new BufferedInputStream(new FileInputStream(file)); return getContentOfInputStream(is); } public static byte[] getContentOfInputStream(InputStream is) throws IOException { if (is == null) return null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); final int SIZE = 1024 * 1024; byte[] buf = new byte[SIZE]; int len; while ((len = is.read(buf)) > 0) { baos.write(buf, 0, len); } return baos.toByteArray(); } finally { is.close(); } } public static InputStream createInputSteram(String resource) throws IOException { InputStream stream = Util.class.getResourceAsStream(resource); if (stream != null) { return stream; } stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); if (stream != null) { return stream; } try { return new FileInputStream(new File(resource)); } catch (Exception e) { return null; } } // ---------------------------------------------------------------------- // HTML /** * validなHTMLから、HTMLタグとコメントを取り除く。 * * @param html 加工するHTML文字列 * @return HTMLタグとコメントを取り除いた文字列 */ public static String removeTags(String html) { if (html == null) { return null; } return REMOVETAG_PATTERN.matcher(html).replaceAll(""); } // ---------------------------------------------------------------------- // URI /** * Javascriptの同名関数と同様、 * 文字列をURIのパラメータとして使用できるようにエンコードを施す。 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent */ public static String encodeURIComponent(String uri) { try { return URLEncoder.encode(uri, "UTF-8") .replaceAll("\\+", "%20") .replaceAll("\\%21", "!") .replaceAll("\\%27", "'") .replaceAll("\\%28", "(") .replaceAll("\\%29", ")") .replaceAll("\\%7E", "~"); } catch (UnsupportedEncodingException e) { Logger.warn("safely returns empty string.", e); return ""; } } }