Java tutorial
/* * Stallion Core: A Modern Web Framework * * Copyright (C) 2015 - 2016 Stallion Software LLC. * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation, either version 2 of * the License, or (at your option) any later version. This program is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. * * * */ package io.stallion.utils; import com.fasterxml.jackson.core.JsonProcessingException; import io.stallion.Context; import io.stallion.dataAccess.Model; import io.stallion.services.Log; import io.stallion.users.Role; import io.stallion.utils.json.JSON; import io.stallion.utils.json.RestrictedViews; import org.apache.commons.codec.binary.Base32; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.http.client.utils.URLEncodedUtils; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.security.SecureRandom; import java.text.Normalizer; import java.text.NumberFormat; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; import static io.stallion.utils.Literals.UTF8; import static io.stallion.utils.Literals.map; import static io.stallion.utils.Literals.val; public class GeneralUtils { public static final Object NULL = null; public static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("MMM d, YYYY h:mm a"); public static final DateTimeFormatter SLUG_FORMAT = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmm-ssSS"); public static final DateTimeFormatter ISO_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); public static final DateTimeFormatter SQL_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static final ZoneId UTC = ZoneId.of("UTC"); private static final Pattern NONLATIN = Pattern.compile("[^\\w-]"); private static final Pattern WHITESPACE = Pattern.compile("[\\s]"); private static final Pattern MULTIHYPHENS = Pattern.compile("\\-\\-+"); // Because probeContentType doesn't work on all platforms; private static final Map<String, String> mimeTypes = map(val("css", "text/css"), val("js", "text/javascript"), val("tag", "text/javascript"), val("woff", "application/font-woff"), val("otf", "application/octet-stream"), val("eot", "application/octet-stream"), val("ttf", "application/octet-stream"), val("map", "application/json"), val("json", "application/json")); /** * Converts the string into a string containing only hyphens, lower-case letters, and numbers, removing all * other characters. * * @param input * @return */ public static String slugify(String input) { String nowhitespace = WHITESPACE.matcher(input).replaceAll("-"); String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); String slug = MULTIHYPHENS.matcher(NONLATIN.matcher(normalized).replaceAll("-")).replaceAll("-"); return slug.toLowerCase(Locale.ENGLISH); } public static String truncateSmart(String s, int length) { return Literals.truncateSmart(s, length); } public static String formatCurrency(Double amt) { NumberFormat formatter = NumberFormat.getCurrencyInstance(); return formatter.format(amt); } public static String formatCurrency(Float amt) { NumberFormat formatter = NumberFormat.getCurrencyInstance(); return formatter.format(amt); } public static String urlEncode(String s) { try { return URLEncoder.encode(s, "utf-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(s); } } public static String guessMimeType(String path) { return mimeTypes.getOrDefault(FilenameUtils.getExtension(path), null); } public static String md5Hash(String val) { try { java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5"); byte[] array = md.digest(val.getBytes()); StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length; ++i) { sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); } return sb.toString(); } catch (java.security.NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static boolean isValidEmailAddress(String email) { boolean result = true; try { InternetAddress emailAddr = new InternetAddress(email); emailAddr.validate(); } catch (AddressException ex) { result = false; } return result; } // DEPRECATED methods @Deprecated public static ZonedDateTime utcNow() { return ZonedDateTime.now(UTC); } @Deprecated public static ZonedDateTime localNow() { return ZonedDateTime.now(Context.getSettings().getTimeZoneId()); } /* Current milliseconds since the epoch */ @Deprecated public static long mils() { return new Date().getTime(); } /* Epoch milliseconds to a ZonedDateTime */ @Deprecated public static ZonedDateTime milsToDateTime(long mils) { return ZonedDateTime.ofInstant(Instant.ofEpochMilli(mils), UTC); } @Deprecated public static String formatNow(String format) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); return utcNow().format(formatter); } /* We have to add this generic Object overload, because Method typing dispatching * does not work correctly when called from the templates. So instead we have to * include the type in the name of each function. Blech. * */ @Deprecated public static String formatLocalDate(Object dt, String formatPattern) { if (dt instanceof Long) { return formatLocalDateFromLong((Long) dt, formatPattern); } else if (dt instanceof ZonedDateTime) { return formatLocalDateFromZonedDate((ZonedDateTime) dt, formatPattern); } else if (dt instanceof Date) { return formatLocalDateFromJDate((Date) dt, formatPattern); } return ""; } @Deprecated public static String formatLocalDate(Object dt) { return formatLocalDate(dt, null); } @Deprecated public static String formatLocalDateFromJDate(Date date) { return formatLocalDateFromJDate(date, null); } @Deprecated public static String formatLocalDateFromJDate(Date date, String formatPattern) { if (date == null) { return ""; } return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(date.toInstant(), UTC), formatPattern); } @Deprecated public static String formatLocalDateFromLong(long epochMillis) { return formatLocalDateFromLong(epochMillis, null); } @Deprecated public static String formatLocalDateFromLong(long epochMillis, String formatPattern) { if (epochMillis == 0L) { return ""; } return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC), formatPattern); } @Deprecated public static String formatLocalDateFromLong(Long epochMillis) { return formatLocalDateFromLong(epochMillis, null); } @Deprecated public static String formatLocalDateFromLong(Long epochMillis, String formatPattern) { if (epochMillis == 0L || epochMillis == null) { return ""; } return formatLocalDateFromZonedDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC), formatPattern); } @Deprecated public static String formatLocalDateFromZonedDate(ZonedDateTime date) { return formatLocalDateFromZonedDate(date, null); } /** * Turn the given long id into a random base32 string token. This can be used for generating unique, secret strings * for accessing data, such as a web page only viewable by a secret string. By using the long id, of the * underlying object we guarantee uniqueness, by adding on random characters, we make the URL nigh * impossible to guess. * * @param id - a long id that will be convered to base32 and used as the first part of the string * @param length - the number of random base32 characters to add to the end of the string * @return */ public static String tokenForId(Long id, int length) { ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.putLong(id); return StringUtils.stripStart(new Base32().encodeAsString(buffer.array()), "A").replace("=", "") .toLowerCase() + "8" + randomTokenBase32(length); } /** * Psuedo-random string of the given length, in base32 characters * @param length * @return */ public static String randomTokenBase32(int length) { byte[] r = new byte[256]; //Means 2048 bit new Random().nextBytes(r); String s = new Base32().encodeAsString(r).substring(0, length).toLowerCase(); return s; } /** * Generates a random string using the SecureRandom module, of the given length, * using URL safe base64 characters * * @param length * @return */ public static String secureRandomToken(int length) { SecureRandom random = new SecureRandom(); byte bytes[] = new byte[length * 4]; random.nextBytes(bytes); String s = Base64.encodeBase64URLSafeString(bytes).substring(0, length); return s; } public static String secureRandomNumeric(int length) { SecureRandom test = new SecureRandom(); int result = test.nextInt(((Double) Math.pow(10, length)).intValue()); String resultStr = result + ""; if (resultStr.length() != length) for (int x = resultStr.length(); x < length; x++) resultStr = "0" + resultStr; return resultStr; } /** * Generates a random string using the psuedo-random module, of the given length, * using URL safe base64 characters * * @param length * @return */ public static String randomToken(int length) { byte[] r = new byte[256]; //Means 2048 bit new Random().nextBytes(r); String s = Base64.encodeBase64URLSafeString(r).substring(0, length); return s; } @Deprecated public static String formatLocalDateFromZonedDate(ZonedDateTime date, String formatPattern) { if (date == null) { return ""; } ZonedDateTime localDt = date.withZoneSameInstant(Context.getSettings().getTimeZoneId()); DateTimeFormatter formatter; if (StringUtils.isEmpty(formatPattern)) { formatter = DEFAULT_FORMAT; } else if ("iso".equals(formatPattern.toLowerCase())) { formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; } else { formatter = DateTimeFormatter.ofPattern(formatPattern); } return localDt.format(formatter); } @Deprecated public static String slugifyDate(Long epochMillis) { return slugifyDate(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), UTC)); } @Deprecated public static String slugifyDate(ZonedDateTime date) { return date.format(SLUG_FORMAT); } public static Object htmlSafeJson(Object obj) { String out = JSON.stringify(obj); out = out.replace("<", "\\u003c"); return out; } /** * Gets the object in a JSON form that is safe for being outputted on a web page: * <script> * var myObj = {{ utils.htmlSafeJson(obj, "member") }} * <script> * @param obj * @param restrictionLevel - Uses the JsonView annotation to determine which properties of the object * should be outputed. Possible values are: unrestricted/public/member/owner/internal * @return */ @Deprecated public static Object htmlSafeJson(Object obj, String restrictionLevel) { String out = ""; try { restrictionLevel = restrictionLevel == null ? "public" : restrictionLevel.toLowerCase(); if ("public".equals(restrictionLevel)) { out = JSON.stringify(obj, RestrictedViews.Public.class, true); } else if ("unrestricted".equals(restrictionLevel)) { out = JSON.stringify(obj, RestrictedViews.Unrestricted.class, false); } else if ("member".equals(restrictionLevel)) { out = JSON.stringify(obj, RestrictedViews.Member.class, true); } else if ("owner".equals(restrictionLevel)) { out = JSON.stringify(obj, RestrictedViews.Owner.class, true); } else if ("internal".equals(restrictionLevel)) { out = JSON.stringify(obj, RestrictedViews.Internal.class, true); } else { out = "Unknown restriction level: " + restrictionLevel; } } catch (JsonProcessingException ex) { String objId = obj.toString(); if (obj instanceof Model) { objId = ((Model) obj).getId().toString(); } String msg = "Error JSON.stringifying object {0}" + obj.getClass().getSimpleName() + ":" + objId; if (Context.getSettings().getDebug() || Context.getUser().isInRole(Role.ADMIN)) { out = msg + "\n\nStacktrace-----\n\n" + ExceptionUtils.getStackTrace(ex); } Log.exception(ex, msg); } out = out.replace("<", "\\u003c"); return out; } }