/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.util; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SimpleTimeZone; import java.util.StringTokenizer; import java.util.regex.Pattern; import javax.print.attribute.Size2DSyntax; import javax.print.attribute.standard.MediaSize; import org.apache.commons.codec.binary.Base64; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeError; import org.mozilla.javascript.NativeJavaMethod; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import org.mozilla.javascript.xml.XMLObject; import com.servoy.j2db.IApplication; import com.servoy.j2db.IEventDelegator; import com.servoy.j2db.IServiceProvider; import com.servoy.j2db.MediaURLStreamHandler; import com.servoy.j2db.dataprocessing.FoundSet; import com.servoy.j2db.dataprocessing.IDisplayData; import com.servoy.j2db.dataprocessing.Record; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.FormElementGroup; import com.servoy.j2db.persistence.IColumnTypes; import com.servoy.j2db.persistence.IFormElement; import com.servoy.j2db.persistence.IPersist; import com.servoy.j2db.persistence.IRepository; import com.servoy.j2db.persistence.ISupportBounds; import com.servoy.j2db.persistence.ISupportExtendsID; import com.servoy.j2db.scripting.IScriptable; import com.servoy.j2db.scripting.IScriptableProvider; import com.servoy.j2db.ui.runtime.HasRuntimeClientProperty; import com.servoy.j2db.util.docvalidator.IdentDocumentValidator; import de.rtner.security.auth.spi.PBKDF2Engine; import de.rtner.security.auth.spi.PBKDF2HexFormatter; import de.rtner.security.auth.spi.PBKDF2Parameters; /** * Utility methods * Normal Use: static methods <br> * * @author jblok */ public final class Utils { /** * The password hash prefix if it is the new PBKDF2 password or a md5 hash. */ private static final String PBKDF2_PREFIX = "PBKDF2:"; //$NON-NLS-1$ // Client platforms public static final int PLATFORM_OTHER = 0; public static final int PLATFORM_WINDOWS = 1; public static final int PLATFORM_MAC = 2; public static final int PLATFORM_LINUX = 3; public static boolean isInheritedFormElement(Object element, IPersist context) { if (element instanceof Form) { return false; } if (context instanceof Form && element instanceof IPersist && (((IPersist)element).getAncestor(IRepository.FORMS) != context)) { if (element instanceof IPersist && (((IPersist)element).getAncestor(IRepository.FORMS) != context)) { // child of super-form, readonly return true; } } if (element instanceof FormElementGroup) { Iterator<IFormElement> elements = ((FormElementGroup)element).getElements(); while (elements.hasNext()) { if (isInheritedFormElement(elements.next(), context)) { return true; } } } if (element instanceof ISupportExtendsID) { return PersistHelper.isOverrideElement((ISupportExtendsID)element); } // child of this form, not of a inherited form return false; } /** * Change the passed class name to its corresponding file name. E.G. change "Utilities" to "Utilities.class". * * @param name Class name to be changed. * * @throws IllegalArgumentException If a null <TT>name</TT> passed. */ public static String changeClassNameToFileName(String name) { if (name == null) { throw new IllegalArgumentException("Class Name == null"); //$NON-NLS-1$ } return name.replace('.', '/').concat(".class"); //$NON-NLS-1$ } /** * Change the passed file name to its corresponding class name. E.G. change "Utilities.class" to "Utilities". * * @param name Class name to be changed. If this does not represent a Java class then <TT>null</TT> is returned. * * @throws IllegalArgumentException If a null <TT>name</TT> passed. */ public static String changeFileNameToClassName(String name) { if (name == null) { throw new IllegalArgumentException("File Name == null"); //$NON-NLS-1$ } String className = null; if (name.toLowerCase().endsWith(".class")) { //$NON-NLS-1$ className = name.replace('/', '.'); className = className.replace('\\', '.'); className = className.substring(0, className.length() - 6); } return className; } /** * Insert an array into another array at a certain position. Both arrays may be null, resulting array will be extended to fit. Element type will be * preserved. * * @param src * @param toAdd * @param position * @param n * @return the resulting array */ public static <T> T[] arrayInsert(T[] src, Object[] toAdd, int position, int n) { if (src == null && toAdd == null) { // nothing to add return null; } T[] res; if (src == null) { res = (T[])java.lang.reflect.Array.newInstance(toAdd.getClass().getComponentType(), position + n); System.arraycopy(toAdd, 0, res, position, Math.min(toAdd.length, n)); } else { res = (T[])java.lang.reflect.Array.newInstance(src.getClass().getComponentType(), Math.max(src.length, position) + n); if (position > 0 && src.length > 0) { System.arraycopy(src, 0, res, 0, Math.min(src.length, position)); } if (position < src.length) { System.arraycopy(src, position, res, position + n, src.length - position); } if (toAdd != null) { System.arraycopy(toAdd, 0, res, position, Math.min(toAdd.length, n)); } } return res; } /** * Join 2 arrays into 1. Element type will be preserved. * * @param array1 * @param array2 * @return the resulting array */ public static <T> T[] arrayJoin(T[] array1, Object[] array2) { if (array1 == null || (array1.length == 0 && array2 != null)) { return (T[])array2; } if (array2 == null || array2.length == 0) { return array1; } return arrayInsert(array1, array2, array1.length, array2.length); } /** * Add an element to an array. Element type will be preserved. * * @param array * @param element * @param append * @return the resulting array */ public static <T> T[] arrayAdd(T[] array, T element, boolean append) { T[] res; if (array == null) { res = (T[])java.lang.reflect.Array.newInstance(element == null ? Object.class : element.getClass(), 1); } else { res = (T[])java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), array.length + 1); System.arraycopy(array, 0, res, append ? 0 : 1, array.length); } res[append ? res.length - 1 : 0] = element; return res; } /** * Merge two arrays in 1, the upperArray will be overlaid onto the lowerArray. * * <p> * For example: * * <br> * upper = [x, y] lower = [a,b,c] => overlaid = [x, y, c] * * <br> * upper = [a, b c] lower = [x, y] => overlaid = [a, b, c] * * @param upperAarray * @param lowerAarray */ public static <T> T[] arrayMerge(T[] upperAarray, T[] lowerAarray) { if (upperAarray == null) { return lowerAarray; } if (lowerAarray == null || lowerAarray.length <= upperAarray.length) { return upperAarray; } // both arrays filled and lowerArray is longer than upperArray T[] mergedArgs = (T[])java.lang.reflect.Array.newInstance(upperAarray.getClass().getComponentType(), lowerAarray.length); System.arraycopy(upperAarray, 0, mergedArgs, 0, upperAarray.length); System.arraycopy(lowerAarray, upperAarray.length, mergedArgs, upperAarray.length, lowerAarray.length - upperAarray.length); return mergedArgs; } public static <T> List<T> asList(Iterator< ? extends T> it) { List<T> lst = new ArrayList<T>(); while (it.hasNext()) { lst.add(it.next()); } return lst; } public static <T> T[] asArray(Iterator< ? extends T> it, Class<T> clazz) { List<T> lst = asList(it); return lst.toArray((T[])java.lang.reflect.Array.newInstance(clazz, lst.size())); } public static <T> Iterator<T> asSortedIterator(Iterator< ? extends T> it, Comparator< ? super T> comparator) { Object[] array = asList(it).toArray(); Arrays.sort(array, (Comparator<Object>)comparator); return (Iterator<T>)Arrays.asList(array).iterator(); } /** * Get a sub-array. Element type will be preserved. * * @param array * @param beginIndex * @param endIndex * @return the resulting array */ public static <T> T[] arraySub(T[] array, int beginIndex, int endIndex) { if (array == null || (beginIndex == 0 && endIndex == array.length)) { return array; } if (beginIndex > endIndex) { throw new IllegalArgumentException("arraySub: " + beginIndex + '>' + endIndex); //$NON-NLS-1$ } T[] res = (T[])java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), endIndex - beginIndex); System.arraycopy(array, beginIndex, res, 0, endIndex - beginIndex); return res; } /* * _____________________________________________________________ Declaration of attributes */ /* * _____________________________________________________________ Declaration and definition of constructors */ private Utils() { } /* * _____________________________________________________________ The methods below belong to this class */ /** * Try to parse the given string as a long * * @param s the string to parse * @return the parsed long - or 0 (zero) if the parse doesn't succeed */ public static long getAsLong(String s) { return getAsLong(s, false); } /** * Try to parse the given object as a long * * @param o the object (String, Number, ...) to parse * @return the parsed long - or 0 (zero) if the parse doesn't succeed */ public static long getAsLong(Object o) { return getAsLong(o, false); } /** * Try to parse the given string as a long * * @param s the string to parse * @param throwOnException whether or not to throw a RuntimeException on failure * @return the parsed long - or 0 (zero) if the parse doesn't succeed and throwOnException is false */ public static long getAsLong(String s, boolean throwOnException) { if (s == null) return 0l; try { // Note: very big longs may parse incorrectly due to precision loss (should not be a problem in the Servoy context). return new Double(s.replace(',', '.')).longValue(); } catch (Exception ex) { if (throwOnException) { if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex.getMessage()); } else { return 0l; } } } /** * Try to parse the given object as a long * * @param o the Object (Number, String, ...) to parse * @param throwOnException whether or not to throw a RuntimeException on failure * @return the parsed long - or 0 (zero) if the parse doesn't succeed and throwOnException is false */ public static long getAsLong(Object o, boolean throwOnException) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).longValue(); } if (o instanceof Boolean) { return ((Boolean)o).booleanValue() ? 1 : 0; } return getAsLong(o.toString(), throwOnException); } /** * Try to parse the given string as an integer * * @param s the string to parse * @return the parsed integer - or 0 (zero) if the parse doesn't succeed */ public static int getAsInteger(String s) { if (s == null) return 0; try { return new Double(s.replace(',', '.')).intValue(); } catch (Exception ex) { return 0; } } /** * Try to parse the given string as an integer * * @param s the string to parse * @param throwOnException whether or not to throw a RuntimeException on failure * @return the parsed integer - or 0 (zero) if the parse doesn't succeed and throwOnException is false * @throws RuntimeException in case of trouble when throwOnException is true */ public static int getAsInteger(String s, boolean throwOnException) { if (s == null) return 0; try { return new Double(s.replace(',', '.')).intValue(); } catch (Exception ex) { if (throwOnException) { if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex.getMessage()); } else { return 0; } } } /** * Try to parse the given string as a double * * @param s the string to parse * @return the parsed double - or 0 (zero) if the parse doesn't succeed */ public static double getAsDouble(String s) { if (s == null) return 0; try { return new Double(s.replace(',', '.')).doubleValue(); } catch (Exception ex) { return 0; } } /** * Try to parse the given string as a double * * @param s the string to parse * @param throwOnFail whether or not to throw a RuntimeException on failure * @return the parsed double - or 0 (zero) if the parse doesn't succeed and throwOnFail is false */ public static double getAsDouble(String s, boolean throwOnFail) { if (s == null) return 0; try { return new Double(s.replace(',', '.')).doubleValue(); } catch (Exception ex) { if (throwOnFail) { if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex.getMessage()); } else { return 0; } } } /* * public static String getAsMoney(String s) { NumberFormat nf = NumberFormat.getCurrencyInstance(); return nf.format(getAsDouble(s)); } */ /** * Try to parse the given string as a boolean * * @param s the string to parse * @return the boolean (false if the parse doesn't succeed) */ public static boolean getAsBoolean(String s) { if (s == null) return false; s = s.toLowerCase(); boolean retval = false; if (s.startsWith("1")) //$NON-NLS-1$ { retval = true; } else if (s.startsWith("y")) //$NON-NLS-1$ { retval = true; } else if (s.startsWith("t")) //$NON-NLS-1$ { retval = true; } return retval; } /** * Try to parse the given object as a boolean * * @param o the object (Boolean, Number, String, ...) to parse * @return the boolean (false if the parse doesn't succeed) */ public static boolean getAsBoolean(Object o) { if (o == null) return false; if (o instanceof Boolean) { return ((Boolean)o).booleanValue(); } else if (o instanceof Number) { return (((Number)o).intValue() > 0); } else { return getAsBoolean(o.toString()); } } public static BigDecimal roundNumber(Object number, int precision, boolean throwOnFail) { MathContext mathContext = new MathContext(precision); if (number instanceof BigDecimal) { return ((BigDecimal)number).round(mathContext); } return new BigDecimal(Utils.getAsDouble(number, throwOnFail)).round(mathContext); } /** * Format a given number (Visitor (locale) specific) * * @param param the input number * @param digits number of digits * @return the formatted number */ public static String formatNumber(Locale locale, String param, String digits) { return formatNumber(locale, getAsDouble(param), getAsInteger(digits)); } /** * Checks whether the <code>string</code> is considered empty. Empty means that the string may contain whitespace, but no visible characters. * * "\n\t " is considered empty, while " a" is not. * * @param str the string * @return true if the string is null or "" */ public static boolean stringIsEmpty(final CharSequence str) { return str == null || str.length() == 0 || str.toString().trim().length() == 0; } public static CharSequence stringLimitLenght(final CharSequence str, int length) { if (str == null) return null; return (str.length() > length ? str.subSequence(0, length) : str); } public static boolean stringContainsIgnoreCase(final CharSequence source, String contains) { if (stringIsEmpty(source) || stringIsEmpty(contains)) return false; return (source.toString().toLowerCase().contains(contains.toLowerCase())); } /** * Compares two strings no matter if they are null * * @param left string * @param right string * @return true if they are the same */ public static boolean stringSafeEquals(String left, String right) { return safeEquals(left, right); } /** * Compares two objects no matter if they are null * * @param left object * @param right object * @return true if they are the same */ public static boolean safeEquals(Object left, Object right) { if (left == null) { return right == null; } return left.equals(right); } /** * Format a given number (Visitor (locale) specific) * * @param param the input number * @param digits number of digits * @return the formatted number */ public static String formatNumber(Locale locale, double param, int digits) { RoundHalfUpDecimalFormat nf = new RoundHalfUpDecimalFormat(locale); int digits_nr = digits; nf.setMaximumFractionDigits(digits_nr); nf.setMinimumFractionDigits(digits_nr); return nf.format(param); } /** * Format a given number of miliseconds in a formatted time * * Note:if the time (in milliseconds) is smaller than ~month, it is calulated without a timezone) * * @param msec the miliseconds (current time can be get by 'new java.util.Date().getTime()') * @param format the display format (format used from java.text.SimpleDateFormat!) * @return the formatted time * @see java.text.SimpleDateFormat */ public static String formatTime(int msec, String format) { return formatTime((long)msec, format); } /** * Format a given number of miliseconds in a formatted time * * Note:if the time (in milliseconds) is smaller than ~month, it is calulated without a timezone) * * @param msec the miliseconds (current time can be get by 'new java.util.Date().getTime()') * @param format the display format * @return the formatted time * @see java.text.SimpleDateFormat */ public static String formatTime(long msec, String format) { Date d = new Date(msec); SimpleDateFormat sdf = new SimpleDateFormat(format == null ? "yyyy.MM.dd G 'at' hh:mm:ss z" : format); //$NON-NLS-1$ if (msec < 2678400000L && msec >= 0) //if smaller than a ~month { //format the time without timezone (GMT +0) //now it is possible to format for example telephone calling seconds to a formatted time (hh:mm:ss) //otherwise the timezone is involed sdf.setTimeZone(new SimpleTimeZone(0, "GMT")); //$NON-NLS-1$ } return sdf.format(d); } /** * Create a Date (time) from a String, returns null on failure<br> * Example: Timestamp t = Utils.parseDate("23-06-1975 6:08 AM", "dd-MM-yyyy hh:mm a"); <br> * * @param datetime the date as formatted string * @param format the format to be used * @return the Timestamp object * @see java.text.SimpleDateFormat */ public static Timestamp parseDate(String datetime, String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); Date d = sdf.parse(datetime, new ParsePosition(0)); return new Timestamp(d.getTime()); } /** * Parse a javascript string into a java string, example parseJSString("'HelloWorld'") returns:HelloWorld * @param o * * @return the parsed object */ public static Object parseJSExpression(Object o) { return parseJSExpression(o, Types.OTHER); } /** * The same as parseJSExpression but try to convert the object to the given type parameter * @param type from java.sql.Types , java.sql.Types.OTHER to get the behavior of parseJSExpression(Object o) */ public static Object parseJSExpression(Object o, int type) { if (o instanceof String) { int tp = Column.mapToDefaultType(type); String s = ((String)o).trim(); if ("".equals(s)) return null; if (tp == Types.OTHER || type == Types.BOOLEAN || type == Types.BIT) { if ("true".equals(s)) return Boolean.TRUE; if ("false".equals(s)) return Boolean.FALSE; if (type == Types.BOOLEAN || type == Types.BIT) return null; } if (tp == Types.OTHER || tp == IColumnTypes.NUMBER) { try { return Double.valueOf(s); } catch (NumberFormatException e) { if (tp != Types.OTHER) return null; } } if (tp == IColumnTypes.INTEGER) { try { return Integer.valueOf(s); } catch (NumberFormatException e) { if ("true".equals(s)) return Boolean.TRUE; if ("false".equals(s)) return Boolean.FALSE; return null; } } if (tp == Types.OTHER || tp == IColumnTypes.TEXT) { if ((s.charAt(0) == '\'' && s.charAt(s.length() - 1) == '\'') || (s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"')) { return s.substring(1, s.length() - 1); } } return null; } // non-string, keep original return o; } public static Object[] parseJSExpressions(List<Object> exprs) { if (exprs == null) return null; Object[] parsed = new Object[exprs.size()]; for (int i = 0; i < parsed.length; i++) { parsed[i] = parseJSExpression(exprs.get(i)); } return parsed; } /** * Represent a primitive as js string. * Booleans and Numbers are converted to their string representation. * Strings are quoted with single quotes. * * @see #parseJSExpression(Object) reverse method * * @param o * @return the result string */ public static String makeJSExpression(Object o) { if (o instanceof CharSequence) { return '\'' + o.toString().replaceAll("'", "\\\\$0") + '\''; //$NON-NLS-1$ //$NON-NLS-2$ } return o == null ? null : o.toString(); } //exact word match public static String stringReplaceExact(String org, String source, String destination) { return stringReplace(org, source, destination, -1, true, false); } /** * Method for replacing part of a string with a string * * @param org the orginal string * @param source the string to search * @param destination the string to replace * @return the result */ public static String stringReplace(String org, String source, String destination) { return stringReplace(org, source, destination, -1, false, false); } public static String stringReplaceCaseInsensitiveSearch(String org, String source, String destination) { return stringReplace(org, source, destination, -1, false, true); } public static String stringReplace(String org, String source, String destination, int replaceOccurence) { return stringReplace(org, source, destination, replaceOccurence, false, false); } public static String stringReplace(String org, String source, String destination, int replaceOccurence, boolean mustExact, boolean caseInsensitiveSearch) { if (org == null) return null; if (source == null || source.length() == 0) return org; String searchOrg = org; if (caseInsensitiveSearch) { searchOrg = org.toLowerCase(); source = source.toLowerCase(); } int index = searchOrg.indexOf(source); if (index != -1) { int occurence = 0; StringBuilder sb = new StringBuilder(); int startIndex = 0; boolean isExact = true; while (index != -1) { if (mustExact) { //check left if (index > 0) { isExact = !Character.isJavaIdentifierStart(org.charAt(index - 1)); } //check right if ((index + source.length() < org.length()) && isExact) { isExact = !Character.isJavaIdentifierStart(org.charAt(index + source.length())); } } sb.append(org.substring(startIndex, index)); if (mustExact) { if (isExact) { sb.append(destination); } else { sb.append(source); } } else { if (replaceOccurence < 0 || replaceOccurence == occurence) { sb.append(destination); } else { sb.append(source); } } startIndex = index + source.length(); index = searchOrg.indexOf(source, startIndex); occurence++; } sb.append(org.substring(startIndex, org.length()));//add tail return sb.toString(); } else { return org; } } public static String stringReplaceRecursive(String org, String source, String destination) { if (org == null) return null; if (source.length() == 0) return org; while (org.indexOf(source) != -1) { org = stringReplace(org, source, destination); } return org; } /** * Removes all substrings between '<' and corresponding '>'. If the number of '<' characters differs from the number of '>' characters, the behavior is * undetermined. * * @param str the initial string. * @return a string equal to the given string from which all substrings between '<' and corresponding '>' were removed. */ public static String stringRemoveTags(String str) { if (str == null) return null; StringBuilder sb = new StringBuilder(str.length()); int opened = 0; char ch; for (int i = 0; i < str.length(); i++) { ch = str.charAt(i); if (ch == '<') { opened++; } else { if (opened == 0) { sb.append(ch); } else if (ch == '>') { opened--; } } } return sb.toString(); } /** * Escape quotes in a string, handle already escaped quotes * * @param str * @param quote */ public static String stringEscapeQuote(String str, char quote) { if (str == null || ((str.indexOf('\\') < 0) && str.indexOf(quote) < 0)) return str; StringBuilder sb = new StringBuilder(str.length() + 10); for (char c : str.toCharArray()) { if (c == '\\' || c == quote) { sb.append('\\'); } sb.append(c); } return sb.toString(); } public static String stringJoin(Object[] array, char separator) { if (array == null) return null; return stringJoin(Arrays.asList(array).iterator(), separator); } public static String stringJoin(Object[] array, String separator) { if (array == null) return null; return stringJoin(Arrays.asList(array).iterator(), separator); } /** * <p> * Joins the elements of the provided <code>Iterator</code> into a single String containing the provided elements. * </p> * * <p> * No delimiter is added before or after the list. Null objects or empty strings within the iteration are represented by empty strings. * </p> * * @param iterator the <code>Iterator</code> of values to join together, may be null * @param separator the separator character to use * @return the joined String, <code>null</code> if null iterator input */ public static String stringJoin(Iterator< ? > iterator, char separator) { if (iterator == null) { return null; } StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small while (iterator.hasNext()) { Object obj = iterator.next(); if (obj != null) { buf.append(obj); } if (iterator.hasNext()) { buf.append(separator); } } return buf.toString(); } public static String stringJoin(Iterator< ? > iterator, String separator) { if (iterator == null) { return null; } StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small while (iterator.hasNext()) { Object obj = iterator.next(); if (obj != null) { buf.append(obj); } if (iterator.hasNext()) { buf.append(separator); } } return buf.toString(); } /** * Converts a Java String to an HTML markup string, but does not convert normal spaces to non-breaking space entities (<nbsp>). * * @param s The string to be escaped * @see Utils#escapeMarkup(String, boolean) * @return The escaped string */ @Deprecated public static CharSequence escapeMarkup(final String s) { return HtmlUtils.escapeMarkup(s); } /** * Converts a Java String to an HTML markup String by replacing illegal characters with HTML entities where appropriate. Spaces are converted to * non-breaking spaces (<nbsp>) if escapeSpaces is true, tabs are converted to four non-breaking spaces, less than signs are converted to &lt; * entities and greater than signs to &gt; entities. * * @param s The string to escape * @param escapeSpaces True to replace ' ' with nonbreaking space * @return The escaped string */ @Deprecated public static CharSequence escapeMarkup(final String s, final boolean escapeSpaces) { return HtmlUtils.escapeMarkup(s, escapeSpaces); } /** * Converts a Java String to an HTML markup String by replacing illegal characters with HTML entities where appropriate. Spaces are converted to * non-breaking spaces (<nbsp>) if escapeSpaces is true, tabs are converted to four non-breaking spaces, less-than signs are converted to &lt; * entities and greater-than signs to &gt; entities. * * @param s The string to escape * @param escapeSpaces True to replace ' ' with nonbreaking space * @param convertToHtmlUnicodeEscapes True to convert non-7 bit characters to unicode HTML (&#...) * @return The escaped string */ @Deprecated public static CharSequence escapeMarkup(final String s, final boolean escapeSpaces, final boolean convertToHtmlUnicodeEscapes) { return HtmlUtils.escapeMarkup(s, escapeSpaces, convertToHtmlUnicodeEscapes); } /** * Converts a String to multiline HTML markup by replacing newlines with line break entities (<br/>) and multiple occurrences of newline with * paragraph break entities (<p>). * * @param s String to transform * @return String with all single occurrences of newline replaced with <br/> and all multiple occurrences of newline replaced with <p>. */ public static CharSequence toMultilineMarkup(final CharSequence s) { if (s == null) { return null; } final StringBuilder buffer = new StringBuilder(); int newlineCount = 0; buffer.append("<p>"); //$NON-NLS-1$ for (int i = 0; i < s.length(); i++) { final char c = s.charAt(i); switch (c) { case '\n' : newlineCount++; break; case '\r' : break; default : if (newlineCount == 1) { buffer.append("<br/>"); //$NON-NLS-1$ } else if (newlineCount > 1) { buffer.append("</p><p>"); //$NON-NLS-1$ } buffer.append(c); newlineCount = 0; break; } } if (newlineCount == 1) { buffer.append("<br/>"); //$NON-NLS-1$ } else if (newlineCount > 1) { buffer.append("</p><p>"); //$NON-NLS-1$ } buffer.append("</p>"); //$NON-NLS-1$ return buffer; } /** * Format a given number (Visitor (locale) specific) * * @param param the input number * @param digits number of digits * @return the formatted number */ public static String formatNumber(Locale locale, double param, String digits) { return formatNumber(locale, param, getAsInteger(digits)); } /** * Format a given number (Visitor (locale) specific) * * @param param the input number * @param digits number of digits * @return the formatted number */ public static String formatNumber(Locale locale, Object param, String digits) { return formatNumber(locale, getAsDouble(param), getAsInteger(digits)); } /** * Try to parse the given object as a double * * @param o the object (Number, String, ...) to parse * @return the parsed double - or 0 (zero) if the parse doesn't succeed */ public static double getAsDouble(Object o) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).doubleValue(); } else { return getAsDouble(o.toString()); } } /** * Try to parse the given object as a double * * @param o the object (Number, String, ...) to parse * @param throwOnFail whether or not to throw a RuntimeException on failure * @return the parsed double - or 0 (zero) if the parse doesn't succeed and throwOnFail is false */ public static double getAsDouble(Object o, boolean throwOnFail) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).doubleValue(); } else { return getAsDouble(o.toString(), throwOnFail); } } /** * Try to parse the given object as a float * * @param o the object (Number, String, ...) to parse * @return the parsed float or 0 (zero) if the parse doesn't succeed */ public static float getAsFloat(Object o) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).floatValue(); } else { return getAsFloat(o.toString()); } } /** * Try to parse the given object as a float * * @param o the object (Number, String, ...) to parse * @param throwOnException whether or not to throw a RuntimeException on failure * @return the parsed float - or 0 (zero) if the parse doesn't succeed and throwOnException is false */ public static float getAsFloat(Object o, boolean throwOnException) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).floatValue(); } else { return getAsFloat(o.toString(), throwOnException); } } /** * Try to parse the given string as a float * * @param s the string to parse * @return the float - or 0 (zero) if the parse doesn't succeed */ public static float getAsFloat(String s) { if (s == null) return 0; try { return new Float(s.replace(',', '.')).floatValue(); } catch (Exception ex) { return 0; } } /** * Try to parse the given string as a float * * @param s the string to parse * @param throwOnException whether or not to throw a RuntimeException on failure * @return the float - or 0 (zero) if the parse doesn't succeed if throwOnException is false */ public static float getAsFloat(String s, boolean throwOnException) { if (s == null) return 0; try { return new Float(s.replace(',', '.')).floatValue(); } catch (Exception ex) { if (throwOnException) { if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex.getMessage()); } else { return 0; } } } /** * Try to parse the given object as a UUID * * @param o the object to try * @param throwOnException whether or not to throw a RuntimeException on failure * @return UUID or null if o is not recognized * @return the UUID - or null if the parse doesn't succeed if throwOnException is false */ public static UUID getAsUUID(Object o, boolean throwOnException) { if (o == null) { return null; } try { if (o instanceof byte[] && ((byte[])o).length == 16) { return new UUID((byte[])o); } if (o instanceof String) { return UUID.fromString((String)o); } if (o instanceof UUID) { return (UUID)o; } if (o instanceof NativeJavaObject) { return getAsUUID(((NativeJavaObject)o).unwrap(), throwOnException); } } catch (RuntimeException e) { if (throwOnException) { throw e; } } String msg = "Could not parse UUID from object " + o + " type " + o.getClass().getName(); //$NON-NLS-1$ //$NON-NLS-2$ if (throwOnException) { throw new RuntimeException(msg); } Debug.trace(msg); return null; } /* * _____________________________________________________________ The methods below override methods from superclass <classname> */ /* * _____________________________________________________________ The methods below belong to interface <interfacename> */ /** * count the numbers in a string * * @param s the string with the numbers * @return the count */ public static int countNumbers(String s) { if (s == null) return 0; String p = findNumberEx(s); StringTokenizer tk = new StringTokenizer(p, " "); //$NON-NLS-1$ return tk.countTokens(); } /** * hardcore try to find the last number in a string * * @param s the string with the number * @return only the digits */ public static String findLastNumber(String s) { if (s == null) return null; String p = findNumberEx(s); int index = p.lastIndexOf(" "); // incase it is shomething like '100 fl. (45 EUR)' //$NON-NLS-1$ if (index != -1) { p = p.substring(index + 1, p.length()); } // p = p.replace(',','.'); //replace '35,0' to become '35.0' // System.out.println("lastnumber "+p); return p; } /** * hardcore try to find a number in a string * * @param s the string with the number * @return only the digits */ public static String findNumber(String s) { if (s == null) return null; String p = findNumberEx(s); int index = p.indexOf(" "); // incase it is shomething like '100 fl. (45 EUR)' //$NON-NLS-1$ if (index != -1) { p = p.substring(0, index); } if (p.endsWith(",") || p.endsWith(".")) //$NON-NLS-1$ //$NON-NLS-2$ { p = p.substring(0, p.length() - 1); } // p = p.replace(',','.'); //replace '35,0' to become '35.0' return p; } /** * hardcore try to find a number in a string * * @param s the string with the number * @return only the digits */ private static String findNumberEx(String s) { if (s.endsWith(",") || s.endsWith(".")) s = s.substring(0, s.length() - 1); //$NON-NLS-1$ //$NON-NLS-2$ if (s.startsWith(",") || s.startsWith(".")) s = s.substring(1); //$NON-NLS-1$ //$NON-NLS-2$ char[] array = s.toCharArray(); boolean founddigit = false; for (int i = 0; i < array.length; i++) { char currentchar = array[i]; if (!founddigit && Character.isDigit(currentchar)) { founddigit = true; } if (!Character.isDigit(currentchar) && currentchar != ',') { if (currentchar == '.')//the price has many times dots for example 'kr. 1000','fl.100',etc { if (!founddigit) { array[i] = ' '; } } else { array[i] = ' '; } } } String p = new String(array); p = p.trim(); return p; } /* * _____________________________________________________________ The methods below belong to this class */ /** * Try to parse the given object as an integer * * @param o the object (Number, String, ...) to parse * @return the parsed integer - or 0 (zero) if the parse doesn't succeed */ public static int getAsInteger(Object o) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).intValue(); } if (o instanceof Boolean) { return ((Boolean)o).booleanValue() ? 1 : 0; } return getAsInteger(o.toString(), false); } /** * Try to parse the given object as an integer * * @param o the object (Number, String, ...) to parse * @param throwOnException whether to throw an exception if parsing failed * @return the parsed integer - or 0 (zero) if the parse doesn't succeed and throwOnException is false */ public static int getAsInteger(Object o, boolean throwOnException) { if (o == null) return 0; if (o instanceof Number) { return ((Number)o).intValue(); } if (o instanceof Boolean) { return ((Boolean)o).booleanValue() ? 1 : 0; } return getAsInteger(o.toString(), throwOnException); } public static String getURLContent(URL url) { StringBuffer sb = new StringBuffer(); String charset = null; try { URLConnection connection = url.openConnection(); InputStream is = connection.getInputStream(); final String type = connection.getContentType(); if (type != null) { final String[] parts = type.split(";"); for (int i = 1; i < parts.length && charset == null; i++) { final String t = parts[i].trim(); final int index = t.toLowerCase().indexOf("charset="); if (index != -1) charset = t.substring(index + 8); } } InputStreamReader isr = null; if (charset != null) isr = new InputStreamReader(is, charset); else isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); int read = 0; while ((read = br.read()) != -1) { sb.append((char)read); } br.close(); isr.close(); is.close(); } catch (Exception e) { Debug.error(e); } return sb.toString(); } public static byte[] getURLContent(String url) { return getURLContent(url, null); } public static byte[] getURLContent(String url, IServiceProvider serviceProvider) { ByteArrayOutputStream sb = new ByteArrayOutputStream(); try { URL u; if (serviceProvider != null && url.startsWith(MediaURLStreamHandler.MEDIA_URL_DEF)) { u = new URL(null, url, new MediaURLStreamHandler(serviceProvider)); } else { u = new URL(url); } InputStream is = u.openStream(); BufferedInputStream bis = new BufferedInputStream(is); Utils.streamCopy(bis, sb); bis.close(); is.close(); } catch (Exception e) { Debug.error(e); return null; } return sb.toByteArray(); } /** * Doesn't close the input stream. * @param is */ public static byte[] getBytesFromInputStream(InputStream is) throws IOException { ByteArrayOutputStream sb = new ByteArrayOutputStream(); BufferedInputStream bis = new BufferedInputStream(is); Utils.streamCopy(bis, sb); bis.close(); return sb.toByteArray(); } //if you want to be notified every x bytes public static int streamCopy(InputStream is, OutputStream os, ActionListener l, int actionSize) throws IOException { int actionBlock = actionSize; if (is == null || os == null) return 0; int bufferSize = 128; byte[] buffer = new byte[bufferSize]; int length = 0; int totalLength = 0; while ((length = is.read(buffer)) >= 0) { os.write(buffer, 0, length); totalLength += length; if (actionSize > 0 && totalLength > actionBlock) { actionBlock += actionSize; if (l != null) l.actionPerformed(new ActionEvent("streamCopy", ActionEvent.ACTION_PERFORMED, "streamCopy")); //$NON-NLS-1$ //$NON-NLS-2$ } } return totalLength; } public static int streamCopy(InputStream is, OutputStream os) throws IOException { return streamCopy(is, os, null, 0); } public static int readerWriterCopy(Reader is, Writer os) throws IOException { int bufferSize = 128; char[] buffer = new char[bufferSize]; int length = 0; while ((length = is.read(buffer)) >= 0) { os.write(buffer, 0, length); } return length; } public static void rollback(Connection connection) { try { if (connection != null) { connection.rollback(); } } catch (SQLException e) { Debug.error(e); } } public static <T extends Closeable> T close(T closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { Debug.error(e); } return null; } public static <T extends Closeable> T closeQuietly(T closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException e) { //ignore } return null; } public static <T extends Connection> T closeConnection(T connection) { try { if (connection != null) { if (!connection.isClosed()) { connection.close(); } } } catch (SQLException e) { Debug.error(e); } return null; } public static <T extends Statement> T closeStatement(T statement) { try { if (statement != null) { statement.close(); } } catch (SQLException e) { Debug.error(e); } return null; } public static <T extends ResultSet> T closeResultSet(T resultSet) { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e) { Debug.error(e); } return null; } public static void releaseConnection(Connection connection) { if (connection != null && connection instanceof ITransactionConnection) { ((ITransactionConnection)connection).release(); } } public static String calculateMD5HashBase64(String password) { String result = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); //$NON-NLS-1$ byte[] hash = md.digest(password.getBytes("UTF-8")); //$NON-NLS-1$ result = encodeBASE64(hash).trim(); } catch (Exception e) { Debug.error(e); } return result; } public static String calculateAndPrefixPBKDF2PasswordHash(String password) { return PBKDF2_PREFIX + calculatePBKDF2(password, 2000); } /** * Hashes the given string with the PKCS/PBKDF2 algoritme see http://en.wikipedia.org/wiki/PBKDF2 for more information * * @param textString The string to hash * @param iterations Number of hash iterations to be done (should be higher then 1000) * @return the hash of the string */ @SuppressWarnings("nls") public static String calculatePBKDF2(String textString, int iterations) { try { SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); byte[] salt = new byte[8]; sr.nextBytes(salt); PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, iterations); PBKDF2Engine e = new PBKDF2Engine(p); p.setDerivedKey(e.deriveKey(textString)); return new PBKDF2HexFormatter().toString(p); } catch (NoSuchAlgorithmException e) { Debug.error("No SHA1 algorime found under the name SHA1PRNG", e); } return null; } public static boolean validatePrefixedPBKDF2Hash(String password, String hash) { if (hash.startsWith(PBKDF2_PREFIX)) { return validatePBKDF2Hash(password, hash.substring(PBKDF2_PREFIX.length())); } return false; } public static boolean validatePBKDF2Hash(String password, String hash) { PBKDF2Parameters p = new PBKDF2Parameters(); p.setHashAlgorithm("HmacSHA1"); p.setHashCharset("ISO-8859-1"); if (new PBKDF2HexFormatter().fromString(p, hash)) { return false; } PBKDF2Engine e = new PBKDF2Engine(p); return e.verifyKey(password); } @Deprecated public static String calculateMD5Hash(String input) { return calculateMD5HashBase64(input); } public static String calculateMD5HashBase16(String input) { String result = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); //$NON-NLS-1$ byte[] messageDigest = md.digest(input.getBytes("UTF-8")); //$NON-NLS-1$ BigInteger number = new BigInteger(1, messageDigest); result = number.toString(16); if (result.length() < 32) { for (int i = 1; i <= 32 - result.length(); i++) result = '0' + result; } } catch (Exception e) { Debug.error(e); } return result; } public static String encodeBASE64(byte[] data) { String result = null; try { result = Base64.encodeBase64String(data).trim(); } catch (Exception e) { Debug.error(e); } return result; } public static byte[] decodeBASE64(String data) { byte[] result = null; try { result = Base64.decodeBase64(data); } catch (Exception e) { Debug.error(e); } return result; } /** * will give you the index in the string of the first char in the char array that is found in that string. * * @param string * @param chars * @return the index of the first char found or -1 if no char is found */ public static int firstIndexOf(String string, char[] chars) { return firstIndexOf(string, chars, 0); } /** * will give you the index in the string of the first char in the char array that is found in that string * starting at the startIndex * * @param string * @param chars * @param startIndex * @return the index of the first char found or -1 if no char is found */ public static int firstIndexOf(String string, char[] chars, int startIndex) { if (startIndex < 0 || string.length() <= startIndex) return -1; String charsString = new String(chars); int counter = startIndex; while (counter < string.length()) { char ch = string.charAt(counter); if (charsString.indexOf(ch) != -1) return counter; counter++; } return -1; } public static int stringIndexOf(String string, int ch, int escape) { return stringIndexOf(string, ch, escape, 0); } public static int stringIndexOf(String string, int ch, int escape, int beginIndex) { int i = string.indexOf(ch, beginIndex); while (i > 0 && string.charAt(i - 1) == escape) { i = string.indexOf(ch, i + 1); } return i; } public static String unescape(String string, int escape) { StringBuilder buffer = new StringBuilder(string.length()); int i = string.indexOf(escape), b = 0; while (i >= 0) { buffer.append(string.substring(b, i)); if (i + 1 < string.length()) { buffer.append(string.charAt(i + 1)); } b = i + 2; i = string.indexOf(string, b); } if (b < string.length()) { buffer.append(string.substring(b)); } return buffer.toString(); } public static String[] stringSplit(String string, int separator, int escape) { int i = stringIndexOf(string, separator, escape); return i >= 0 ? new String[] { unescape(string.substring(0, i), escape), string.substring(i + 1) } : new String[] { unescape(string, escape), null }; } public static String[] stringSplit(final String s, final String split) { if (s == null) { return new String[0]; } final List<String> strings = new ArrayList<String>(); int pos = 0; while (true) { int next = s.indexOf(split, pos); if (next == -1) { strings.add(s.substring(pos)); break; } else { strings.add(s.substring(pos, next)); } pos = next + 1; } final String[] result = new String[strings.size()]; strings.toArray(result); return result; } public static String longToHexString(long n, int digits) { StringBuilder buffer = new StringBuilder(Long.toHexString(n)); if (buffer.length() > digits) return buffer.substring(buffer.length() - digits); while (buffer.length() < digits) buffer.insert(0, '0'); return buffer.toString(); } public static int compare(double d1, double d2) { if (d1 < d2) return -1; // Neither val is NaN, thisVal is smaller if (d1 > d2) return 1; // Neither val is NaN, thisVal is larger long thisBits = Double.doubleToLongBits(d1); long anotherBits = Double.doubleToLongBits(d2); return (thisBits == anotherBits ? 0 : // Values are equal (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 1)); // (0.0, -0.0) or (NaN, !NaN) } // public static int compare(String s1, String s2) // { // if (s1 == null) return s2 == null ? 0 : -1; // if (s2 == null) return 1; // return s1.compareTo(s2); // } // // public static boolean arrayContentEquals(Object[] a, Object[] a2) // { // if (a == a2) return true; // if (a == null || a2 == null) return false; // // int length = a.length; // if (a2.length != length) return false; // // for (int i = 0; i < length; i++) // { // Object o1 = a[i]; // Object o2 = a2[i]; // if (o1 instanceof ISupportContentEquals && o2 instanceof ISupportContentEquals) // { // if (!((ISupportContentEquals)o1).contentEquals(o2)) return false; // } // else if (!(o1 == null ? o2 == null : o1.equals(o2))) // { // return false; // } // } // return true; // } public static InputStream closeInputStream(InputStream is) { return close(is); } public static Reader closeReader(Reader r) { return close(r); } public static void invokeLater(IEventDelegator delegator, List<Runnable> runnables) { if (runnables != null) { for (Runnable r : runnables) { delegator.invokeLater(r); } } } // @Deprecated // public static String firstLetterToUpperCase(String string) // { // return stringInitCap(string); // if (string == null || string.length() < 1) return string; // return string.substring(0, 1).toUpperCase() + string.substring(1); // } public static Object mapToNullIfUnmanageble(Object value) { if (value instanceof Undefined || value == Scriptable.NOT_FOUND) { return null; } if (value instanceof Number) { if (Double.isNaN(((Number)value).doubleValue())) { return null; } if (Double.isInfinite(((Number)value).doubleValue())) { return null; } } return value; } public static final float PPI = 72f; // standard number of pixels per inch public static double convertPageFormatUnit(int oldUnit, int newUnit, double value) { return new MediaSize(0, (float)value, oldUnit).getY(newUnit); } /** * Create a PageFormat object from the dimensions and margins. * * @param width the actual paper width - ignoring orientation. It is the width of the paper as seen by the printer. * @param height the actual paper height - ignoring orientation. It is the height of the paper as seen by the printer. * @param lm left margin of the page, not paper. So this is the left margin affected by orientation, as used in application. * @param rm right margin of the page, not paper. So this is the right margin affected by orientation, as used in application. * @param tm top margin of the page, not paper. So this is the top margin affected by orientation, as used in application. * @param bm bottom margin of the page, not paper. So this is the bottom margin affected by orientation, as used in application. * @param orientation the orientation of the page. Establishes a relation between page and paper coordinates. * @param units INCHES or MM. * @return the required PageFormat object. */ public static PageFormat createPageFormat(double width, double height, double lm, double rm, double tm, double bm, int orientation, int units) { double pixWidth = convertPageFormatUnit(units, Size2DSyntax.INCH, width) * PPI; double pixHeight = convertPageFormatUnit(units, Size2DSyntax.INCH, height) * PPI; double pixLm = convertPageFormatUnit(units, Size2DSyntax.INCH, lm) * PPI; double pixRm = convertPageFormatUnit(units, Size2DSyntax.INCH, rm) * PPI; double pixTm = convertPageFormatUnit(units, Size2DSyntax.INCH, tm) * PPI; double pixBm = convertPageFormatUnit(units, Size2DSyntax.INCH, bm) * PPI; // The margins of the Paper object are relative to the physical paper, so independent // of the text orientation; The PageFormat object takes the orientation into account relative to the text. // We have to convert back to the paper-relative margins here... double paperLm; double paperRm; double paperTm; double paperBm; if (orientation == PageFormat.LANDSCAPE) { paperLm = pixTm; paperRm = pixBm; paperTm = pixRm; paperBm = pixLm; } else if (orientation == PageFormat.PORTRAIT) { paperLm = pixLm; paperRm = pixRm; paperTm = pixTm; paperBm = pixBm; } else // orientation == PageFormat.REVERSE_LANDSCAPE { paperLm = pixBm; paperRm = pixTm; paperTm = pixLm; paperBm = pixRm; } PageFormat pf = new PageFormat(); pf.setOrientation(orientation); Paper paper = new Paper(); paper.setSize(pixWidth, pixHeight); paper.setImageableArea(paperLm, paperTm, pixWidth - (paperLm + paperRm), pixHeight - (paperTm + paperBm)); pf.setPaper(paper); return pf; } public static final double DEFAULT_EQUALS_PRECISION = 1e-7d; //null,null == true public final static boolean equalObjects(Object oldObj, Object obj) { return equalObjects(oldObj, obj, DEFAULT_EQUALS_PRECISION, false); } public final static boolean equalObjects(Object oldObj, Object obj, boolean ignoreCase) { return equalObjects(oldObj, obj, DEFAULT_EQUALS_PRECISION, ignoreCase); } public final static boolean equalObjects(Object oldObj, Object obj, double equalsPrecision) { return equalObjects(oldObj, obj, equalsPrecision, false); } public final static boolean equalObjects(Object oldObj, Object obj, double equalsPrecision, boolean ignoreCase) { if (oldObj instanceof Wrapper) oldObj = ((Wrapper)oldObj).unwrap(); if (obj instanceof Wrapper) obj = ((Wrapper)obj).unwrap(); if (oldObj == obj) { return true; } if (oldObj == null && obj != null) { return false; } if (oldObj != null && obj == null) { return false; } // Compare UUID with possible storage for UUID if (oldObj.getClass() == UUID.class) { if (obj.getClass() == byte[].class && ((byte[])obj).length == 16) { // compare UUID and byte[] return oldObj.equals(new UUID((byte[])obj)); } if (obj.getClass() == String.class && ((String)obj).length() == 36) { // compare UUID and String return oldObj.toString().equals(obj); } return oldObj.equals(obj); } if (obj.getClass() == UUID.class) { if (oldObj.getClass() == byte[].class && ((byte[])oldObj).length == 16) { // compare UUID and byte[] return obj.equals(new UUID((byte[])oldObj)); } if (oldObj.getClass() == String.class && ((String)oldObj).length() == 36) { // compare UUID and String return obj.toString().equals(oldObj); } return obj.equals(oldObj); } if (oldObj.getClass().isArray() && obj.getClass().isArray()) { int length = Array.getLength(obj); if (length == Array.getLength(oldObj)) { for (int i = 0; i < length; i++) { if (!equalObjects(Array.get(obj, i), Array.get(oldObj, i), equalsPrecision, ignoreCase)) return false; } return true; } return false; } //in case one side is String and other Number -> make both string if (oldObj instanceof Number && obj instanceof String) { try { obj = new Double((String)obj); } catch (Exception e) { oldObj = oldObj.toString(); } } else if (obj instanceof Number && oldObj instanceof String) { try { oldObj = new Double((String)oldObj); } catch (Exception e) { obj = obj.toString(); } } if (oldObj instanceof BigDecimal && !(obj instanceof BigDecimal)) { if (obj instanceof Long) { obj = BigDecimal.valueOf(((Long)obj).longValue()); } else if (obj instanceof Double) { obj = BigDecimal.valueOf(((Double)obj).doubleValue()); } } else if (obj instanceof BigDecimal && !(oldObj instanceof BigDecimal)) { if (oldObj instanceof Long) { oldObj = BigDecimal.valueOf(((Long)oldObj).longValue()); } else if (oldObj instanceof Double) { oldObj = BigDecimal.valueOf(((Double)oldObj).doubleValue()); } } // separate tests for BigDecimal and Long, the tests based on Double may give // incorrect results for Long values not fitting in a double mantissa. // note that 2.0 is not equal to 2.00 according to BigDecimal.equals() if (oldObj instanceof BigDecimal && obj instanceof BigDecimal) { return ((BigDecimal)oldObj).subtract((BigDecimal)obj).abs().doubleValue() < equalsPrecision; } if (oldObj instanceof Long && obj instanceof Long) { return oldObj.equals(obj); } // Always cast to double so we don't lose precision. if (oldObj instanceof Number && obj instanceof Number) { if (oldObj instanceof Float || oldObj instanceof Double || oldObj instanceof BigDecimal || obj instanceof Float || obj instanceof Double || obj instanceof BigDecimal) { double a = ((Number)oldObj).doubleValue(); double b = ((Number)obj).doubleValue(); return a == b || Math.abs(a - b) < equalsPrecision; } return ((Number)oldObj).longValue() == ((Number)obj).longValue(); } if (oldObj instanceof Date && obj instanceof Date) { return (((Date)oldObj).getTime() == ((Date)obj).getTime()); } if (ignoreCase && oldObj instanceof String && obj instanceof String) { return ((String)oldObj).equalsIgnoreCase((String)obj); } return oldObj.equals(obj); } /** * Convert to string representation, remove trailing '.0' for numbers. * * @return the result string */ public static String convertToString(Object o) { if (!(o instanceof Number)) { return String.valueOf(o); } String numberToString = o.toString(); int i; for (i = numberToString.length() - 1; i > 0; i--) { if (numberToString.charAt(i) != '0') { break; } } if (numberToString.charAt(i) == '.') { return numberToString.substring(0, i); } return numberToString; } public static OutputStream closeOutputStream(OutputStream os) { try { if (os != null) { os.close(); } } catch (IOException e) { Debug.error(e); } return null; } public static Writer closeWriter(Writer w) { return close(w); } public static byte[] readFile(File f, long size) { if (f != null && f.exists()) { FileInputStream fis = null; try { int length = (int)f.length(); fis = new FileInputStream(f); FileChannel fc = fis.getChannel(); if (size > length || size < 0) size = length; ByteBuffer bb = ByteBuffer.allocate((int)size); fc.read(bb); bb.rewind(); byte[] bytes = null; if (bb.hasArray()) { bytes = bb.array(); } else { bytes = new byte[(int)size]; bb.get(bytes, 0, (int)size); } return bytes; } catch (Exception e) { Debug.error("Error reading file: " + f, e); //$NON-NLS-1$ } finally { try { if (fis != null) fis.close(); } catch (Exception ex) { } } // ByteArrayOutputStream sb = new ByteArrayOutputStream(); // try // { // FileInputStream is = new FileInputStream(f); // BufferedInputStream bis = new BufferedInputStream(is); // streamCopy(bis, sb); // closeInputStream(bis); // } // catch (Exception e) // { // Debug.error(e); // } // return sb.toByteArray(); } return null; } public static byte[] getFileContent(File f) { if (f != null) return readFile(f, -1); return null; } public static String getTXTFileContent(File f) { return getTXTFileContent(f, Charset.defaultCharset()); } public static String getTXTFileContent(File f, Charset charset) { if (f != null /* && f.exists() */) { if (Thread.currentThread().isInterrupted()) { Thread.interrupted(); // reset interrupted flag of current thread, FileChannel.read() will throw an exception for it. } FileInputStream fis = null; try { int length = (int)f.length(); if (f.exists()) { fis = new FileInputStream(f); FileChannel fc = fis.getChannel(); ByteBuffer bb = ByteBuffer.allocate(length); fc.read(bb); bb.rewind(); CharBuffer cb = charset.decode(bb); return cb.toString(); } } catch (Exception e) { Debug.error("Error reading txt file: " + f, e); //$NON-NLS-1$ } finally { closeInputStream(fis); } } return null; } public static InputStream getUTF8EncodedStream(String out) { byte[] content = null; try { content = out.getBytes("UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { Debug.error(e); } if (content == null) content = out.getBytes(); return new ByteArrayInputStream(content); } public static String getTXTFileContent(InputStream f, Charset charset) { return getTXTFileContent(f, charset, true); } public static String getTXTFileContent(InputStream f, Charset charset, boolean closeStream) { InputStreamReader isr = null; if (charset != null) isr = new InputStreamReader(f, charset); else isr = new InputStreamReader(f); try { StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { sb.append(line); sb.append('\n'); } if (sb.length() > 0) sb.setLength(sb.length() - 1); // remove newline if (closeStream) { closeReader(br); } return sb.toString(); } catch (IOException e) { Debug.error("Error reading txt file", e); //$NON-NLS-1$ } return null; } public static boolean writeTXTFile(File file, String content) { return writeTXTFile(file, content, Charset.defaultCharset()); } public static boolean writeTXTFile(File f, String content, Charset charset) { if (f != null) { FileOutputStream fos = null; try { fos = new FileOutputStream(f); FileChannel fc = fos.getChannel(); ByteBuffer bb = charset.encode(content); fc.write(bb); bb.rewind(); return true; } catch (Exception e) { Debug.error("Error writing txt file: " + f, e); //$NON-NLS-1$ } finally { closeOutputStream(fos); } } return false; } public static boolean writeFile(File f, byte[] content) { if (f != null) { FileOutputStream fos = null; try { f.getParentFile().mkdirs(); fos = new FileOutputStream(f); fos.write(content); fos.flush(); return true; } catch (Exception e) { Debug.error("Error writing file: " + f, e); //$NON-NLS-1$ } finally { closeOutputStream(fos); } } return false; } public static boolean isAppleMacOS() { return getPlatform() == PLATFORM_MAC; } public static boolean isValidEmailAddress(String email) { return (email != null ? (Pattern.compile("^[_a-z0-9-+]+(\\.[_a-z0-9-+]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,16})$", Pattern.CASE_INSENSITIVE).matcher(email).matches()) //$NON-NLS-1$ : false); } public static boolean isValidJavaIdentifier(String s) { return IdentDocumentValidator.isJavaIdentifier(s); } public static boolean isValidJavaSimpleOrQualifiedName(String s) { boolean ok = true; StringTokenizer tokenizer = new StringTokenizer(s, ".", false); //$NON-NLS-1$ while (tokenizer.hasMoreTokens()) { ok = IdentDocumentValidator.isJavaIdentifier(tokenizer.nextToken()); } return ok; } public static int getPlatform() { return getPlatform(System.getProperty("os.name")); //$NON-NLS-1$ } public static int getPlatform(String osname) { if (osname != null) { String lc = osname.toLowerCase(); if (lc.contains("mac")) return PLATFORM_MAC; //$NON-NLS-1$ if (lc.contains("linux")) return PLATFORM_LINUX; //$NON-NLS-1$ if (lc.contains("win")) return PLATFORM_WINDOWS; //$NON-NLS-1$ } return PLATFORM_OTHER; } public static String getPlatformAsString() { switch (Utils.getPlatform()) { case Utils.PLATFORM_LINUX : return "linux"; //$NON-NLS-1$ case Utils.PLATFORM_MAC : return "mac"; //$NON-NLS-1$ case Utils.PLATFORM_WINDOWS : return "windows"; //$NON-NLS-1$ default : return "other"; //$NON-NLS-1$ } } public static String getDotQualitfied(Object... tokens) { if (tokens == null) { return null; } if (tokens.length == 0) { return ""; //$NON-NLS-1$ } if (tokens.length == 1) { return String.valueOf(tokens[0]); } StringBuilder ptr = new StringBuilder(String.valueOf(tokens[0])); for (int i = 1; i < tokens.length; i++) { ptr.append('.'); ptr.append(String.valueOf(tokens[i])); } return ptr.toString(); } public static String toEnglishLocaleLowerCase(String text) { if (text == null) return null; return text.toLowerCase(Locale.ENGLISH); } public static String stringInitCap(Object text) { if (text != null) { try { StringBuilder sb = new StringBuilder(); StringTokenizer st = new StringTokenizer(text.toString(), " "); //$NON-NLS-1$ int i = 0; while (st.hasMoreTokens()) { String value = st.nextToken(); String value_upper = value.substring(0, 1); String value_lower = value.substring(1); value_upper = value_upper.toUpperCase(); value_lower = value_lower.toLowerCase(); sb.append(value_upper + value_lower); if (st.hasMoreTokens()) { sb.append(" "); //$NON-NLS-1$ } i++; } return sb.toString(); } catch (Exception ex) { Debug.error(ex); return text.toString(); } } else { return ""; //$NON-NLS-1$ } } /** * Get the rectangle that surrounds all elements * * @param elements * @return the rectangle */ public static Rectangle getBounds(Iterator< ? > elements) { int minx = -1; int miny = -1; int maxx = -1; int maxy = -1; while (elements != null && elements.hasNext()) { Object element = elements.next(); if (element instanceof ISupportBounds) { java.awt.Point location = ((ISupportBounds)element).getLocation(); java.awt.Dimension size = ((ISupportBounds)element).getSize(); if (location != null && size != null) { if (minx == -1 || minx > location.x) minx = location.x; if (miny == -1 || miny > location.y) miny = location.y; if (maxx == -1 || maxx < location.x + size.width) maxx = location.x + size.width; if (maxy == -1 || maxy < location.y + size.height) maxy = location.y + size.height; } } } return new Rectangle(minx, miny, maxx - minx, maxy - miny); } /** * Set Calendar time part to 00:00:00:000 * * @param cal */ public static void applyMinTime(Calendar cal) { cal.set(Calendar.HOUR_OF_DAY, 00); //h:00 cal.set(Calendar.MINUTE, 00);//x:00 cal.set(Calendar.SECOND, 00); cal.set(Calendar.MILLISECOND, 000); } /** * Set Calendar time part to 23:59:59:999 * * @param cal */ public static void applyMaxTime(Calendar cal) { cal.set(Calendar.HOUR_OF_DAY, 23); //h:23 cal.set(Calendar.MINUTE, 59);//x:59 cal.set(Calendar.SECOND, 59); cal.set(Calendar.MILLISECOND, 999); } public static String[] getTokenElements(String value, String delim, boolean trim) { if (value == null) { return new String[] { }; } List<String> lst = new ArrayList<String>(); StringTokenizer tokemizer = new StringTokenizer(value, delim); while (tokemizer.hasMoreElements()) { String token = tokemizer.nextToken(); if (trim) { lst.add(token.trim()); } else { lst.add(token); } } return lst.toArray(new String[lst.size()]); } /** * Iterate over iterator. * <pre> * for (T o: Utils.iterate(iterator)) * { * o. .... * } * </pre> * @param iterator when null, iterate over empty list */ public static <T> Iterable<T> iterate(final Iterator<T> iterator) { return iterator == null ? Collections.<T> emptyList() : new Iterable<T>() { public Iterator<T> iterator() { return iterator; } }; } public static <T> Iterable<T> iterate(Iterable<T> iterable) { return iterable == null ? Collections.<T> emptyList() : iterable; } /** * Iterate over enumeration. * <pre> * for (T o: Utils.iterate(enumeration)) * { * o. .... * } * </pre> * @param enumeration when null, iterate over empty list */ public static <T> Iterable<T> iterate(final Enumeration<T> enumeration) { return iterate(enumeration == null ? null : new Iterator<T>() { @Override public boolean hasNext() { return enumeration.hasMoreElements(); } @Override public T next() { return enumeration.nextElement(); } @Override public void remove() { throw new UnsupportedOperationException(); } }); } /** * Returns true if the given client/application type is a Swing client and false if it is not. * @param applicationType the type to check */ public static boolean isSwingClient(int applicationType) { return applicationType == IApplication.CLIENT || applicationType == IApplication.OFFLINE || applicationType == IApplication.RUNTIME; } private static String getPrefixedType(String type, String prefix) { return prefix != null ? prefix + type : type; } /** * Removes the first map entry based on a map value * * @param value the map value to remove the map entry * @param map a map from which to remove the value and key * @return the previous value associated with key, or null if there was no mapping for key */ public static <K, V> K mapRemoveByValue(V value, Map<K, V> map) { K removalKey = null; for (Map.Entry<K, V> entry : map.entrySet()) { if (value == null && entry.getValue() == null) { removalKey = entry.getKey(); break; } else if (value != null && value.equals(entry.getValue())) { removalKey = entry.getKey(); break; } } if (removalKey != null) { map.remove(removalKey); return removalKey; } return null; } /** * Returns the first Key of a map based on the value parameter * * @param map * @param value * @return the first key of a map based on the value parameter */ public static <K, V> K mapGetKeyByValue(Map<K, V> map, V value) { for (Entry<K, V> entry : map.entrySet()) { if (value == null && entry.getValue() == null) { return entry.getKey(); } else if (value != null && value.equals(entry.getValue())) { return entry.getKey(); } } return null; } public static <T, E> Set<T> mapGetKeysByValue(Map<T, E> map, E value) { Set<T> keys = new HashSet<T>(); for (Entry<T, E> entry : map.entrySet()) { if (value == null && entry.getValue() == null) { keys.add(entry.getKey()); } else if (value != null && value.equals(entry.getValue())) { keys.add(entry.getKey()); } } return keys; } /** * Returns a js/json string representation of the given {@link Scriptable} * @param obj * @return the scriptable as string */ public static String getScriptableString(Scriptable obj) { if (obj == null) return "null"; //$NON-NLS-1$ return getScriptableString(obj, new HashMap<Scriptable, CharSequence>()).toString(); } /** * Returns a js/json string representation of the given Array, that can have {@link Scriptable} objects inside it. * @param array * @return the scriptable as string */ public static String getScriptableString(Object[] array) { if (array == null) return "null"; //$NON-NLS-1$ return getArrayString(array).toString(); } public static Object removeJavascripLinkFromDisplay(IDisplayData display, Object[] value) { Object obj = value == null ? display.getValueObject() : value[0]; if (obj instanceof String && display instanceof IScriptableProvider) { IScriptable scriptable = ((IScriptableProvider)display).getScriptObject(); if (scriptable instanceof HasRuntimeClientProperty) { HasRuntimeClientProperty scriptableWithClientProperty = (HasRuntimeClientProperty)scriptable; if (!Boolean.TRUE.equals(scriptableWithClientProperty.getClientProperty(IApplication.ALLOW_JAVASCRIPT_LINK_INPUT))) { obj = ((String)obj).replaceAll("(?i)javascript:", ""); //$NON-NLS-1$ //$NON-NLS-2$ display.setValueObject(obj); } } } return obj; } /** * Returns a js/json string representation of the given array. * @param a * @return the array as string */ private static CharSequence getArrayString(Object[] a) { StringBuilder buf = new StringBuilder(); buf.append('['); for (int i = 0; i < a.length; i++) { if (i > 0) buf.append(", "); //$NON-NLS-1$ if (a[i] instanceof Scriptable) buf.append(getScriptableString((Scriptable)a[i], new HashMap<Scriptable, CharSequence>())); else if (a[i] instanceof Object[]) buf.append(getArrayString((Object[])a[i])); else buf.append(String.valueOf(a[i])); } buf.append(']'); return buf; } /** * Returns a js/json string representation of the given {@link Scriptable} * @param scriptable * @param processed map to prevent loops in graph * @return the scriptable as string */ private static CharSequence getScriptableString(Scriptable scriptable, Map<Scriptable, CharSequence> processed) { if (scriptable instanceof Record || scriptable instanceof FoundSet) return scriptable.toString(); if (scriptable instanceof XMLObject || scriptable instanceof NativeError) return scriptable.toString(); CharSequence processedString = processed.get(scriptable); if (processedString != null) { return processedString; } if (processed.size() > 10) return scriptable.toString(); if (scriptable instanceof NativeArray) processed.put(scriptable, "Array[SelfRef]"); //$NON-NLS-1$ else processed.put(scriptable, "Object[SelfRef]"); //$NON-NLS-1$ Object[] ids = scriptable.getIds(); if (ids != null && ids.length > 0) { StringBuilder sb = new StringBuilder(); if (scriptable instanceof NativeArray) sb.append('['); else sb.append('{'); for (Object object : ids) { if (!(object instanceof Integer)) { sb.append(object); sb.append(':'); } Object value = null; if (object instanceof String) { value = scriptable.get((String)object, scriptable); } else if (object instanceof Number) { value = scriptable.get(((Number)object).intValue(), scriptable); } if (!(value instanceof NativeJavaMethod)) { if (value instanceof Scriptable) { sb.append(getScriptableString((Scriptable)value, processed)); } else { sb.append(value); } sb.append(','); } } sb.setLength(sb.length() - 1); if (scriptable instanceof NativeArray) sb.append(']'); else sb.append('}'); processed.put(scriptable, sb); return sb; } Object defaultValue; try { defaultValue = scriptable.getDefaultValue(String.class); } catch (Exception e) { defaultValue = null; } if (defaultValue == null) defaultValue = scriptable.toString(); processed.put(scriptable, defaultValue.toString()); return defaultValue.toString(); } }