+ * Precise to the second. * * @param time - * the time in milliseconds + * the specified number of milliseconds since the standard base + * time known as "the epoch", namely January 1, 1970, 00:00:00 + * GMT * * @return the time as a {@link String} */ @@ -186,422 +427,736 @@ public class StringUtils { } /** - * Convert between time as a {@link String} to milliseconds in a "static" + * Convert between the time as a {@link String} to milliseconds in a "fixed" * way (to exchange data over the wire, for instance). + *
+ * Precise to the second. * - * @param time + * @param displayTime * the time as a {@link String} * - * @return the time in milliseconds + * @return the number of milliseconds since the standard base time known as + * "the epoch", namely January 1, 1970, 00:00:00 GMT, or -1 in case + * of error + * + * @throws ParseException + * in case of parse error */ - static public long toTime(String display) { + static public long toTime(String displayTime) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.parse(displayTime).getTime(); + } + + /** + * Return a hash of the given {@link String}. + * + * @param input + * the input data + * + * @return the hash + */ + static public String getMd5Hash(String input) { try { - return sdf.parse(display).getTime(); - } catch (ParseException e) { - return -1; + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(getBytes(input)); + byte byteData[] = md.digest(); + + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < byteData.length; i++) { + String hex = Integer.toHexString(0xff & byteData[i]); + if (hex.length() == 1) + hexString.append('0'); + hexString.append(hex); + } + + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + return input; } } /** - * Convert the given {@link Image} object into a Base64 representation of - * the same {@link Image}. object. - * - * @param image - * the {@link Image} object to convert + * Remove the HTML content from the given input, and un-html-ize the rest. * - * @return the Base64 representation + * @param html + * the HTML-encoded content * - * @throws IOException - * in case of IO error + * @return the HTML-free equivalent content */ - static public String fromImage(BufferedImage image) throws IOException { - String imageString = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); + public static String unhtml(String html) { + StringBuilder builder = new StringBuilder(); + + int inTag = 0; + for (char car : html.toCharArray()) { + if (car == '<') { + inTag++; + } else if (car == '>') { + inTag--; + } else if (inTag <= 0) { + builder.append(car); + } + } + + char nbsp = 'Â '; // non-breakable space (a special char) + char space = ' '; + return HtmlEscape.unescapeHtml(builder.toString()).replace(nbsp, space); + } - ImageIO.write(image, "jpeg", out); - byte[] imageBytes = out.toByteArray(); + /** + * Escape the given {@link String} so it can be used in XML, as content. + * + * @param input + * the input {@link String} + * + * @return the escaped {@link String} + */ + public static String xmlEscape(String input) { + if (input == null) { + return ""; + } - imageString = new String(Base64.getEncoder().encode(imageBytes)); + return HtmlEscape.escapeHtml(input, + HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } - out.close(); + /** + * Escape the given {@link String} so it can be used in XML, as text content + * inside double-quotes. + * + * @param input + * the input {@link String} + * + * @return the escaped {@link String} + */ + public static String xmlEscapeQuote(String input) { + if (input == null) { + return ""; + } - return imageString; + return HtmlEscape.escapeHtml(input, + HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); } /** - * Convert the given {@link File} image into a Base64 representation of the - * same {@link File}. + * Zip the data and then encode it into Base64. * - * @param file - * the {@link File} image to convert + * @param data + * the data * - * @return the Base64 representation + * @return the Base64 zipped version * * @throws IOException - * in case of IO error + * in case of I/O error */ - static public String fromStream(InputStream in) throws IOException { - String fileString = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); + public static String zip64(String data) throws IOException { + try { + return zip64(getBytes(data)); + } catch (UnsupportedEncodingException e) { + // All conforming JVM are required to support UTF-8 + e.printStackTrace(); + return null; + } + } - byte[] buf = new byte[8192]; + /** + * Zip the data and then encode it into Base64. + * + * @param data + * the data + * + * @return the Base64 zipped version + * + * @throws IOException + * in case of I/O error + */ + public static String zip64(byte[] data) throws IOException { + // 1. compress + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try { + OutputStream out = new GZIPOutputStream(bout); + try { + out.write(data); + } finally { + out.close(); + } + } finally { + data = bout.toByteArray(); + bout.close(); + } - int c = 0; - while ((c = in.read(buf, 0, buf.length)) > 0) { - out.write(buf, 0, c); + // 2. base64 + InputStream in = new ByteArrayInputStream(data); + try { + in = new Base64InputStream(in, true); + return new String(IOUtils.toByteArray(in), "UTF-8"); + } finally { + in.close(); } - out.flush(); - in.close(); + } + + /** + * Unconvert from Base64 then unzip the content, which is assumed to be a + * String. + * + * @param data + * the data in Base64 format + * + * @return the raw data + * + * @throws IOException + * in case of I/O error + */ + public static String unzip64s(String data) throws IOException { + return new String(unzip64(data), "UTF-8"); + } - fileString = new String(Base64.getEncoder().encode(out.toByteArray())); - out.close(); + /** + * Unconvert from Base64 then unzip the content. + * + * @param data + * the data in Base64 format + * + * @return the raw data + * + * @throws IOException + * in case of I/O error + */ + public static byte[] unzip64(String data) throws IOException { + InputStream in = new Base64InputStream(new ByteArrayInputStream( + getBytes(data)), false); + try { + in = new GZIPInputStream(in); + return IOUtils.toByteArray(in); + } finally { + in.close(); + } + } - return fileString; + /** + * Convert the given data to Base64 format. + * + * @param data + * the data to convert + * + * @return the Base64 {@link String} representation of the data + * + * @throws IOException + * in case of I/O errors + */ + public static String base64(String data) throws IOException { + return base64(getBytes(data)); } /** - * Convert the given Base64 representation of an image into an {@link Image} - * object. + * Convert the given data to Base64 format. * - * @param b64data - * the {@link Image} in Base64 format + * @param data + * the data to convert * - * @return the {@link Image} object + * @return the Base64 {@link String} representation of the data * * @throws IOException - * in case of IO error + * in case of I/O errors */ - static public BufferedImage toImage(String b64data) throws IOException { - ByteArrayInputStream in = new ByteArrayInputStream(Base64.getDecoder() - .decode(b64data)); - return toImage(in); + public static String base64(byte[] data) throws IOException { + Base64InputStream in = new Base64InputStream(new ByteArrayInputStream( + data), true); + try { + return new String(IOUtils.toByteArray(in), "UTF-8"); + } finally { + in.close(); + } } /** - * Convert the given {@link InputStream} (which must allow calls to - * {@link InputStream#reset()}) into an {@link Image} object. + * Unconvert the given data from Base64 format back to a raw array of bytes. * - * @param in - * the 'resetable' {@link InputStream} + * @param data + * the data to unconvert * - * @return the {@link Image} object + * @return the raw data represented by the given Base64 {@link String}, * * @throws IOException - * in case of IO error + * in case of I/O errors */ - static public BufferedImage toImage(InputStream in) throws IOException { - int orientation; + public static byte[] unbase64(String data) throws IOException { + Base64InputStream in = new Base64InputStream(new ByteArrayInputStream( + getBytes(data)), false); try { - orientation = getExifTransorm(in); - } catch (Exception e) { - // no EXIF transform, ok - orientation = -1; - } - - in.reset(); - BufferedImage image = ImageIO.read(in); - - if (image == null) { - throw new IOException("Failed to convert input to image"); - } - - // Note: this code has been found on internet; - // thank you anonymous coder. - int width = image.getWidth(); - int height = image.getHeight(); - AffineTransform affineTransform = new AffineTransform(); - - switch (orientation) { - case 1: - break; - case 2: // Flip X - affineTransform.scale(-1.0, 1.0); - affineTransform.translate(-width, 0); - break; - case 3: // PI rotation - affineTransform.translate(width, height); - affineTransform.rotate(Math.PI); - break; - case 4: // Flip Y - affineTransform.scale(1.0, -1.0); - affineTransform.translate(0, -height); - break; - case 5: // - PI/2 and Flip X - affineTransform.rotate(-Math.PI / 2); - affineTransform.scale(-1.0, 1.0); - break; - case 6: // -PI/2 and -width - affineTransform.translate(height, 0); - affineTransform.rotate(Math.PI / 2); - break; - case 7: // PI/2 and Flip - affineTransform.scale(-1.0, 1.0); - affineTransform.translate(-height, 0); - affineTransform.translate(0, width); - affineTransform.rotate(3 * Math.PI / 2); - break; - case 8: // PI / 2 - affineTransform.translate(0, width); - affineTransform.rotate(3 * Math.PI / 2); - break; - default: - affineTransform = null; - break; + return IOUtils.toByteArray(in); + } finally { + in.close(); + } + } + + /** + * Unonvert the given data from Base64 format back to a {@link String}. + * + * @param data + * the data to unconvert + * + * @return the {@link String} represented by the given Base64 {@link String} + * + * @throws IOException + * in case of I/O errors + */ + public static String unbase64s(String data) throws IOException { + return new String(unbase64(data), "UTF-8"); + } + + /** + * Return a display {@link String} for the given value, which can be + * suffixed with "k" or "M" depending upon the number, if it is big enough. + *
+ *
+ * Examples: + *
+ * Examples (assuming decimalPositions = 1): + *
+ * Of course, the conversion to and from display form is lossy (example: + * 6870 to "6.5k" to 6500). * - * @param input - * the input data + * @param value + * the value in display form with possible "M" and "k" suffixes, + * can be NULL * - * @return the hash + * @return the value as a number, or 0 if not possible to convert */ - static public String getHash(String input) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(input.getBytes()); - byte byteData[] = md.digest(); + public static long toNumber(String value) { + return toNumber(value, 0l); + } - StringBuffer hexString = new StringBuffer(); - for (int i = 0; i < byteData.length; i++) { - String hex = Integer.toHexString(0xff & byteData[i]); - if (hex.length() == 1) - hexString.append('0'); - hexString.append(hex); + /** + * The reverse operation to {@link StringUtils#formatNumber(long)}: it will + * read a "display" number that can contain a "M" or "k" suffix and return + * the full value. + *
+ * Of course, the conversion to and from display form is lossy (example:
+ * 6870 to "6.5k" to 6500).
+ *
+ * @param value
+ * the value in display form with possible "M" and "k" suffixes,
+ * can be NULL
+ * @param def
+ * the default value if it is not possible to convert the given
+ * value to a number
+ *
+ * @return the value as a number, or 0 if not possible to convert
+ */
+ public static long toNumber(String value, long def) {
+ long count = def;
+ if (value != null) {
+ value = value.trim().toLowerCase();
+ try {
+ long mult = 1;
+ if (value.endsWith("g")) {
+ value = value.substring(0, value.length() - 1).trim();
+ mult = 1000000000;
+ } else if (value.endsWith("m")) {
+ value = value.substring(0, value.length() - 1).trim();
+ mult = 1000000;
+ } else if (value.endsWith("k")) {
+ value = value.substring(0, value.length() - 1).trim();
+ mult = 1000;
+ }
+
+ long deci = 0;
+ if (value.contains(".")) {
+ String[] tab = value.split("\\.");
+ if (tab.length != 2) {
+ throw new NumberFormatException(value);
+ }
+ double decimal = Double.parseDouble("0."
+ + tab[tab.length - 1]);
+ deci = ((long) (mult * decimal));
+ value = tab[0];
+ }
+ count = mult * Long.parseLong(value) + deci;
+ } catch (Exception e) {
}
+ }
- return hexString.toString();
- } catch (NoSuchAlgorithmException e) {
- return input;
+ return count;
+ }
+
+ /**
+ * Return the bytes array representation of the given {@link String} in
+ * UTF-8.
+ *
+ * @param str
+ * the {@link String} to transform into bytes
+ * @return the content in bytes
+ */
+ static public byte[] getBytes(String str) {
+ try {
+ return str.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // All conforming JVM must support UTF-8
+ e.printStackTrace();
+ return null;
}
}
/**
- * Return the EXIF transformation flag of this image if any.
+ * The "remove accents" pattern.
*
+ * @return the pattern, or NULL if a problem happens
+ */
+ private static Pattern getMarks() {
+ try {
+ return Pattern
+ .compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
+ } catch (Exception e) {
+ // Can fail on Android...
+ return null;
+ }
+ }
+
+ //
+ // justify List
- * Note: this code has been found on internet; thank you anonymous coder.
- *
+ * Will automatically detect zipped data and also uncompress it before
+ * returning, unless ZIP is false.
*
- * @return the escaped {@link String}
+ * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
+ *
+ * @param data
+ * the data to unconvert
+ * @param zip
+ * TRUE to also uncompress the data from a GZIP format
+ * automatically; if set to FALSE, zipped data can be returned
+ *
+ * @return the raw data represented by the given Base64 {@link String},
+ * optionally compressed with GZIP
+ *
+ * @throws IOException
+ * in case of I/O errors
*/
- public static String xmlEscapeQuote(String input) {
- if (input == null) {
- return "";
+ @Deprecated
+ public static byte[] unbase64(String data, boolean zip) throws IOException {
+ byte[] buffer = unbase64(data);
+ if (!zip) {
+ return buffer;
}
- return HtmlEscape.escapeHtml(input,
- HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA,
- HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT);
+ try {
+ GZIPInputStream zipped = new GZIPInputStream(
+ new ByteArrayInputStream(buffer));
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ IOUtils.write(zipped, out);
+ return out.toByteArray();
+ } finally {
+ out.close();
+ }
+ } finally {
+ zipped.close();
+ }
+ } catch (Exception e) {
+ return buffer;
+ }
+ }
+
+ /**
+ * Unconvert the given data from Base64 format back to a raw array of bytes.
+ *
+ * Will automatically detect zipped data and also uncompress it before
+ * returning, unless ZIP is false.
+ *
+ * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
+ *
+ * @param data
+ * the data to unconvert
+ * @param zip
+ * TRUE to also uncompress the data from a GZIP format
+ * automatically; if set to FALSE, zipped data can be returned
+ *
+ * @return the raw data represented by the given Base64 {@link String},
+ * optionally compressed with GZIP
+ *
+ * @throws IOException
+ * in case of I/O errors
+ */
+ @Deprecated
+ public static InputStream unbase64(InputStream data, boolean zip)
+ throws IOException {
+ return new ByteArrayInputStream(unbase64(IOUtils.readSmallStream(data),
+ zip));
+ }
+
+ /**
+ * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
+ */
+ @Deprecated
+ public static byte[] unbase64(byte[] data, int offset, int count,
+ boolean zip) throws IOException {
+ byte[] dataPart = Arrays.copyOfRange(data, offset, offset + count);
+ return unbase64(new String(dataPart, "UTF-8"), zip);
+ }
+
+ /**
+ * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
+ */
+ @Deprecated
+ public static String unbase64s(String data, boolean zip) throws IOException {
+ return new String(unbase64(data, zip), "UTF-8");
+ }
+
+ /**
+ * @deprecated DO NOT USE ANYMORE (bad perf, will be dropped)
+ */
+ @Deprecated
+ public static String unbase64s(byte[] data, int offset, int count,
+ boolean zip) throws IOException {
+ return new String(unbase64(data, offset, count, zip), "UTF-8");
}
}