0001: /*
0002: * The contents of this file are subject to the Mozilla Public License
0003: * Version 1.1 (the "License"); you may not use this file except in
0004: * compliance with the License. You may obtain a copy of the License at
0005: * http://www.mozilla.org/MPL/
0006: *
0007: * Software distributed under the License is distributed on an "AS IS"
0008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
0009: * License for the specific language governing rights and limitations
0010: * under the License.
0011: *
0012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
0013: *
0014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
0015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
0016: *
0017: * Contributor(s):
0018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
0019: *
0020: * If you didn't download this code from the following link, you should check
0021: * if you aren't using an obsolete version: http://www.isqlviewer.com
0022: */
0023: package org.isqlviewer.util;
0024:
0025: import java.awt.Color;
0026: import java.awt.Font;
0027: import java.io.ByteArrayOutputStream;
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.io.Reader;
0031: import java.io.UnsupportedEncodingException;
0032: import java.lang.reflect.Field;
0033: import java.math.BigDecimal;
0034: import java.math.BigInteger;
0035: import java.security.GeneralSecurityException;
0036: import java.security.Key;
0037: import java.security.MessageDigest;
0038: import java.security.NoSuchAlgorithmException;
0039: import java.sql.Blob;
0040: import java.sql.Clob;
0041: import java.sql.Ref;
0042: import java.sql.Time;
0043: import java.sql.Timestamp;
0044: import java.sql.Types;
0045: import java.text.MessageFormat;
0046: import java.text.NumberFormat;
0047: import java.text.ParseException;
0048: import java.util.ArrayList;
0049: import java.util.Arrays;
0050: import java.util.Collection;
0051: import java.util.Date;
0052: import java.util.Iterator;
0053: import java.util.Properties;
0054: import java.util.StringTokenizer;
0055:
0056: import javax.crypto.Cipher;
0057:
0058: /**
0059: * Generic String manipulation library for converting strings or working with text.
0060: * <p>
0061: *
0062: * @author Mark A. Kobold <mkobold at isqlviewer dot com>
0063: * @version 1.0
0064: */
0065: public final class StringUtilities {
0066:
0067: private static final String RESOURCE_BUNDLE = "org.isqlviewer.util.ResourceBundle";
0068: private static final String MD5_ALGORITHIM = "MD5";
0069: private static final String DELIM_START = "${";
0070: private static final char DELIM_STOP = '}';
0071: private static final int DELIM_START_LEN = 2;
0072: private static final int DELIM_STOP_LEN = 1;
0073: private static final LocalMessages messages = new LocalMessages(
0074: RESOURCE_BUNDLE);
0075:
0076: private StringUtilities() {
0077:
0078: }
0079:
0080: /**
0081: * Utility method for accessing a system property without a security permission.
0082: * <p>
0083: * This method will simply eat any security exception that could occur and return the default.
0084: *
0085: * @param key of the system property to lookup.
0086: * @param def the default value to use if the property cannot be read.
0087: * @return the respective system property or the default if cannot be read.
0088: */
0089: public static String getSystemProperty(String key, String def) {
0090:
0091: try {
0092: return System.getProperty(key, def);
0093: } catch (Throwable e) {
0094: return def;
0095: }
0096: }
0097:
0098: /**
0099: * Performs variable string substitution with a set of named parameters.
0100: * <p>
0101: * In similar fashion the the JVM and Jakarta projects this will perform string substitution on a string based on a
0102: * set of properties using the names to create a dynamically formatted text, that is based on easy to create, as
0103: * well as read source string.
0104: * <p>
0105: * Using the source text of <code>'The quick brown ${fast animal} jumps over the lazy ${lazy animal}'</code> you
0106: * would then set properties in the give props of 'fast animal' and 'lazy animal' with the respective values that
0107: * you want to be used as a replacement to the ${} variables. such that is 'fast animal' = 'fox' and 'lazy animal' =
0108: * 'dog' the result would be the famous sentence of 'The quick brown fox jumps over the lazy dog'.
0109: * <p>
0110: * This method does support recursive variables such that a variable can reference another variable until either
0111: * there is a stack overflow or there are no variables left to substitute.
0112: *
0113: * @param source string to be formatted using variable substitution.
0114: * @param props set of variable names to variable values to be used for the substitution process.
0115: * @return a formatted string with the variables properly substituted.
0116: * @throws ParseException if a variable declaration is started but is not closed.
0117: */
0118: public static String substituteVariables(String source,
0119: Properties props) throws ParseException {
0120:
0121: StringBuffer sbuf = new StringBuffer();
0122: int i = 0;
0123: int j, k;
0124: while (true) {
0125: j = source.indexOf(DELIM_START, i);
0126: if (j == -1) {
0127: // no more variables
0128: if (i == 0) { // this is a simple string
0129: return source;
0130: }
0131: // add the tail string which contains no variables and return the result.
0132: sbuf.append(source.substring(i, source.length()));
0133: return sbuf.toString();
0134: }
0135: sbuf.append(source.substring(i, j));
0136: k = source.indexOf(DELIM_STOP, j);
0137: if (k == -1) {
0138: throw new ParseException(messages
0139: .format("StringUtilities.bad_variable_format"),
0140: j);
0141: }
0142:
0143: j += DELIM_START_LEN;
0144: String key = source.substring(j, k);
0145: // first try in System properties
0146: String replacement = props == null ? null : props
0147: .getProperty(key);
0148: // then try props parameter
0149: if (replacement == null) {
0150: replacement = getSystemProperty(key, null);
0151: }
0152: if (replacement != null) {
0153: // Do variable substitution on the replacement string
0154: // such that we can solve "Hello ${x2}" as "Hello p1"
0155: // the where the properties are
0156: // x1=p1
0157: // x2=${x1}
0158: String recursiveReplacement = substituteVariables(
0159: replacement, props);
0160: sbuf.append(recursiveReplacement);
0161: }
0162: i = k + DELIM_STOP_LEN;
0163: }
0164: }
0165:
0166: /**
0167: * Forces a string to be trimmed to a specified length.
0168: * <p>
0169: * Makes sure that the text is <= maxLength. If text is null or blank the original text will be returned.
0170: * <p>
0171: * A trim() will occur before a substring is created, the substring is create iff the trimed version greater than
0172: * the maxLength.
0173: *
0174: * @param text to strip.
0175: * @param maxLength maximum size to trim to.
0176: * @return the text parameter that does not exceed maxLength.
0177: */
0178: public static String trimToSize(String text, int maxLength) {
0179:
0180: if (text == null) {
0181: return text;
0182: }
0183: String temp = text.trim();
0184: if (temp.length() <= maxLength) {
0185: return temp;
0186: }
0187: return temp.substring(0, maxLength);
0188: }
0189:
0190: /**
0191: * Searches a string for a set of characters.
0192: * <p>
0193: * This will return the first occurrence of any character that exists in both s and chars. The return values is the
0194: * index >=0 and <= s.length()-1;
0195: * <p>
0196: * If either s or chars is null or empty the method will immediately return -1.
0197: *
0198: * @param s string to search
0199: * @param chars set of character to look for.
0200: * @return first index of any character in chars within s.
0201: */
0202: public static int searchStringSet(String s, String chars) {
0203:
0204: if (s == null || chars == null) {
0205: return -1;
0206: }
0207:
0208: int mx = s.length();
0209: for (int i = 0; i < mx; i++) {
0210: char ch = s.charAt(i);
0211: if (chars.indexOf(ch) >= 0) {
0212: return i;
0213: }
0214: }
0215: return -1;
0216: }
0217:
0218: /**
0219: * Encodes XML reserved characters to their respective entities.
0220: * <p>
0221: * This will encode/change the following raw characters:
0222: * <ul>
0223: * <li>" = &quot;</li>
0224: * <li>> = &gt;</li>
0225: * <li>< = &lt;</li>
0226: * <li>& = &amp;</li>
0227: * <li>' = &apos;</li>
0228: * </ul>
0229: *
0230: * @param str to be encoded.
0231: * @return String with standard encoded entities.
0232: */
0233: public static String encodeXMLEntities(String str) {
0234:
0235: if (str == null) {
0236: return "";
0237: }
0238:
0239: int len = str.length();
0240: StringBuffer outBuffer = new StringBuffer(len * 2);
0241: for (int x = 0; x < len; x++) {
0242: char aChar = str.charAt(x);
0243: switch (aChar) {
0244: case '\"':
0245: outBuffer.append(""");
0246: break;
0247: case '>':
0248: outBuffer.append(">");
0249: break;
0250: case '<':
0251: outBuffer.append("<");
0252: break;
0253: case '&':
0254: outBuffer.append("&");
0255: break;
0256: case '\'':
0257: outBuffer.append("'");
0258: break;
0259: default:
0260: if ((aChar < 0x0020) || (aChar > 0x007e)) {
0261: outBuffer.append('&');
0262: outBuffer.append("#x");
0263: outBuffer.append(toHex((aChar >> 12) & 0xF));
0264: outBuffer.append(toHex((aChar >> 8) & 0xF));
0265: outBuffer.append(toHex((aChar >> 4) & 0xF));
0266: outBuffer.append(toHex(aChar & 0xF));
0267: outBuffer.append(';');
0268: } else {
0269: outBuffer.append(aChar);
0270: }
0271: }
0272: }
0273: return outBuffer.toString();
0274: }
0275:
0276: /**
0277: * Encodes string safe for HTML.
0278: * <p>
0279: * If encoding into HTML various entities will be encoded for proper HTML formatting.
0280: *
0281: * @param theString to be encoded
0282: * @return encoded string.
0283: */
0284: public static String encodeHTMLEntities(String theString) {
0285:
0286: if (theString == null) {
0287: return "";
0288: }
0289:
0290: int len = theString.length();
0291: StringBuffer outBuffer = new StringBuffer(len * 2);
0292: for (int x = 0; x < len; x++) {
0293: char aChar = theString.charAt(x);
0294: switch (aChar) {
0295: case '&':
0296: outBuffer.append("&");
0297: break;
0298: case '\t':
0299: outBuffer.append(" ");
0300: break;
0301: case '\"':
0302: outBuffer.append(""");
0303: break;
0304: case '>':
0305: outBuffer.append(">");
0306: break;
0307: case '`':
0308: outBuffer.append("'");
0309: break;
0310: case '<':
0311: outBuffer.append("<");
0312: break;
0313: default:
0314: if ((aChar < 0x0020) || (aChar > 0x007e)) {
0315: outBuffer.append('&');
0316: outBuffer.append("#x");
0317: outBuffer.append(toHex((aChar >> 12) & 0xF));
0318: outBuffer.append(toHex((aChar >> 8) & 0xF));
0319: outBuffer.append(toHex((aChar >> 4) & 0xF));
0320: outBuffer.append(toHex(aChar & 0xF));
0321: outBuffer.append(';');
0322: } else {
0323: outBuffer.append(aChar);
0324: }
0325: }
0326: }
0327: return outBuffer.toString();
0328: }
0329:
0330: /**
0331: * Encode text using an MD5 algorithim.
0332: * <p>
0333: *
0334: * @param text to encode.
0335: * @return MD5 checksum of the text given.
0336: */
0337: public static String encodeMD5(String text) {
0338:
0339: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0340: try {
0341: baos.write(text == null ? new byte[0] : text
0342: .getBytes("UTF8"));
0343: } catch (IOException ioe) {
0344: }
0345: byte[] hash = null;
0346: MessageDigest digest = allocateDigestInstance(MD5_ALGORITHIM);
0347: hash = digest.digest(baos.toByteArray());
0348: return encodeMD5Data(hash);
0349: }
0350:
0351: /**
0352: * Encodes method into an ASCI compatible form.
0353: * <p>
0354: * This method has the result equivalent the native2ascii tool provided by the JDK.
0355: * <p>
0356: * The value returned here is similar to that found in the Java properties output.
0357: *
0358: * @param theString to be encoded
0359: * @return encoded string.
0360: */
0361: public static String encodeASCII(String theString) {
0362:
0363: int len = theString.length();
0364: StringBuffer outBuffer = new StringBuffer(len * 2);
0365: for (int x = 0; x < len; x++) {
0366: char aChar = theString.charAt(x);
0367: switch (aChar) {
0368: case ' ':
0369: outBuffer.append('\\');
0370: outBuffer.append(' ');
0371: break;
0372: case '\\':
0373: outBuffer.append('\\');
0374: break;
0375: case '\t':
0376: outBuffer.append('\\');
0377: outBuffer.append('t');
0378: break;
0379: case '\"':
0380: outBuffer.append('\"');
0381: break;
0382: case '>':
0383: outBuffer.append('>');
0384: break;
0385: case '<':
0386: outBuffer.append('<');
0387: break;
0388: case '\n':
0389: outBuffer.append('\\');
0390: outBuffer.append('n');
0391: break;
0392: case '\r':
0393: outBuffer.append('\\');
0394: outBuffer.append('r');
0395: break;
0396: case '\f':
0397: outBuffer.append('\\');
0398: outBuffer.append('f');
0399: break;
0400: default:
0401: if ((aChar < 0x0020) || (aChar > 0x007e)) {
0402: outBuffer.append('\\');
0403: outBuffer.append('u');
0404: outBuffer.append(toHex((aChar >> 12) & 0xF));
0405: outBuffer.append(toHex((aChar >> 8) & 0xF));
0406: outBuffer.append(toHex((aChar >> 4) & 0xF));
0407: outBuffer.append(toHex(aChar & 0xF));
0408: } else {
0409: outBuffer.append(aChar);
0410: }
0411: }
0412: }
0413: return outBuffer.toString();
0414: }
0415:
0416: /**
0417: * Method for decoding ASCII escaped character sequences for UNICODE characters.
0418: * <p>
0419: * This will decode text encoded in a fashion similar to the Java Properties encoding mechanisim. Such that \\uxxxx
0420: * are converted to the proper characters.
0421: * <p>
0422: * In fact most of this code came from that private method contained there.
0423: *
0424: * @see #encodeASCII(String)
0425: * @param str to decode ASCII escaped character sequences from.
0426: * @return decoded string.
0427: */
0428: public static String decodeASCII(String str) {
0429:
0430: char aChar;
0431: int len = str.length();
0432: StringBuffer outBuffer = new StringBuffer(len);
0433:
0434: for (int x = 0; x < len;) {
0435: aChar = str.charAt(x++);
0436: if (aChar == '\\') {
0437: aChar = str.charAt(x++);
0438: if (aChar == 'u') {
0439: int value = 0;
0440: int maxLength = 4;
0441: for (int i = 0; i < maxLength; i++) {
0442: if (x >= str.length()) {
0443: break;
0444: }
0445: aChar = str.charAt(x++);
0446: switch (aChar) {
0447: case '0':
0448: case '1':
0449: case '2':
0450: case '3':
0451: case '4':
0452: case '5':
0453: case '6':
0454: case '7':
0455: case '8':
0456: case '9':
0457: value = (value << 4) + aChar - '0';
0458: break;
0459: case 'a':
0460: case 'b':
0461: case 'c':
0462: case 'd':
0463: case 'e':
0464: case 'f':
0465: value = (value << 4) + 10 + aChar - 'a';
0466: break;
0467: case 'A':
0468: case 'B':
0469: case 'C':
0470: case 'D':
0471: case 'E':
0472: case 'F':
0473: value = (value << 4) + 10 + aChar - 'A';
0474: break;
0475: default:
0476: String chr = Character
0477: .toString((char) value);
0478: String err = messages
0479: .format(
0480: "stringutilities.malformedunicodecharacter",
0481: chr);
0482: throw new IllegalArgumentException(err);
0483: }
0484: }
0485: outBuffer.append((char) value);
0486: } else {
0487: switch (aChar) {
0488: case 't':
0489: aChar = '\t';
0490: break;
0491: case 'r':
0492: aChar = '\r';
0493: break;
0494: case 'n':
0495: aChar = '\n';
0496: break;
0497: case 'f':
0498: aChar = '\f';
0499: break;
0500: default:
0501: break;
0502: }
0503: outBuffer.append(aChar);
0504: }
0505: } else {
0506: outBuffer.append(aChar);
0507: }
0508: }
0509: return outBuffer.toString();
0510: }
0511:
0512: /**
0513: * Helper method to count number of given characters in a string.
0514: * <p>
0515: * This will return number of character instances in the given string. if the given string is null then a -1 will
0516: * returned.
0517: * <p>
0518: * For example if the given string is "XXNNXXnxXnXN" and the char is 'n' it will return a value of two as this
0519: * method is case sensitive.
0520: * <p>
0521: * Another example is if given the string 'X\nXxxNnN' with count escaped set to false will return 1 otherwise it
0522: * will return 2.
0523: *
0524: * @param str to check characters for.
0525: * @param c character to count
0526: * @param countEscaped <tt>true</tt> will included escaped characters as part of the count`
0527: * @return number of occurrences of the character C in the given string.
0528: */
0529: public static int charCount(String str, char c, boolean countEscaped) {
0530:
0531: if (str == null)
0532: return -1;
0533:
0534: int cnt = 0;
0535: for (int i = 0; i < str.length(); i++) {
0536: if (str.charAt(i) == c) {
0537: if (i >= 1) {
0538: if (str.charAt(i - 1) == '\\') {
0539: if (countEscaped) {
0540: cnt++;
0541: } else {
0542: continue;
0543: }
0544: } else {
0545: cnt++;
0546: }
0547: }
0548: }
0549: }
0550: return cnt;
0551: }
0552:
0553: /**
0554: * Gets an SQL Type field string based on the given object.
0555: * <p>
0556: * Basically this method gets the field name that can be easily reflected back into the java.sql.Types object to get
0557: * the integer SQL type that should be used.
0558: *
0559: * @see Types
0560: * @see #getTypeforValue(int)
0561: * @param o to test.
0562: * @return String that is a field name in the Types object.
0563: */
0564: public static String getTypeStringForObject(Object o) {
0565:
0566: if (o instanceof String)
0567: return "VARCHAR";
0568: if (o instanceof Byte)
0569: return "TINYINT";
0570: if (o instanceof Boolean)
0571: return "BOOLEAN";
0572: if (o instanceof Short)
0573: return "SMALLINT";
0574: if (o instanceof Integer)
0575: return "INTEGER";
0576: if (o instanceof Long || o instanceof BigInteger)
0577: return "BIGINT";
0578: if (o instanceof Double || o instanceof Float)
0579: return "DOUBLE";
0580: if (o instanceof BigDecimal)
0581: return "DECIMAL";
0582: if (o instanceof Timestamp)
0583: return "TIMESTAMP";
0584: if (o instanceof Time)
0585: return "TIME";
0586: if (o instanceof Ref)
0587: return "REF";
0588: if (o instanceof Date)
0589: return "DATE";
0590: if (o instanceof Clob)
0591: return "CLOB";
0592: if (o instanceof Blob)
0593: return "BLOB";
0594: if (o instanceof byte[] || o instanceof InputStream)
0595: return "LONGVARBINARY";
0596: if (o instanceof Reader)
0597: return "LONGVARCHAR";
0598: return "JAVA_OBJECT";
0599: }
0600:
0601: /**
0602: * Gets the Field name corresponding to the given type value.
0603: * <p>
0604: * Simple reflection to find the field name that has the same value given. If the value cannot be found the text
0605: * 'OTHER' will be returned.
0606: * <p>
0607: * This method will also return null if an error occurs during reflection.
0608: *
0609: * @param type that corresponds to an SQL type.
0610: * @return field name based on the value type.
0611: * @see Types
0612: */
0613: public static String getTypeforValue(int type) {
0614:
0615: try {
0616: Class c = Types.class;
0617: Field[] fields = c.getDeclaredFields();
0618: for (int i = 0; i < fields.length; i++) {
0619: String name = fields[i].getName();
0620: int value = fields[i].getInt(null);
0621: if (value == type) {
0622: return name;
0623: }
0624: }
0625: return "OTHER";
0626: } catch (Exception e) {
0627: return null;
0628: }
0629: }
0630:
0631: /**
0632: * Creates HTML friendly format for Java Color.
0633: * <p>
0634: * creates a string format of a Color object by taking the RGB color values and converting them to the hexadecimal
0635: * equivalent, for example Color.BLACK will be '#000000'
0636: * <p>
0637: * To get the color back from the value returned here, use the decode method on Color.
0638: *
0639: * @see Color#decode(java.lang.String)
0640: * @param c to encode into HTML friendly format.
0641: * @return Hexadecimal representation of the color.
0642: */
0643: public static String getHTMLColor(Color c) {
0644:
0645: String red = Integer.toHexString(c.getRed());
0646: String blu = Integer.toHexString(c.getBlue());
0647: String gre = Integer.toHexString(c.getGreen());
0648:
0649: red = (red.length() == 1 ? '0' + red : red);
0650: blu = (blu.length() == 1 ? '0' + blu : blu);
0651: gre = (gre.length() == 1 ? '0' + gre : gre);
0652: return '#' + red + gre + blu;
0653: }
0654:
0655: /**
0656: * Converts a long number representing milliseconds into human readable format.
0657: * <p>
0658: * This value is mainly for formating milli-seconds into something a bit more readable much like the
0659: * getHumanReadableTime() method.
0660: * <p>
0661: * Instead of seeing large incoherent number of milli-seconds you can see something like '1.016m'= 1 minute 1 second
0662: * or '1.05h' = 1 hour 3 minutes.
0663: *
0664: * @param milliseconds to format into a string.
0665: * @return human readable format for large milli-seconds intervals.
0666: */
0667: public static String getHumanReadableTime(long milliseconds) {
0668:
0669: long sc = 1000;
0670: long mn = 60 * sc;
0671: long hr = 60 * mn;
0672: long dy = 24 * hr;
0673:
0674: NumberFormat nf = NumberFormat.getNumberInstance();
0675: nf.setMaximumFractionDigits(3);
0676: double relSize = 0.0d;
0677: long abstime = Math.abs(milliseconds);
0678:
0679: String id = "";
0680:
0681: if ((abstime / dy) >= 1) {
0682: relSize = (double) abstime / (double) dy;
0683: id = messages.format("stringutilities.dayshorthand");
0684: } else if ((abstime / hr) >= 1) {
0685: relSize = (double) abstime / (double) hr;
0686: id = messages.format("stringutilities.hourshorthand");
0687: } else if ((abstime / mn) >= 1) {
0688: relSize = (double) abstime / (double) mn;
0689: id = messages.format("stringutilities.minuteshorthand");
0690: } else if ((abstime / sc) >= 1) {
0691: relSize = (double) abstime / (double) sc;
0692: id = messages.format("stringutilities.secondshorthand");
0693: } else {
0694: relSize = abstime;
0695: id = messages
0696: .format("stringutilities.millisecondshorthand");
0697: }
0698: return nf.format((milliseconds < 0 ? -1 : 1) * relSize) + id;
0699: }
0700:
0701: /**
0702: * Converts a long number representing milliseconds into human readable format.
0703: * <p>
0704: * This value is mainly for formating milli-seconds into something a bit more readable much like the
0705: * getHumanReadableTime() method.
0706: * <p>
0707: * Instead of seeing large incoherent number of milli-seconds you can see something like '1.016m'= 1 minute 1 second
0708: * or '1.05h' = 1 hour 3 minutes.
0709: *
0710: * @param milliseconds to format into a string.
0711: * @return human readable format for large milli-seconds intervals.
0712: */
0713: public static String getFullHumanReadableTime(long milliseconds) {
0714:
0715: long sc = 1000;
0716: long mn = 60 * sc;
0717: long hr = 60 * mn;
0718: long dy = 24 * hr;
0719:
0720: NumberFormat nf = NumberFormat.getNumberInstance();
0721: nf.setMaximumFractionDigits(0);
0722: StringBuffer buff = new StringBuffer("");
0723:
0724: double relSize = 0.0d;
0725: boolean showseconds = true;
0726: boolean showmillis = true;
0727:
0728: long abstime = Math.abs(milliseconds);
0729: String id = null;
0730:
0731: while (abstime >= 1) {
0732: buff.append(" ");
0733: if ((abstime / dy) >= 1) {
0734: showmillis = false;
0735: relSize = (double) abstime / (double) dy;
0736: abstime -= dy * (long) relSize;
0737: showseconds = false;
0738: id = messages.format("stringutilities.days");
0739: } else if ((abstime / hr) >= 1) {
0740: showmillis = false;
0741: relSize = (double) abstime / (double) hr;
0742: id = messages.format("stringutilities.hours");
0743: abstime -= hr * (long) relSize;
0744: } else if ((abstime / mn) >= 1) {
0745: showmillis = false;
0746: relSize = (double) abstime / (double) mn;
0747: id = messages.format("stringutilities.minutes");
0748: abstime -= mn * (long) relSize;
0749: } else if ((abstime / sc) >= 1) {
0750: showmillis = false;
0751: relSize = (double) abstime / (double) sc;
0752: id = messages.format("stringutilities.seconds");
0753: abstime -= sc * (long) relSize;
0754: if (!showseconds) {
0755: continue;
0756: }
0757: } else {
0758: if (showmillis) {
0759: relSize = abstime;
0760: id = messages
0761: .format("stringutilities.milliseconds");
0762: }
0763: abstime -= abstime;
0764: if (!showmillis) {
0765: continue;
0766: }
0767: }
0768:
0769: Object[] p = { nf.format(Math.floor(relSize)), id };
0770: buff.append(MessageFormat.format("{0} {1}", p));
0771: }
0772: return buff.toString().trim();
0773: }
0774:
0775: /**
0776: * Converts a long number representing bytes into human readable format.
0777: * <p>
0778: * This value is mainly for formating values like a file size, memory size and so forth, so instead of seeing a
0779: * large incoherent number you can see something like '308.123KB' or '9.68MB'
0780: *
0781: * @param bytes to format into a string.
0782: * @return human readable format for larger byte counts.
0783: */
0784: public static String getHumanReadableSize(long bytes) {
0785:
0786: long mb = (long) Math.pow(2, 20);
0787: long kb = (long) Math.pow(2, 10);
0788: long gb = (long) Math.pow(2, 30);
0789:
0790: NumberFormat nf = NumberFormat.getNumberInstance();
0791: nf.setMaximumFractionDigits(3);
0792: double relSize = 0.0d;
0793: long abytes = Math.abs(bytes);
0794: String id = "";
0795:
0796: if ((abytes / gb) >= 1) {
0797: relSize = (double) abytes / (double) gb;
0798: id = messages
0799: .format("stringutilities.gigabyte_abbreviation");
0800: } else if ((abytes / mb) >= 1) {
0801: relSize = (double) abytes / (double) mb;
0802: id = messages
0803: .format("stringutilities.megabyte_abbreviation");
0804: } else if ((abytes / kb) >= 1) {
0805: relSize = (double) abytes / (double) kb;
0806: id = messages
0807: .format("stringutilities.kilobyte_abbreviation");
0808: } else {
0809: relSize = abytes;
0810: id = messages.format("stringutilities.byte_abbreviation");
0811: }
0812: return nf.format((bytes < 0 ? -1 : 1) * relSize) + id;
0813: }
0814:
0815: /**
0816: * Removes a substring from a given string.
0817: * <p>
0818: * If source equals something like 'hello world' and the sub equals 'ell' the returned value will be 'ho world'.
0819: * Also it should be noted that this method is a case-insensitive operation, for example 'Hello World' with sub
0820: * equal to 'LO' would produce 'Hel World'.
0821: *
0822: * @param source string to remove sub string from.
0823: * @param sub to remove from source value.
0824: * @return new string with sub removed from the source string.
0825: */
0826: public static String removeSubString(String source, String sub) {
0827:
0828: StringBuffer buff = new StringBuffer(source);
0829: String upcase = source.toUpperCase();
0830:
0831: int idx = upcase.indexOf(sub.toUpperCase());
0832: int end = (idx + sub.length());
0833:
0834: if (idx >= 0 && end <= source.length()) {
0835: buff.delete(idx, end);
0836: }
0837: return buff.toString();
0838: }
0839:
0840: /**
0841: * Utility method for wrapping text to a specific width.
0842: * <p>
0843: * This method allows you to pragmatically wrap long sections of text to a specified length. This can be help ful if
0844: * the string is too long to show on one line to the user.
0845: * <p>
0846: * This also allows for HTML style breaks or '\n' style line breaks.
0847: *
0848: * @param length maximum line length to wrap lines to.
0849: * @param text to to format and wrap.
0850: * @param newLine this is text insert with each new line created i.e. '\t'.
0851: * @param htmlBreak use HTML style <br/> or '\n' line breaks.
0852: * @return formatted and wrapped text.
0853: */
0854: public static String formatBreak(int length, String text,
0855: String newLine, boolean htmlBreak) {
0856:
0857: if (text == null) {
0858: return "";
0859: }
0860:
0861: StringBuffer buff = new StringBuffer("");
0862: StringTokenizer st = new StringTokenizer(text, "\t\r\n ", false);
0863: int nicebreak = length;
0864: while (st.hasMoreTokens()) {
0865: String token = st.nextToken();
0866: if (nicebreak < buff.length() + token.length() + 2) {
0867: buff.append((htmlBreak ? "<br>" : System.getProperty(
0868: "line.seperator", "\n")));
0869: if (newLine != null) {
0870: buff.append(newLine);
0871: }
0872: nicebreak = buff.length() + length;
0873: }
0874: buff.append(token);
0875: buff.append(" ");
0876: }
0877: return buff.toString();
0878: }
0879:
0880: /**
0881: * Standard base64 encoding mechanisim.
0882: * <p>
0883: * This uses the internal sun base64 encoder.
0884: *
0885: * @param text to encode using base64.
0886: * @return encoded text using base64.
0887: */
0888: public static String encodeBase64(String text) {
0889:
0890: byte[] utf8 = encodeASCII(text).getBytes();
0891: return new sun.misc.BASE64Encoder().encode(utf8);
0892: }
0893:
0894: /**
0895: * Standard base64 decoding mechanisim.
0896: * <p>
0897: * This uses the internal sun base64 decoder.
0898: *
0899: * @param text to decode using base64.
0900: * @return original text before base64 encoding.
0901: */
0902: public static String decodeBase64(String text) {
0903:
0904: byte[] dec;
0905: try {
0906: dec = new sun.misc.BASE64Decoder().decodeBuffer(text);
0907: } catch (IOException e) {
0908: return null;
0909: }
0910: try {
0911: return decodeASCII(new String(dec));
0912: } catch (Exception error) {
0913: // TODO log decode problem//
0914: return new String(dec);
0915: }
0916: }
0917:
0918: /**
0919: * Forces the single character to be stripped from both ends of the string.
0920: * <p>
0921: * This method is mainly useful for stripping off enclosing quotation marks and the like. this method will removes
0922: * the first and last character of text, and return the result.
0923: * <p>
0924: * if the text length is less than 2 it will simply return a blank string.
0925: *
0926: * @param text to strip first and last character from.
0927: * @return stripped form of the text parameter.
0928: */
0929: public static String forceTrim(String text) {
0930:
0931: if (text.length() > 2) {
0932: return text.substring(1, text.length() - 1);
0933: }
0934: return "";
0935: }
0936:
0937: /**
0938: * Method for removing a set of characters from a source string.
0939: * <p>
0940: * Strips characters from the given set string from the source string s. for example the String 'XXJavaXxRuntime'
0941: * with the set parameter being 'Xa' will results in the following string 'JvxRuntime'
0942: * <p>
0943: * This method is very useful for removing various different characters at once from a string.
0944: *
0945: * @param s String to strip characters from.
0946: * @param set collection of characters to strip.
0947: * @return new string that is s that contains no characters from set.
0948: */
0949: public static String stripCharacters(String s, String set) {
0950:
0951: if (s == null) {
0952: return "null";
0953: }
0954:
0955: StringBuffer outBuffer = new StringBuffer("");
0956: for (int x = 0; x < s.length(); x++) {
0957: char aChar = s.charAt(x);
0958: if (set.indexOf(aChar) < 0) {
0959: outBuffer.append(aChar);
0960: }
0961: }
0962: return outBuffer.toString();
0963: }
0964:
0965: /**
0966: * Creates a comma seperated list of strings from a collection.
0967: * <p>
0968: *
0969: * @param list of strings or objects to created csv string from.
0970: * @return a comma seperated list of values from the list.
0971: * @see #explode(Collection)
0972: */
0973: public static String explode(Object[] list) {
0974:
0975: return explode(Arrays.asList(list));
0976: }
0977:
0978: /**
0979: * Creates a comma seperated list of strings from a collection.
0980: * <p>
0981: * For every object in the list, this method relys on the toString() of Object to apped to, so it is allowable to
0982: * send a collection containing non-strings.
0983: *
0984: * @param list of strings or objects to created csv string from.
0985: * @return a comma seperated list of values from the list.
0986: */
0987: public static String explode(Collection list) {
0988:
0989: return explode(list, ",");
0990: }
0991:
0992: /**
0993: * Creates a comma seperated list of strings from a collection.
0994: * <p>
0995: * For every object in the list, this method relys on the toString() of Object to apped to, so it is allowable to
0996: * send a collection containing non-strings.
0997: *
0998: * @param list of strings or objects to created csv string from.
0999: * @param delimiter delimiter to explode the list by, if null a comma (,) is used.
1000: * @return a comma seperated list of values from the list.
1001: */
1002: public static String explode(Collection list, String delimiter) {
1003:
1004: StringBuffer buffer = new StringBuffer("");
1005: Iterator itr = list.iterator();
1006:
1007: while (itr.hasNext()) {
1008: buffer.append(itr.next());
1009: if (itr.hasNext()) {
1010: buffer.append(delimiter == null ? "," : delimiter);
1011: }
1012:
1013: }
1014: return buffer.toString();
1015: }
1016:
1017: /**
1018: * Collapses a comma delimted text to a collection of strings.
1019: * <p>
1020: * This method is the opposite of the explode methods in this class.
1021: *
1022: * @see #explode(Collection)
1023: * @param list comma seperated list of values.
1024: * @param delimiter to used to distinguish the elements in the string list, for example ','.
1025: * @return collection of string from tokens in the list.
1026: */
1027: public static Collection<String> collapseToCollection(String list,
1028: String delimiter) {
1029:
1030: StringTokenizer st = new StringTokenizer(list, delimiter, false);
1031: Collection<String> collection = new ArrayList<String>();
1032: while (st.hasMoreTokens()) {
1033: collection.add(st.nextToken());
1034: }
1035: return collection;
1036: }
1037:
1038: /**
1039: * Collapses a comma delimted text to an array of strings.
1040: * <p>
1041: * This method is the opposite of the explode methods in this class.
1042: *
1043: * @see #explode(Collection)
1044: * @param list comma seperated list of values.
1045: * @param delimiter to used to distinguish the elements in the string list, for example ','.
1046: * @return collection of string from tokens in the list.
1047: */
1048: public static String[] collapseToArray(String list, String delimiter) {
1049:
1050: Collection<String> c = collapseToCollection(list, delimiter);
1051: return c.toArray(new String[c.size()]);
1052: }
1053:
1054: /**
1055: * Appends whitespace to a string to a sepecified length.
1056: * <p>
1057: * if leftFill("Bork",8) = 'Bork\ \ \ \ '
1058: * <p>
1059: * <em>spaces are escaped for clarity.</em>
1060: *
1061: * @param original text to append data to.
1062: * @param maxLength maximum size the length the text grows to.
1063: * @return string right filled with white space to maxLength.
1064: * @see #leftFill(String, int)
1065: */
1066: public static String rightFill(String original, int maxLength) {
1067:
1068: StringBuffer buffer = new StringBuffer(original);
1069: while (buffer.length() < maxLength) {
1070: buffer.append(' ');
1071: }
1072: return buffer.toString();
1073: }
1074:
1075: /**
1076: * Prepends whitespace to a string to a sepecified length.
1077: * <p>
1078: * if leftFill("Bork",8) = '\ \ \ \ Bork'
1079: * <p>
1080: * <em>spaces are escaped for clarity.</em>
1081: *
1082: * @param original text to prepend data to.
1083: * @param maxLength maximum size the length the text grows to.
1084: * @return string left filled with white space to maxLength.
1085: * @see #rightFill(String, int)
1086: */
1087: public static String leftFill(String original, int maxLength) {
1088:
1089: StringBuffer buffer = new StringBuffer(original);
1090: while (buffer.length() < maxLength) {
1091: buffer.insert(0, ' ');
1092: }
1093: return buffer.toString();
1094: }
1095:
1096: /**
1097: * Encodes MD5 Hashes into a conformed statisically unique identifier.
1098: * <p>
1099: *
1100: * @param binaryData MD5 hash from the message digest.
1101: * @return encoded hash value.
1102: * @see java.security.MessageDigest#digest()
1103: */
1104: public static String encodeMD5Data(byte[] binaryData) {
1105:
1106: if (binaryData.length != 16) {
1107: return null;
1108: }
1109:
1110: char[] buffer = new char[32];
1111: for (int i = 0; i < 16; i++) {
1112: int low = (binaryData[i] & 0x0f);
1113: int high = ((binaryData[i] & 0xf0) >> 4);
1114: buffer[i * 2] = hexDigitLow[high];
1115: buffer[i * 2 + 1] = hexDigitLow[low];
1116: }
1117: return new String(buffer);
1118: }
1119:
1120: /**
1121: * Compares to strings together to see if they are equal.
1122: * <p>
1123: * What seperates this method from a standard equals is that both strings can be null and they are still compared
1124: * safely.
1125: *
1126: * @param s1 first string to compare.
1127: * @param s2 second string to compare.
1128: * @return <tt>true</tt> if both of the given strings are equal.
1129: */
1130: public static boolean compare(String s1, String s2) {
1131:
1132: if (s1 == null && s2 == null) {
1133: return true;
1134: }
1135:
1136: String r1 = s1 == null ? "" : s1;
1137: String r2 = s2 == null ? "" : s2;
1138: return r1.compareTo(r2) == 0;
1139: }
1140:
1141: /**
1142: * Gets a normalized class name without a package namespace.
1143: * <p>
1144: * This will strip of the package name of a fully qualified class name that usually comes from
1145: * <tt>class.getName()</tt>
1146: *
1147: * @param fqcn fully qualified class name to normalize.
1148: * @return class name only.
1149: */
1150: public static String getNormalizedClassName(String fqcn) {
1151:
1152: int idx = fqcn.lastIndexOf('.');
1153: if (idx >= 0) {
1154: return fqcn.substring(idx + 1);
1155: }
1156: return fqcn;
1157: }
1158:
1159: /**
1160: * Somewhat agressive number conversion method.
1161: * <p>
1162: * This will attempt to parse normal number, local currency, and or a percentage.
1163: *
1164: * @param possibleNumber to change to a number.
1165: * @return <tt>null</tt> if the string cannot be interpreted as a number.
1166: */
1167: public static Number toNumber(String possibleNumber) {
1168:
1169: NumberFormat nf = NumberFormat.getNumberInstance();
1170: try {
1171: return nf.parse(possibleNumber);
1172: } catch (ParseException e) {
1173: // not a normal number....
1174: }
1175:
1176: nf = NumberFormat.getCurrencyInstance();
1177: try {
1178: return nf.parse(possibleNumber);
1179: } catch (ParseException e) {
1180: // not a currency ...
1181: }
1182:
1183: nf = NumberFormat.getPercentInstance();
1184: try {
1185: return nf.parse(possibleNumber);
1186: } catch (ParseException e) {
1187: // not a percentage
1188: }
1189: return null;
1190: }
1191:
1192: /**
1193: * Encrypts simple text using a key and specified transformation.
1194: * <p>
1195: * This will get an instance of the {@link Cipher} object using the specified transformation. With the given Cipher
1196: * the bytes will be transformed into a new string through the Cipher.doFinal() method.
1197: * <p>
1198: * If null or a blank string is given for text, the value is immediately returned as is.
1199: *
1200: * @param text that needs to be encrypted.
1201: * @param key to lock the text with.
1202: * @param transformation the name of the transformation, e.g., DES/CBC/PKCS5Padding.
1203: * @return encrypted string of the original text parameter.
1204: * @throws GeneralSecurityException if an error occurs during encryption.
1205: * @see Cipher#getInstance(java.lang.String)
1206: */
1207: public static String encryptText(String text, Key key,
1208: String transformation) throws GeneralSecurityException {
1209:
1210: if (text == null || text.trim().length() == 0) {
1211: return text;
1212: }
1213:
1214: byte[] utf8 = null;
1215: try {
1216: utf8 = text.getBytes("UTF8");
1217: } catch (UnsupportedEncodingException e) {
1218: // shouldn't happen since UTF8 is internal to the JVM.
1219: }
1220:
1221: Cipher cipher = Cipher.getInstance(transformation);
1222: cipher.init(Cipher.ENCRYPT_MODE, key);
1223: byte[] enc = cipher.doFinal(utf8);
1224: return new sun.misc.BASE64Encoder().encode(enc);
1225: }
1226:
1227: /**
1228: * Decrypts simple text using a key and specificed transformation.
1229: * <p>
1230: * This will get an instance of the {@link Cipher}object using the specified transformation. With the given Cipher
1231: * the bytes will be transformed into a new string through the Cipher.doFinal() method.
1232: * <p>
1233: * If null or a blank string is given for text, the value is immediately returned as is.
1234: *
1235: * @param text that needs to be decrypted.
1236: * @param key to unlock the text with.
1237: * @param transformation the name of the transformation, e.g., DES/CBC/PKCS5Padding.
1238: * @return decrypted string of the original text parameter.
1239: * @throws GeneralSecurityException if an error occurs during decryption.
1240: * @see Cipher#getInstance(java.lang.String)
1241: */
1242: public static String decryptText(String text, Key key,
1243: String transformation) throws GeneralSecurityException {
1244:
1245: if (text == null || text.trim().length() == 0) {
1246: return text;
1247: }
1248:
1249: Cipher cipher = Cipher.getInstance(transformation);
1250: cipher.init(Cipher.DECRYPT_MODE, key);
1251: byte[] dec;
1252: try {
1253: dec = new sun.misc.BASE64Decoder().decodeBuffer(text);
1254: } catch (IOException e) {
1255: return null;
1256: }
1257: byte[] utf8 = cipher.doFinal(dec);
1258: try {
1259: return new String(utf8, "UTF8");
1260: } catch (UnsupportedEncodingException e) {
1261: // shouldn't happen since UTF8 is internal to the JVM.
1262: return null;
1263: }
1264: }
1265:
1266: /**
1267: * Parses a font string to a Font object.
1268: * <p>
1269: * Complimentary method for re-creating the font objects formatted to a string by the getFontString(Font) of this
1270: * object.
1271: * <p>
1272: * for information on the format of the font string see getFontString(Font)
1273: *
1274: * @param s to parse for the font.
1275: * @return new Font object represented by the given string.
1276: */
1277: public static Font parseFontString(String s) {
1278:
1279: try {
1280: String name = s.substring(0, s.indexOf(","));
1281: int size = Integer
1282: .parseInt(s.substring(s.indexOf(",") + 1));
1283: return new Font(name, Font.PLAIN, size);
1284: } catch (Throwable t) {
1285: return null;
1286: }
1287: }
1288:
1289: private static MessageDigest allocateDigestInstance(
1290: String algorithim) {
1291:
1292: try {
1293: return MessageDigest.getInstance(algorithim);
1294: } catch (NoSuchAlgorithmException nsa) {
1295: throw new RuntimeException(nsa);
1296: }
1297:
1298: }
1299:
1300: private static char toHex(int nibble) {
1301:
1302: return hexDigit[(nibble & 0xF)];
1303:
1304: }
1305:
1306: /** A table of hex digits */
1307:
1308: private static final char[] hexDigitLow = { '0', '1', '2', '3',
1309: '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
1310:
1311: 'e', 'f' };
1312:
1313: /** A table of hex digits */
1314:
1315: private static final char[] hexDigit = { '0', '1', '2', '3', '4',
1316: '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
1317:
1318: 'F' };
1319:
1320: }
|