/* * * Copyright 2014 McEvoy Software Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.milton.common; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Handy functions exposes to rendering logic for formatting. * * @author brad */ public class Formatter { private static Logger log = LoggerFactory.getLogger(Formatter.class); public static final String CHECKBOX_SUFFIX = "_checkbox"; public static ThreadLocal<DateFormat> tlSdfUkShort = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("dd/MM/yyyy"); } }; public static ThreadLocal<DateFormat> tlSdfUkLong = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("dd MMMM yyyy"); } }; public static final ThreadLocal<DateFormat> sdfDateOnly = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("dd/MM/yyyy"); } }; public static final ThreadLocal<DateFormat> sdfDateAndTime = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("dd/MM/yyyy HH:mm"); } }; /** * Null safe method, returns empty string if the value is null * * @param o * @return */ public String toString(Object o) { if (o == null) { return ""; } else { return o.toString(); } } public Boolean toBool(Object o) { if (o == null) { return null; } else if (o instanceof Boolean) { return (Boolean) o; } else if (o instanceof Integer) { Integer i = (Integer) o; return i == 0; } else if (o instanceof String) { String s = (String) o; s = s.toLowerCase(); s = s.trim(); if (s.length() > 0) { return s.equals("true") || s.equals("yes"); } else { return null; } } else { throw new RuntimeException("Unsupported boolean type: " + o.getClass()); } } public BigDecimal toDecimal(Object o, int places) { if (o == null) { return BigDecimal.ZERO; } else if (o instanceof BigDecimal) { BigDecimal bd = (BigDecimal) o; return bd.setScale(places, RoundingMode.HALF_UP); } else if (o instanceof Double) { Double d = (Double) o; return BigDecimal.valueOf(d).setScale(places, RoundingMode.HALF_UP); } else if (o instanceof Integer) { Integer i = (Integer) o; return BigDecimal.valueOf(i.longValue()).setScale(places, RoundingMode.HALF_UP); } else if (o instanceof Float) { Float f = (Float) o; return BigDecimal.valueOf(f.doubleValue()).setScale(places, RoundingMode.HALF_UP); } else if (o instanceof String) { String s = (String) o; s = s.trim(); if (s.length() == 0) { return BigDecimal.ZERO; } else { try { return new BigDecimal(s).setScale(places, RoundingMode.HALF_UP); } catch (NumberFormatException numberFormatException) { throw new RuntimeException("Non-numeric data: " + s); } } } else { throw new RuntimeException("Unsupported value type, should be numeric: " + o.getClass()); } } public Double toDouble(Object o) { if (o == null) { return 0d; } else if (o instanceof String) { String s = (String) o; s = s.trim(); if (s.length() == 0) { return 0d; } else { try { return Double.valueOf(s); } catch (NumberFormatException numberFormatException) { throw new RuntimeException("Non-numeric data: " + s); } } } else if (o instanceof Double) { return (Double) o; } else if (o instanceof Integer) { Integer i = (Integer) o; return (double) i; } else if (o instanceof Float) { Float f = (Float) o; return f.doubleValue(); } else if (o instanceof BigDecimal) { BigDecimal bd = (BigDecimal) o; return bd.doubleValue(); } else { throw new RuntimeException("Unsupported value type, should be numeric: " + o.getClass()); } } public Long toLong(Object oLimit) { return toLong(oLimit, false); } public Long toLong(Object oVal, boolean withNulls) { Long limit; if (oVal == null) { limit = withNulls ? null : 0l; } else if (oVal instanceof Long) { limit = (Long) oVal; } else if (oVal instanceof Integer) { int i = (Integer) oVal; limit = (long) i; } else if (oVal instanceof Double) { Double d = (Double) oVal; return d.longValue(); } else if (oVal instanceof Float) { Float d = (Float) oVal; return d.longValue(); } else if (oVal instanceof BigDecimal) { BigDecimal bd = (BigDecimal) oVal; return bd.longValue(); } else if (oVal instanceof Boolean) { Boolean bb = (Boolean) oVal; return bb ? 1l : 0l; } else if (oVal instanceof String) { String s = (String) oVal; if (s.length() == 0) { limit = withNulls ? null : 0l; } else { if (s.equals("true") || s.equals("false")) { Boolean b = Boolean.parseBoolean(s); return toLong(b); } else { if (s.contains(".")) { Double d = toDouble(s); limit = d.longValue(); } else { limit = Long.parseLong(s); } } } } else { throw new RuntimeException("unsupported class: " + oVal.getClass()); } return limit; } public int getYear(Object o) { if (o == null || !(o instanceof Date)) { return 0; } Date dt = (Date) o; Calendar cal = Calendar.getInstance(); cal.setTime(dt); return cal.get(Calendar.YEAR); } public int getMonth(Object o) { if (o == null || !(o instanceof Date)) { return 0; } Date dt = (Date) o; Calendar cal = Calendar.getInstance(); cal.setTime(dt); return cal.get(Calendar.MONTH) + 1; } public int getDayOfMonth(Object o) { if (o == null || !(o instanceof Date)) { return 0; } Date dt = (Date) o; Calendar cal = Calendar.getInstance(); cal.setTime(dt); return cal.get(Calendar.DAY_OF_MONTH) + 1; } public String formatMinsAsDuration(Object o) { return formatMinsAsDuration(o, true); } /** * Given a value which can be parsed to a Long, return it formatted as a * human readable duration such as 12:30 (12 mins, 30 seconds) or 12 mins, 3 * hrs 20 * * @param o * @param numeric * @return */ public String formatMinsAsDuration(Object o, boolean numeric) { Long l = toLong(o); if (l == null) { return ""; } else { if (l == 0) { return ""; } long hours = l / 60; long mins = l % 60; if (numeric) { return hours + ":" + pad(mins, 2); } else { if (hours == 0) { return mins + "mins"; } else if (hours == 1) { return hours + "hr " + mins; } else { return hours + "hrs " + mins; } } } } public String pad2(long l) { return pad(l, 2); } public String pad(long l, int length) { return padWith("0", l, length); } public String padWith(String padChar, long l, int length) { return _pad(padChar, l + "", length); } private String _pad(String padChar, String val, int length) { if (val.length() >= length) { return val; } return _pad(padChar, padChar + val, length); } /** * Format as a percentage, including a percentage symbol and where * blank/null values result in a blank output * * @param num - the numerator * @param div - the divisor * @return */ public String toPercent(Object num, Object div) { return toPercent(num, div, true, true); } /** * * @param num * @param div * @param appendSymbol - if true the percentage symbol is appended if a * non-blank value * @param withBlanks - if true, blank numerators or divisors result in a * blank value. Otherwise return zero. * @return */ public String toPercent(Object num, Object div, boolean appendSymbol, boolean withBlanks) { Long lNum = toLong(num, true); Long lDiv = toLong(div, true); if (lDiv == null || lDiv == 0 || lNum == null) { if (withBlanks) { return ""; } else { return "0" + (appendSymbol ? "%" : ""); } } else { long perc = lNum * 100 / lDiv; return perc + (appendSymbol ? "%" : ""); } } /** * Removes the file extension if present * * Eg file1.swf -> file1 * * file1 -> file1 * * @param s * @return */ public String stripExt(String s) { if (s == null || s.length() == 0) { return ""; } return FileUtils.stripExtension(s); } /** * True if val1 is greater then val2 * * will do string conversions * * @param val1 * @param val2 * @return */ public boolean gt(Object val1, Object val2) { if (val1 == null) { return false; } if (val2 == null) { return true; } Double d1 = toDouble(val1); Double d2 = toDouble(val2); return d1 > d2; } public boolean lt(Object val1, Object val2) { if (val1 == null) { return false; } if (val2 == null) { return true; } Double d1 = toDouble(val1); Double d2 = toDouble(val2); return d1 < d2; } public boolean eq(Object val1, Object val2) { if (val1 == null) { return (val2 == null); } if (val2 == null) { return false; } Double d1 = toDouble(val1); Double d2 = toDouble(val2); return d1 == d2; } /** * Decode percentage encoded paths. Eg a%20b -> a b * * @param s * @return */ public String percentDecode(String s) { if (s == null) { return ""; } else if (s.length() == 0) { return ""; } return Utils.decodePath(s); } public String percentEncode(String s) { if( s == null ) { return null; } return Utils.percentEncode(s); } public Date toDate(Object oVal) { if (oVal == null) { return null; } else if (oVal instanceof Date) { return (Date) oVal; } else { if (oVal instanceof String) { String s = (String) oVal; return parseDate(s); } else { return null; } } } public java.sql.Date toSqlDate(Object oVal) { Date dt = toDate(oVal); if (dt == null) { return null; } else { return new java.sql.Date(dt.getTime()); } } public java.sql.Timestamp toSqlTimestamp(Object oVal) { Date dt = toDate(oVal); if (dt == null) { return null; } else { return new java.sql.Timestamp(dt.getTime()); } } public String toPlain(String html) { if (html == null) { return null; } html = replaceTag("br", html, "", "\n"); html = replaceTag("p", html, "", "\n"); html = replaceTag("b", html, "", ""); html = replaceTag("i", html, "", ""); html = replaceTag("h1", html, "", ""); html = replaceTag("h2", html, "", ""); html = replaceTag("h3", html, "", ""); return html; } private String replaceTag(String tag, String html, String replaceWithOpening, String replaceWithClosing) { html = html.replace("<" + tag + "/>", replaceWithClosing); // self closing html = html.replace("<" + tag + ">", replaceWithOpening); // opening tag html = html.replace("</" + tag + ">", replaceWithClosing); // closing tag return html; } public String getMonthName(int i) { switch (i) { case 0: return "January"; case 1: return "February"; case 2: return "March"; case 3: return "April"; case 4: return "May"; case 5: return "June"; case 6: return "July"; case 7: return "August"; case 8: return "September"; case 9: return "October"; case 10: return "November"; case 11: return "December"; default: return "Unknown month " + i; } } public String ifEqual(String ifEqual, String ifNoteEqual, Object o1, Object o2) { if (o1 == null) { return o2 == null ? ifEqual : ifNoteEqual; } else { return o1.equals(o2) ? ifEqual : ifNoteEqual; } } /** * This just permits simple templating syntax for basic conditional values * * Eg: <li><a class="$formatter.ifTrue($item.active, 'navActive', '')" * href="$item.href">$item.text</a></li> * * @param b * @param o1 * @param o2 * @return */ public Object ifTrue(Object bb, Object o1, Object o2) { Boolean b = toBool(bb); if (b == null) { b = Boolean.FALSE; } return b ? o1 : o2; } private Date parseDate(String s) { if (s == null || s.trim().length() == 0) { return null; } try { Date dt; if (s.contains(":")) { dt = sdf(true).parse(s); } else { dt = sdf(false).parse(s); } return dt; } catch (ParseException ex) { log.warn("couldnt parse date", ex); return null; // throw new RuntimeException(ex); } } public DateFormat sdf(boolean hasTime) { if (hasTime) { return sdfDateAndTime.get(); } else { return sdfDateOnly.get(); } } public BigDecimal toBigDecimal(Object o, int decimals) { if (o instanceof Integer) { Integer ii = (Integer) o; return new BigDecimal(ii); } else if (o instanceof Double) { Double dd = (Double) o; return new BigDecimal(dd).setScale(decimals, RoundingMode.HALF_UP); } else if (o instanceof Float) { Float ff = (Float) o; return new BigDecimal(ff); } else if (o instanceof String) { Double dd = toDouble(o); return toBigDecimal(dd, decimals); } else { log.warn("unhandled type: " + o.getClass()); return null; } } public String checkbox(String name, Object oChecked) { String s = checkbox(null, name, oChecked, "true"); return s; } public String checkbox(String id, String name, Object oChecked) { return checkbox(id, name, oChecked, "true"); } public String checkbox(String id, String name, Object oChecked, String value) { Boolean checked = toBool(oChecked); if (checked == null) { checked = Boolean.FALSE; } StringBuilder sb = new StringBuilder(); sb.append("<input type='hidden' value='' name='").append(name).append(CHECKBOX_SUFFIX).append("'/>"); sb.append("<input type=\"checkbox\""); sb.append(" name=\"").append(name).append("\" "); if (checked) { sb.append("checked=\"true\""); } appendValue(sb, value); if (id != null) { sb.append(" id=\"").append(id).append("\""); } sb.append(" />"); return sb.toString(); } public String radio(String id, String name, Object oChecked, String value) { Boolean checked = toBool(oChecked); if (checked == null) { checked = Boolean.FALSE; } StringBuilder sb = new StringBuilder("<input type=\"radio\""); sb.append(" name=\"").append(name).append("\""); if (checked) { sb.append(" checked=\"true\""); } appendValue(sb, value); if (id != null) { sb.append(" id=\"").append(id).append("\""); } sb.append(" />"); return sb.toString(); } /** * Generate an option element * * @return */ public String option(Object value, String text, Object currentValue) { StringBuilder sb = new StringBuilder("<option"); appendValue(sb, value); if (currentValue != null && currentValue.equals(value)) { sb.append("selected=\"true\""); } sb.append(">"); sb.append(text).append("</option>"); return sb.toString(); } private void appendValue(StringBuilder sb, Object value) { sb.append(" value="); sb.append("\""); if( value != null ) { sb.append(value.toString()); } sb.append("\""); } public String toCsv(Iterable list) { StringBuilder sb = new StringBuilder(); if (list != null) { for (Object o : list) { if (sb.length() > 0) { sb.append(","); } sb.append(o.toString()); } } return sb.toString(); } public String toCsv(String[] list) { StringBuilder sb = new StringBuilder(); if (list != null) { for (Object o : list) { if (sb.length() > 0) { sb.append(","); } sb.append(o.toString()); } } return sb.toString(); } /** * Return a date which has the given number of days added (or subtracted if * negative) to the given date * * @param now * @param i * @return */ public Date addDays(Date now, int days) { Calendar cal = Calendar.getInstance(); cal.setTime(now); cal.add(Calendar.DAY_OF_YEAR, days); return cal.getTime(); } }