All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.text.Text Maven / Gradle / Ivy

The newest version!
/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2012 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/

package com.day.text;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
import java.util.Vector;

/**
 * This class holds a collection of useful string operations that are not
 * available in java.
 */
public final class Text {

    /**
     * The default format pattern used in strftime() if no pattern
     * parameter has been supplied. This is the default format used to format
     * dates in Communiqué 2
     */
    public static final String DEFAULT_DATE_FORMAT_PATTERN = "dd.MM.yyyy HH:mm:ss";

    /**
     * Common used DateFormat implementation. When the supplied formatting
     * pattern has been translated it is applied to this formatter and then
     * executed. Both steps occur in a synchronized section, such that no
     * two threads disturb each other.
     * 
    *
  1. A single instance of the formatter is used to prevent the object * creation overhead. But then how much is this ? *
  2. Using one static formatter for each thread may prove to give even * more overhead given all those synchronized blocks waiting for each * other during real formatting. But then how knows ? *
*

* This formatter must always be used synchronized as follows : *

            synchronized (dateFormatter) {
                dateFormatter.applyPattern( ... );
                dateFormatter.setTimezone( ... );
                String result = dateFormatter.format( ... );
            }
     *	
*

* To parse date and time strings , the formatter is used as follows : *

            synchronized (dateFormatter) {
                dateFormatter.applyPattern( ... );
                dateFormatter.setTimezone( ... );
                try {
                    Date result = dateFormatter.parse(dateString);
                } catch (ParseException pe) {
                    // handle exception
                }
            }
     *	
* */ private static final SimpleDateFormat dateFormatter = new SimpleDateFormat(); /** * The UTC timezone */ public static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone("UTC"); /** format for RFC 1123 date string -- "Sun, 06 Nov 1994 08:49:37 GMT" */ private final static SimpleDateFormat rfc1123Format = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss z", Locale.US); static { rfc1123Format.setTimeZone(TIMEZONE_UTC); } /** * The local timezone */ public static final TimeZone TIMEZONE_LOCAL = TimeZone.getDefault(); /** * Empty result */ private final static String[] empty = new String[0]; /** * MD4 hash algorithm name. */ private static final String MD4_HASH = "md4"; /** * NTLM hash that requires UnicodeLittleUnmarked encoding. */ private static final String NTLM_HASH = "ntlm"; /** * avoid instantiation */ private Text() { } /** * returns an array of strings decomposed of the original string, split at * every occurance of 'ch'. if 2 'ch' follow each other with no intermediate * characters, empty "" entries are avoided. * * @param str the string to decompose * @param ch the character to use a split pattern * @return an array of strings * * @deprecated use {@link #explode(String, int)} */ public static String[] split(String str, int ch) { return explode(str,ch,false); } /** * returns an array of strings decomposed of the original string, split at * every occurance of 'ch'. * @param str the string to decompose * @param ch the character to use a split pattern * @param respectEmpty if true, empty elements are generated * @return an array of strings * * @deprecated use {@link #explode(String, int, boolean)} */ public static String[] split(String str, int ch, boolean respectEmpty) { return explode(str, ch, respectEmpty); } /** * returns an array of strings decomposed of the original string, split at * every occurance of 'ch'. if 2 'ch' follow each other with no intermediate * characters, empty "" entries are avoided. * * @param str the string to decompose * @param ch the character to use a split pattern * @return an array of strings */ public static String[] explode(String str, int ch) { return explode(str,ch,false); } /** * returns an array of strings decomposed of the original string, split at * every occurance of 'ch'. * @param str the string to decompose * @param ch the character to use a split pattern * @param respectEmpty if true, empty elements are generated * @return an array of strings */ public static String[] explode(String str, int ch, boolean respectEmpty) { if (str == null) { return empty; } Vector strings = new Vector(); int pos; int lastpos = 0; // add snipples while ((pos = str.indexOf(ch, lastpos)) >= 0) { if (pos-lastpos>0 || respectEmpty) strings.add(str.substring(lastpos, pos)); lastpos = pos+1; } // add rest if (lastpos < str.length()) { strings.add(str.substring(lastpos)); } else if (respectEmpty && lastpos==str.length()) { strings.add(""); } // return stringarray return (String[]) strings.toArray(new String[strings.size()]); } /** * Concatenates all strings in the string array using the specified delimiter. * @param arr * @param delim * @return the concatenated string */ public static String implode(String[] arr, String delim) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < arr.length; i++) { if (i > 0) { buf.append(delim); } buf.append(arr[i]); } return buf.toString(); } /** * Replaces all occurences of oldString in text * with newString. * * @param text * @param oldString old substring to be replaced with newString * @param newString new substring to replace occurences of oldString * @return a string */ public static String replace(String text, String oldString, String newString) { if (text == null || oldString == null || newString == null) { throw new IllegalArgumentException("null argument"); } int pos = text.indexOf(oldString); if (pos == -1) { return text; } int lastPos = 0; StringBuffer sb = new StringBuffer(text.length()); while (pos != -1) { sb.append(text.substring(lastPos, pos)); sb.append(newString); lastPos = pos + oldString.length(); pos = text.indexOf(oldString, lastPos); } if (lastPos < text.length()) { sb.append(text.substring(lastPos)); } return sb.toString(); } /** * Returns the name part of the path * * @param path the path * @return the name part */ public static String getName(String path) { if (path == null) { return null; } else { return path.substring(path.lastIndexOf('/') + 1); } } /** * Returns the name part of the path, delimited by the given delim * * @param path the path * @param delim the delimiter * @return the name part */ public static String getName(String path, char delim) { if (path == null) { return null; } else { return path.substring(path.lastIndexOf(delim) + 1); } } /** * Same as {@link #getName(String)} but adding the possibility * to pass paths that end with a trailing '/' * * @param path the path * @param ignoreTrailingSlash if true a trailing slash is ignored * @return the name part * * @see #getName(String) */ public static String getName(String path, boolean ignoreTrailingSlash) { if (ignoreTrailingSlash && path.endsWith("/") && path.length() > 1) { path = path.substring(0, path.length()-1); } return getName(path); } /** * Returns the namespace prefix of the given qname. If the * prefix is missing, an empty string is returned. Please note, that this * method does not validate the name or prefix. *

* the qname has the format: qname := [prefix ':'] local; * * @param qname a qualified name * @return the prefix of the name or "". * * @see #getLocalName(String) * * @throws NullPointerException if qname is null */ public static String getNamespacePrefix(String qname) { int pos = qname.indexOf(':'); return pos >=0 ? qname.substring(0, pos) : ""; } /** * Returns the local name of the given qname. Please note, that * this method does not validate the name. *

* the qname has the format: qname := [prefix ':'] local; * * @param qname a qualified name * @return the localname * * @see #getNamespacePrefix(String) * * @throws NullPointerException if qname is null */ public static String getLocalName(String qname) { int pos = qname.indexOf(':'); return pos >=0 ? qname.substring(pos+1) : qname; } /** * compares to handles lexigographically with one exception: the '/' * character is always considered smaller than all other chars. this results * in a ordering, in which the parent pages come first (it's about 6 times * slower than the string impl. of compareTo). *
    example (normal string compare): *
  • /foo *
  • /foo-bar *
  • /foo/bar
  • *
*
    example (this handle compare): *
  • /foo *
  • /foo/bar
  • *
  • /foo-bar *
* * @param h1 the first handle * @param h2 the second handle * @return the return is positive, if the first handle is bigger than the * second; negative, if the first handle is smaller than the second; * and zero, if the two handles are equal. */ public static int compareHandles(String h1, String h2) { char[] ca1=h1.toCharArray(); // this is faster, than a .charAt everytime char[] ca2=h2.toCharArray(); int n= ca1.length < ca2.length ? ca1.length : ca2.length; int i=0; while (iparent as parent directory rather than a base * handle. if further respects full qualified uri's. *
examples: * * parent | path | result * ----------+----------+------------ * "" | "" | / * /foo | "" | /foo * "" | /foo | /foo * "." | foo | foo * /foo/bar | bla | /foo/bar/bla * /foo/bar | /bla | /bla * /foo/bar | ../bla | /foo/bla * /foo/bar | ./bla | /foo/bar/bla * foo | bar | foo/bar * c:/bla | gurk | c:/bla/gurk * /foo | c:/bar | c:/bar * * * @param parent the base handle * @param path the path */ public static String fullFilePath(String parent, String path) { if (parent == null) parent = ""; // compose source string char[] source; if (path == null || path.length() == 0) { // only parent path source = parent.toCharArray(); } else if (path.charAt(0) == '/' || path.indexOf(':') >= 0) { // absolute path, ignore base source = path.toCharArray(); } else { // relative to parent source = (parent + "/./" + path).toCharArray(); } return makeCanonicalPath(source); } /** * returns a full path. * if base is empty, '/' is assumed * if base and path are relative, a relative path will be generated. *
examples: *
     * base      | path     | result
     * ----------+----------+------------
     * ""        | ""       | /
     * /foo      | ""       | /foo
     * ""        | /foo     | /foo
     * "."       | foo      | foo
     * /foo/bar  | bla      | /foo/bla
     * /foo/bar  | /bla     | /bla
     * /foo/bar  | ../bla   | /bla
     * /foo/bar  | ./bla    | /foo/bla
     * foo       | bar      | bar
     * 
* * @param base the base handle * @param path the path */ public static String fullPath(String base, String path) { if (base == null) base = ""; // compose source string char[] source; if (path == null || path.length() == 0) { // only base path source = base.toCharArray(); } else if (path.charAt(0) == '/') { // absolute path, ignore base source = path.toCharArray(); } else { // relative to base source = (base + "/../" + path).toCharArray(); } return makeCanonicalPath(source); } /** * Make a path canonical. This is a shortcut for * * Text.makeCanonicalPath(new StringBuffer(path)); * * @param path path to make canonical */ public static String makeCanonicalPath(String path) { return makeCanonicalPath(path.toCharArray()); } /** * make a cannonical path. removes all /./ and /../ and multiple slashes. * @param source the input source * @return a string containing the cannonical path * * @deprecated use {@link #makeCanonicalPath(char[])} instead */ public static String makeCanonicalPath(StringBuffer source) { return makeCanonicalPath(source.toString().toCharArray()); } /** * make a cannonical path. removes all /./ and /../ and multiple slashes. * @param source the input source * @return a string containing the cannonical path */ public static String makeCanonicalPath(char[] source) { // remove/resolve .. and . int dst = 0, pos = 0, slash = 0, dots = 0, len = source.length; char last = 0; int[] slashes = new int[1024]; slashes[0] = (len > 0 && source[0] == '/') ? 0 : -1; while (pos < len) { char ch = source[pos++]; switch (ch) { case '/': // ignore multiple slashes if (last == '/') continue; // end of segment if (dots == 1) { // ignore dst = slashes[slash]; } else if (dots == 2) { // backtrack if (--slash < 0) slash = 0; dst = slashes[slash]; } else { // remember slash position slashes[++slash] = dst; } dots = 0; break; case '.': // count dots dots++; break; default: // invalidate dots dots = 3; } last = ch; if (dst >= 0) source[dst] = ch; dst++; } // check dots again if (dots == 1) { dst = slashes[slash]; } else if (dots == 2) { if (slash > 0) { slash--; } dst = slashes[slash]; } // truncate result return (dst == 0) ? "/" : new String(source, 0, dst); } /** * Determines, if two handles are sister-pages, that meens, if they * represent the same hierarchic level and share the same parent page. * @param h1 first handle * @param h2 second handle * @return true if on same level, false otherwise */ public static boolean isSibling(String h1, String h2) { int pos1 = h1.lastIndexOf('/'); int pos2 = h2.lastIndexOf('/'); return (pos1==pos2 && pos1>=0 && h1.regionMatches(0,h2,0,pos1)); } /** * Determines if the descendant handle is hierarchical a * descendant of handle. * * /content/playground/en isDescendantOf /content/playground * /content/playground/en isDescendantOf /content * /content/playground/en isNOTDescendantOf /content/designground * /content/playground/en isNOTDescendantOf /content/playground/en * * * @param handle the current handle * @param descendant the potential descendant * @return true if the descendant is a descendant; * false otherwise. * @since gumbear */ public static boolean isDescendant(String handle, String descendant) { return !handle.equals(descendant) && descendant.startsWith(handle) && (descendant.charAt(handle.length())=='/' || handle.equals("/")); } /** * Determines if the descendant handle is hierarchical a * descendant of handle or equal to it. * * /content/playground/en isDescendantOrEqualOf /content/playground * /content/playground/en isDescendantOrEqualOf /content * /content/playground/en isDescendantOrEqualOf /content/playground/en * /content/playground/en isNOTDescendantOrEqualOf /content/designground * * * @param handle the current handle * @param descendant the potential descendant * @return true if the descendant is a descendant * or equal; false otherwise. * @since gumbear */ public static boolean isDescendantOrEqual(String handle, String descendant) { return handle.equals(descendant) || (descendant.startsWith(handle) && (descendant.charAt(handle.length())=='/') || handle.equals("/")); } /** * Returns the label of a handle * @param handle the handle * @return the label * @deprecated use {@link #getName(String)} */ public static String getLabel(String handle) { return getName(handle); } /** * Returns the label of a string * @param handle the string * @param delim the delimiter * @return the label * * @deprecated use {@link #getName(String, char)} */ public static String getLabel(String handle, char delim) { return getName(handle, delim); } /** * used for the md5 */ public final static char[] hexTable="0123456789abcdef".toCharArray(); /** * Calculate an MD5 hash of the string given. * @param data the data to encode * @param enc the character encoding to use * @return a hex encoded string of the md5 digested input * @throws UnsupportedEncodingException if the given encoding is not supported */ public static String md5(String data, String enc) throws UnsupportedEncodingException { try { return digest("MD5", data.getBytes(enc)); } catch (NoSuchAlgorithmException e) { throw new InternalError("MD5 digest not available???"); } } /** * Calculate an MD5 hash of the string given. * @param data the data to encode * @return a hex encoded string of the md5 digested input * * @deprecated {@link #md5(String, String)} */ public static String md5(String data) { try { return digest("MD5", data.getBytes()); } catch (NoSuchAlgorithmException e) { throw new InternalError("MD5 digest not available???"); } } /** * Digest the plain string using the given algorithm. * * @param algorithm The alogrithm for the digest. This algorithm must be * supported by the MessageDigest class. * @param data The plain text String to be digested. * @param enc The character encoding to use * * @return The digested plain text String represented as Hex digits. * * @throws NoSuchAlgorithmException if the desired algorithm is not supported by * the MessageDigest class. * @throws UnsupportedEncodingException if the encoding is not supported */ public static String digest(String algorithm, String data, String enc) throws NoSuchAlgorithmException, UnsupportedEncodingException { if (algorithm.equalsIgnoreCase(NTLM_HASH)) { /* NTLM hash always takes UnicodeLittleUnmarked encoding */ return digest(MD4_HASH, data.getBytes("UnicodeLittleUnmarked")); } return digest(algorithm, data.getBytes(enc)); } /** * Digest the plain string using the given algorithm. * * @param algorithm The alogrithm for the digest. This algorithm must be * supported by the MessageDigest class. * @param data The plain text String to be digested. * * @return The digested plain text String represented as Hex digits. * * @throws NoSuchAlgorithmException if the desired algorithm is not supported by * the MessageDigest class. * * @deprecated use {@link #digest(String, String, String)} */ public static String digest(String algorithm, String data) throws NoSuchAlgorithmException { return digest(algorithm, data.getBytes()); } /** * Digest the plain string using the given algorithm. * * @param algorithm The alogrithm for the digest. This algorithm must be * supported by the MessageDigest class. * @param data the data to digest with the given algorithm * * @return The digested plain text String represented as Hex digits. * * @throws NoSuchAlgorithmException if the desired algorithm is not supported by * the MessageDigest class. */ public static String digest(String algorithm, byte[] data) throws NoSuchAlgorithmException { byte[] digest; if (algorithm.equalsIgnoreCase(MD4_HASH)) { digest = MD4.digest(data); } else { MessageDigest md = MessageDigest.getInstance(algorithm); digest = md.digest(data); } StringBuffer res = new StringBuffer(digest.length * 2); for (int i=0; i>4)&15]); res.append(hexTable[b&15]); } return res.toString(); } /** * Returns the nth relative parent of the handle, where n=level. *

Example:
* * Text.getRelativeParent("/en/home/index/about", 1) == "/en/home/index" * * * @param handle the handle of the page * @param level the level of the parent */ public static String getRelativeParent(String handle, int level) { int idx = handle.length(); while (level > 0) { idx = handle.lastIndexOf('/',idx-1); if (idx < 0) { return ""; } level--; } return handle.substring(0,idx); } /** * Returns the nth absolute parent of the handle, where n=level. *

Example:
* * Text.getAbsoluteParent("/en/home/index/about", 1) == "/en/home" * * * @param handle the handle of the page * @param level the level of the parent */ public static String getAbsoluteParent(String handle, int level) { int idx = 0; int len = handle.length(); while (level >= 0 && idx=0 ? "" : handle.substring(0,idx); } /** * The list of characters that are not encoded by the escape() * and unescape() methods. They contains the characters as * defined 'unreserved' in section 2.3 of the RFC 2396 'URI genric syntax': * *

     * unreserved  = alphanum | mark
     * mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
     * 
*/ public static BitSet URISave; /** * Same as {@link #URISave} but also contains the '/' */ public static BitSet URISaveEx; static { URISave = new BitSet(256); int i; for (i = 'a'; i <= 'z'; i++) { URISave.set(i); } for (i = 'A'; i <= 'Z'; i++) { URISave.set(i); } for (i = '0'; i <= '9'; i++) { URISave.set(i); } URISave.set('-'); URISave.set('_'); URISave.set('.'); URISave.set('!'); URISave.set('~'); URISave.set('*'); URISave.set('\''); URISave.set('('); URISave.set(')'); URISaveEx = (BitSet) URISave.clone(); URISaveEx.set('/'); } /** * Does an URL encoding of the string using the * escape character. The characters that don't need encoding * are those defined 'unreserved' in section 2.3 of the 'URI genric syntax' * RFC 2396, but without the escape character. * * @param string the string to encode. * @param escape the escape character. * @return the escaped string * * @throws NullPointerException if string is null. */ public static String escape(String string, char escape) { return escape(string, escape, false); } /** * Does an URL encoding of the string using the * escape character. The characters that don't need encoding * are those defined 'unreserved' in section 2.3 of the 'URI genric syntax' * RFC 2396, but without the escape character. If isPath is * true, additionally the slash '/' is ignored, too. * * @param string the string to encode. * @param escape the escape character. * @param isPath if true, the string is treated as path * * @return the escaped string * * @throws NullPointerException if string is null. */ public static String escape(String string, char escape, boolean isPath) { try { BitSet validChars = isPath ? URISaveEx : URISave; byte[] bytes = string.getBytes("utf-8"); StringBuffer out = new StringBuffer(bytes.length); for (int i = 0; i < bytes.length; i++) { int c = bytes[i]&0xff; if (validChars.get(c) && c!=escape) { out.append((char)c); } else { out.append(escape); out.append(hexTable[(c>>4) & 0x0f]); out.append(hexTable[(c ) & 0x0f]); } } return out.toString(); } catch (UnsupportedEncodingException e) { throw new InternalError(e.toString()); } } /** * Escapes all character that have specific meaning in XMl/HTML, namely * '<', '>', '"', '&' by their isolating encoding aequivalents * "&lt;", "&gt", "&quot;", "&amp;" * * @param string the string to escape * @return the escaped string or null if the input string was * null */ public static String escapeXml(String string) { if (string == null) { return null; } final int len = string.length(); if (len == 0) { return string; } StringBuffer ret = new StringBuffer(len); for (int i=0; i': ret.append(">"); break; default: ret.append(c); } } return ret.toString(); } /** * Does a URL encoding of the string. The characters that * don't need encoding are those defined 'unreserved' in section 2.3 of * the 'URI genric syntax' RFC 2396. * * @param string the string to encode * @return the escaped string * * @throws NullPointerException if string is null. */ public static String escape(String string) { return escape(string, '%'); } /** * Does a URL encoding of the path. The characters that * don't need encoding are those defined 'unreserved' in section 2.3 of * the 'URI genric syntax' RFC 2396. In contrast to the * {@link #escape(String)} method, not the entire path string is escaped, * but every individual part (i.e. the slashes are not escaped). * * @param path the path to encode * @return the escaped path * * @throws NullPointerException if path is null. */ public static String escapePath(String path) { return escape(path, '%', true); } /** * Does a URL decoding of the string using the * escape character. Please note that in opposite to the * {@link java.net.URLDecoder} it does not transform the + into spaces. * @param string the string to decode * @param escape the escape character * @return the decoded string * * @throws NullPointerException if string is null. * @throws ArrayIndexOutOfBoundsException if not enough character follow an * escape character * @throws IllegalArgumentException if the 2 characters following the escape * character do not represent a hex-number. */ public static String unescape(String string, char escape) { ByteArrayOutputStream out = new ByteArrayOutputStream(string.length()*2); int lastPos=0; int pos; while ((pos=string.indexOf(escape, lastPos))>=0) { try { out.write(string.substring(lastPos, pos).getBytes("utf-8")); } catch (IOException e) { throw new InternalError(e.toString()); } lastPos = pos+3; if (lastPos<=string.length()) { try { out.write(Integer.parseInt(string.substring(pos+1, lastPos),16)); } catch (NumberFormatException e) { throw new IllegalArgumentException(); } } else { // not enough characters, ignore rest } } if (lastPos>=0 && lastPos<=string.length()) { try { out.write(string.substring(lastPos).getBytes("utf-8")); } catch (IOException e) { throw new InternalError(e.toString()); } } try { return new String(out.toByteArray(), "utf-8"); } catch (UnsupportedEncodingException e) { throw new InternalError(e.toString()); } } /** * Does a URL decoding of the string. Please note that in * opposite to the {@link java.net.URLDecoder} it does not transform the + * into spaces. * * @param string the string to decode * @return the decoded string * * @throws NullPointerException if string is null. * @throws ArrayIndexOutOfBoundsException if not enough character follow an * escape character * @throws IllegalArgumentException if the 2 characters following the escape * character do not represent a hex-number. */ public static String unescape(String string) { return unescape(string, '%'); } /** * Returns a stringified date accoring to the date format specified in * RTF1123. this is of the form: "Sun, 06 Nov 1994 08:49:37 GMT" */ public static String dateToRfc1123String(Date date) { synchronized (rfc1123Format) { return rfc1123Format.format(date); } } /** * Implements a date formatting routine supporting (a subset) of the POSIX * strftime() function. * * @param date The date value to be formatted * @param formatPattern The pattern used to format the date. This pattern * supports a subset of the pattern characters of the POSIX * strftime() function. If this pattern is empty or * null the default pattern * dd.MM.yyyy HH:mm:ss is used. * @param zone Defines for which time zone the date should be outputted. If * this parameter is null, then the local time zone is taken. * * @return the formatted date as a String. */ public static String strftime(Date date, String formatPattern, TimeZone zone) { // Check whether to apply default format if (formatPattern == null || formatPattern.length() == 0) { formatPattern = DEFAULT_DATE_FORMAT_PATTERN; } else { formatPattern = convertFormat(formatPattern); } // check zone if (zone == null) zone = TIMEZONE_LOCAL; // Reuse the global SimpleDateFormat synchronizing on it to prevent // multiple tasks from interfering with each other synchronized(dateFormatter) { dateFormatter.applyPattern(formatPattern); dateFormatter.setTimeZone(zone); return dateFormatter.format(date); } } /** * Implements a date formatting routine supporting (a subset) of the POSIX * strftime() function. * * @param date The date value to be formatted * @param formatPattern The pattern used to format the date. This pattern * supports a subset of the pattern characters of the POSIX * strftime() function. If this pattern is empty or * null the default pattern * dd.MM.yyyy HH:mm:ss is used. * @param asUTC Defines whether to interpret the date as belong to the UTC * time zone or the local time zone. */ public static String strftime(Date date, String formatPattern, boolean asUTC) { return strftime(date, formatPattern, asUTC ? TIMEZONE_UTC : TIMEZONE_LOCAL); } /** * Implements a date formatting routine supporting (a subset) of the POSIX * strftime() function. The default pattern * dd.MM.yyyy HH:mm:ss is used to format the date. * * @param date The date value to be formatted * @param asUTC Defines whether to interpret the date as belong to the UTC * time zone or the local time zone. */ public static String strftime(Date date, boolean asUTC) { return strftime(date, null, asUTC); } /** * Implements a date formatting routine supporting (a subset) of the POSIX * strftime() function. The default pattern * dd.MM.yyyy HH:mm:ss is used to format the date, which is * interpreted to be in the local time zone. * * @param date The date value to be formatted */ public static String strftime(Date date) { return strftime(date, null, false); } /** * Parses the date string based on the format pattern which is in the * format used by the Java platfrom SimpleDateFormat class. * * @param dateString The date string to be parsed * @param formatPattern the pattern to use for parsing. If null * or empty, the same default pattern is used as with * {@link #strftime(Date, String, boolean)}, namely * dd.MM.yyyy HH:mm:ss. * * @throws ParseException if the date string cannot be parsed accordinung * to the format pattern. */ public static Date parseDate(String dateString, String formatPattern) throws ParseException { return parseDate(dateString, formatPattern, false); } /** * Parses the date string based on the format pattern which is in the * format used by the Java platfrom SimpleDateFormat class. * * @param dateString The date string to be parsed * @param formatPattern the pattern to use for parsing. If null * or empty, the same default pattern is used as with * {@link #strftime(Date, String, boolean)}, namely * dd.MM.yyyy HH:mm:ss. * @param isUTC if true the date string is considered in UTC, * otherwise the default timezone of the host is used. * * @throws ParseException if the date string cannot be parsed accordinung * to the format pattern. */ public static Date parseDate(String dateString, String formatPattern, boolean isUTC) throws ParseException { synchronized (dateFormatter) { dateFormatter.applyPattern(formatPattern); if (isUTC) { dateFormatter.setTimeZone(TIMEZONE_UTC); } else { dateFormatter.setTimeZone(TimeZone.getDefault()); } return dateFormatter.parse(dateString); } } /** * Parses the date string based on the format pattern which is in the * default format dd.MM.yyyy HH:mm:ss. * * @param dateString The date string to be parsed * * @throws ParseException if the date string cannot be parsed accordinung * to the format pattern. */ public static Date parseDate(String dateString) throws ParseException { return parseDate(dateString, DEFAULT_DATE_FORMAT_PATTERN, false); } /** * Parses the date string based on the format pattern which is in the * default format dd.MM.yyyy HH:mm:ss. * * @param dateString The date string to be parsed * @param isUTC if true the date string is considered in UTC, * otherwise the default timezone of the host is used. * * * @throws ParseException if the date string cannot be parsed accordinung * to the format pattern. */ public static Date parseDate(String dateString, boolean isUTC) throws ParseException { return parseDate(dateString, DEFAULT_DATE_FORMAT_PATTERN, isUTC); } //--------- sprintf() formatting constants --------------------------------- /** left justified - '-' flag */ private static final int FLAG_LJ = 1; /** always show sign - '+' flag */ private static final int FLAG_SI = 2; /** space placeholder for omitted plus sign - ' ' flag, ignore if SI */ private static final int FLAG_SP = 3; /** zero padded if right aligned - '0' flag, ignore if LJ */ private static final int FLAG_ZE = 4; /** * alternate format - '#' flag : * SI - incr. precision to have zero as first char * x - prefix '0x' * X - prefix '0X' * eEf - always show decimal point, omit trailing zeroes * gG - always show decimal point, show trailing zeroes */ private static final int FLAG_AL = 5; /** interpret ints as short - 'h' size */ private static final int FLAG_SHORT = 8; /** interpret ints as long - 'l' size */ private static final int FLAG_LONG = 9; /** waiting for format */ private static final int PARSE_STATE_NONE = 0; /** parsing flags */ private static final int PARSE_STATE_FLAGS = 1; /** parsing wdth */ private static final int PARSE_STATE_WIDTH = 2; /** parsing precision */ private static final int PARSE_STATE_PRECISION = 3; /** parsing size */ private static final int PARSE_STATE_SIZE = 4; /** parsing type */ private static final int PARSE_STATE_TYPE = 5; /** parsing finished */ private static final int PARSE_STATE_END = 6; /** incomplete pattern at end of format string, throw error */ private static final int PARSE_STATE_ABORT = 7; /** end of format string during plain text */ private static final int PARSE_STATE_TERM = 8; /** * This method implements the famous and ubiquituous sprintf * formatter in Java. The arguments to the method are the formatting string * and the list of arguments defined by the formatting instructions * contained in the formatting string. *

* Each element in the argument array is either a Number object * or any other Object type. Whenever the formatting string * stipulates the corresponding argument to be numeric, it is assumed this * array element to be a Number. If this is not the case an * IllegalArgumentException is thrown. If a String * argument is stipulated by the formatting string, a simple call to the * toString() method of the object yields the * String required. *

* SPECIFICATION *

* sprintf accepts a series of arguments, applies to each a * format specifier from format, and stores the formatted data * to the result Strint. The method throws an * IllegalArgumentException if either the format * is incorrect, there are not enough arguments for the format * or if any of the argument's type is wrong. sprintf returns * when it reaches the end of the format string. If there are more * arguments than the format requires, excess arguments are ignored. *

* format is a String containing two types of * objects: ordinary characters (other than %), which * are copied unchanged to the output, and conversion specifications, * each of which is introduced by %. (To include * % in the output, use %% in the format * string.) A conversion specification has the following form: *

* * [ flags ] [ width ] [ "." prec ] [ size ] * type * *
*

* The fields of the conversion specification have the following meanings: * *

*
flags *
an optional sequence of characters which control output * justification, numeric signs, decimal points, trailing zeroes, and * octal and hex prefixes. The flag characters are minus (-), * plus (+), space (" "), zero (0), * and sharp (# ). They can appear in any combination. * * * * * * * * * * * * * * * * * * * * * * *
-The result of the conversion is left justified, and the right * is padded with blanks. If you do not use this flag, the result * is right justified, and padded on the left.
+The result of a signed conversion (as determined by type) * will always begin with a plus or minus sign. (If you do not use * this flag, positive values do not begin with a plus sign.)
" " (space)If the first character of a signed conversion specification is * not a sign, or if a signed conversion results in no characters, * the result will begin with a space. If the space * ( ) flag and the plus (+) flag both * appear, the space flag is ignored.
0If the type is d, i, * o, u, x, X, * e, E, f, g, * or G: leading zeroes, are used to pad the field * width (following any indication of sign or base); no spaces * are used for padding. If the zero (0) and minus * (-) flags both appear, the zero (0) * flag will be ignored. For d, i, * o, u, x, X * conversions, if a precision prec is specified, the zero * (0) flag is ignored. *
* Note that 0 is interpreted as a flag, not as the * beginning of a field width.
#The result is to be converted to an alternative form, according * to the type character: *
*
o *
Increases precision to force the first digit of the result * to be a zero. * *
x *
A non-zero result will have a 0x prefix. * *
X *
A non-zero result will have a 0X prefix. * *
e, E, or f *
The result will always contain a decimal point even if no * digits follow the point. (Normally, a decimal point appears * only if a digit follows it.) Trailing zeroes are removed. * *
g or G *
Same as e or E, but trailing * zeroes arenot removed. * *
all others *
Undefined. *
* *
width *
width is an optional minimum field width. You can either * specify it directly as a decimal integer, or indirectly by using instead * an asterisk (*), in which case an integral numeric argument * is used as the field width. Negative field widths are not supported; if * you attempt to specify a negative field width, it is interpreted as a * minus (i) flag followed by a positive field width. * *
prec *
an optional field; if present, it is introduced with * `.' (a period). This field gives the maximum number of * characters to print in a conversion; the minimum number of digits of an * integer to print, for conversions with type d, * i, o, u, x, and * X; the maximum number of significant digits, for the * g and G conversions; or the number of digits * to print after the decimal point, for e, E, * and f conversions. You can specify the precision either * directly as a decimal integer or indirectly by using an asterisk * (*), in which case an integral numeric argument is used as * the precision. Supplying a negative precision is equivalent to * omitting the precision. If only a period is specified the precision * is zero. If a precision appears with any other conversion type * than those listed here, the behavior is undefined. * *
size *
h, l, and L are optional size * characters which override the default way that sprintf * interprets the data type of the corresponding argument. * h forces the following d, i, * o, u, x, or X * conversion type to apply to a short or unsigned * short. Similarily, an l forces the following * d, i, o, u, * x, or X conversion type to apply to * a long or unsigned long. If an h * or an l appears with another conversion specifier, the * behavior is undefined. L forces a following * e, E, f, g, or * G conversion type to apply to a long * double argument. If L appears with any other * conversion type, the behavior is undefined. * *
type *
type specifies what kind of conversion sprintf * performs. Here is a table of these: * *
*
% *
prints the percent character (%) * *
c *
prints arg as single character. That is the argument is * converted to a String of which only the first * character is printed. * *
s *
Prints characters until precision is reached or the * String ends; takes any Object whose * toString() method is called to get the * String to print. * *
d *
prints a signed decimal integer; takes a Number (same * as i) * *
d *
prints a signed decimal integer; takes a Number (same * as d) * *
d *
prints a signed octal integer; takes a Number * *
u *
prints a unsigned decimal integer; takes a Number. * This conversion is not supported correctly by the Java * implementation and is really the same as i. * *
x *
prints an unsigned hexadecimal integer (using abcdef as * digits beyond 9; takes a Number * *
X *
prints an unsigned hexadecimal integer (using ABCDEF as * digits beyond 9; takes a Number * *
f *
prints a signed value of the form [-]9999.9999; takes a * Number * *
e *
prints a signed value of the form [-]9.9999e[+|-]999; takes * a Number * *
E *
prints the same way as e, but using E to * introduce the exponent; takes a Number * *
g *
prints a signed value in either f or e * form, based on given value and precision &emdash; trailing zeros * and the decimal point are printed only if necessary; takes a * Number * *
G *
prints the same way as g, but using E * for the exponent if an exponent is needed; takes a * Number * *
n *
Not supported in the Java implementation, throws an * IllegalArgumentException if used. * *
p *
Not supported in the Java implementation, throws an * IllegalArgumentException if used. *
* *
* *

* IMPLEMENTATION NOTES *

* Due to the nature of the Java programming language, neither pointer * conversions, nor unsigned and long double * conversions are supported. *

* Also the Java implementation only distinguishes between * Number and other Object arguments. If a * numeric argument is expected as per the format * String, the current argument must be a Number * object that is converted to a basic type as expected. If a * String or char argument is expected, any * Object is valid, whose toString() method is used to convert * to a String. * * @param format The format string as known from the POSIX sprintf() * C function. * @param args The list of arguments to accomodate the format string. This * argument list is supposed to contain at least as much entries as * there are formatting options in the format string. If for a * numeric option, the entry is not a number an * IllegalArgumentException is thrown. * * @return The formatted String. An empty String * is only returned if the format String * is empty. A null value is never returned. * * @throws NullPointerException if the formatting string or any of the * argument values is null. * @throws IllegalArgumentException if the formatting string has wrong * format tags, if the formatting string has an incomplete * formatting pattern at the end of the string, if the argument * vector has not enough values to satisfy the formatting string * or if an argument's type does not match the requirements of the * format string. * */ public static String sprintf(String format, Object[] args) { // Return immediately if we have no arguments .... if (format == null) { throw new NullPointerException("format"); } if (format.length() == 0) { return ""; } // Get the format string char[] s = format.toCharArray(); // prepare the result, initial size has no sound basis StringBuffer res = new StringBuffer( s.length * 3 / 2 ); for (int i=0,j=0, length=format.length(); i < length; ) { int parse_state = PARSE_STATE_NONE; BitSet flags = new BitSet(16); int width = 0; int precision = -1; char fmt = ' '; // find a start of a formatting ... while (parse_state == PARSE_STATE_NONE) { if (i >= length) parse_state = PARSE_STATE_TERM; else if (s[i] == '%') { if (i < length - 1) { if (s[i + 1] == '%') { res.append('%'); i++; } else { parse_state = PARSE_STATE_FLAGS; } } else { throw new java.lang.IllegalArgumentException( "Incomplete format at end of format string"); } } else { res.append(s[i]); } i++; } // Get flags, if any while (parse_state == PARSE_STATE_FLAGS) { if (i >= length) parse_state = PARSE_STATE_ABORT; else if (s[i] == ' ') flags.set(FLAG_SP); else if (s[i] == '-') flags.set(FLAG_LJ); else if (s[i] == '+') flags.set(FLAG_SI); else if (s[i] == '0') flags.set(FLAG_ZE); else if (s[i] == '#') flags.set(FLAG_AL); else { parse_state = PARSE_STATE_WIDTH; i--; } i++; } // Get width specification while (parse_state == PARSE_STATE_WIDTH) { if (i >= length) { parse_state = PARSE_STATE_ABORT; } else if ('0' <= s[i] && s[i] <= '9') { width = width * 10 + s[i] - '0'; i++; } else { // finished with digits or none at all // if width is a '*' take width from arg if (s[i] == '*') { // Check whether we have an argument if (j >= args.length) { throw new IllegalArgumentException("Missing " + "argument for the width"); } try { width = ((Number)(args[j++])).intValue(); } catch (ClassCastException cce) { // something wrong with the arg throw new IllegalArgumentException("Width " + "argument is not numeric"); } i++; } // if next is a dot, then we have a precision, else a size if (s[i] == '.') { parse_state = PARSE_STATE_PRECISION; precision = 0; i++; } else { parse_state = PARSE_STATE_SIZE; } } } // Get precision while (parse_state == PARSE_STATE_PRECISION) { if (i >= length) { parse_state = PARSE_STATE_ABORT; } else if ('0' <= s[i] && s[i] <= '9') { precision = precision * 10 + s[i] - '0'; i++; } else { // finished with digits or none at all // if width is a '*' take precision from arg if (s[i] == '*') { // Check whether we have an argument if (j >= args.length) { throw new IllegalArgumentException("Missing " + "argument for the precision"); } try { width = ((Number)(args[j++])).intValue(); } catch (ClassCastException cce) { // something wrong with the arg throw new IllegalArgumentException("Precision " + "argument is not numeric"); } i++; } parse_state = PARSE_STATE_SIZE; } } // Get size character if (parse_state == PARSE_STATE_SIZE) { if (i >= length) parse_state = 6; else { if (s[i] == 'h') { flags.set(FLAG_SHORT); i++; } else if (s[i] == 'l' || s[i] == 'L') { flags.set(FLAG_LONG); i++; } parse_state = PARSE_STATE_TYPE; } } // Get format character if (parse_state == PARSE_STATE_TYPE) { if (i >= length) parse_state = PARSE_STATE_ABORT; else { fmt = s[i]; i++; parse_state = PARSE_STATE_END; } } // Now that we have anything, format it .... if (parse_state == PARSE_STATE_END) { // Check whether we have an argument if (j >= args.length) { throw new IllegalArgumentException("Not enough parameters for the format string"); } try { // Convert the argument according to the flag switch (fmt) { case 'd': // decimal - fall through case 'i': // integral - fall through case 'x': // hexadecimal, lower case - fall through case 'X': // hexadecimal, upper case - fall through case 'o': // octal - fall through case 'u': // unsigned (not really supported) format(res, (Number)args[j], fmt, width, precision, flags); break; case 'f': // float - fall through case 'e': // exponential, lower case - fall through case 'E': // exponential, upper case - fall through case 'g': // float or exp., lower case - fall through case 'G': // float or exp., upper case - fall through format(res, ((Number)args[j]).doubleValue(), fmt, width, precision, flags); break; case 'c': // character precision = 1; // fall through case 's': // string String val = args[j].toString(); if (val.length() > precision && precision > 0) { val = val.substring(0, precision); } flags.clear(FLAG_ZE); format(res, val, "", width, flags); break; default : // unknown format throw new IllegalArgumentException("Unknown " + "conversion type " + fmt); } } catch (ClassCastException cce) { // something wrong with the arg throw new IllegalArgumentException("sprintf: Argument #" + j + " of type " + args[j].getClass().getName() + " does not match format " + fmt); } // goto the next argument j++; } // if the format string is not complete if (parse_state == PARSE_STATE_ABORT) { throw new java.lang.IllegalArgumentException( "Incomplete format at end of format string"); } } // while i return res.toString(); } /** * Formats a string according to format and argument. This method only * supports string formats. See {@link #sprintf(String, Object[])} for details. * * @param format The format string * @param a0 The single parameter * * @return the result from sprintf(format, new Object[]{ a0 }). * * @throws IllegalArgumentException from {@link #sprintf(String, Object[])}. */ public static String sprintf(String format, Object a0) { return sprintf(format, new Object[]{a0}); } /** * Formats a string according to format and argument. This method only * supports string formats. See {@link #sprintf(String, Object[])} for details. * * @param format The format string * @param a0 The first parameter * @param a1 The second parameter * * @return the result from sprintf(format, new Object[]{ ... }). * * @throws IllegalArgumentException from {@link #sprintf(String, Object[])}. */ public static String sprintf(String format, Object a0, Object a1) { return sprintf(format, new Object[]{a0, a1}); } /** * Formats a string according to format and argument. This method only * supports string formats. See {@link #sprintf(String, Object[])} for details. * * @param format The format string * @param a0 The first parameter * @param a1 The second parameter * @param a2 The thrid parameter * * @return the result from sprintf(format, new Object[]{ ... }). * * @throws IllegalArgumentException from {@link #sprintf(String, Object[])}. */ public static String sprintf(String format, Object a0, Object a1, Object a2) { return sprintf(format, new Object[]{a0, a1, a2}); } /** * Formats a string according to format and argument. This method only * supports string formats. See {@link #sprintf(String, Object[])} for details. * * @param format The format string * @param a0 The first parameter * @param a1 The second parameter * @param a2 The thrid parameter * @param a3 The fourth parameter * * @return the result from sprintf(format, new Object[]{ ... }). * * @throws IllegalArgumentException from {@link #sprintf(String, Object[])}. */ public static String sprintf(String format, Object a0, Object a1, Object a2, Object a3) { return sprintf(format, new Object[]{a0, a1, a2, a3}); } /** * Formats a string according to format and argument. This method only * supports string formats. See {@link #sprintf(String, Object[])} for details. * * @param format The format string * @param a0 The first parameter * @param a1 The second parameter * @param a2 The thrid parameter * @param a3 The fourth parameter * @param a4 The fifth parameter * * @return the result from sprintf(format, new Object[]{ ... }). * * @throws IllegalArgumentException from {@link #sprintf(String, Object[])}. */ public static String sprintf(String format, Object a0, Object a1, Object a2, Object a3, Object a4) { return sprintf(format, new Object[]{a0, a1, a2, a3, a4}); } /** The list of characters that valid label characters */ private final static String[] labelCharMapping = new String[] { "_","_","_","_","_","_","_","_","_","_","_","_","_","_","_","_", "_","_","_","_","_","_","_","_","_","_","_","_","_","_","_","_", "_","_","_","_","_","_","_","_","_","_","_","_","_","-","_","_", "0","1","2","3","4","5","6","7","8","9","_","_","_","_","_","_", "_","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o", "p","q","r","s","t","u","v","w","x","y","z","_","_","_","_","_", "_","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o", "p","q","r","s","t","u","v","w","x","y","z","_","_","_","_","_", "_","f","_","_","_","fi","fi","_","_","_","_","_","_","_","_","_", "_","_","_","_","_","_","_","_","_","_","_","_","y","_","_","_", "_","i","c","p","o","v","_","s","_","_","_","_","_","_","_","_", "_","_","_","_","_","_","_","_","_","_","_","_","_","_","_","_", "a","a","a","a","ae","a","ae","c","e","e","e","e","i","i","i","i", "d","n","o","o","o","o","oe","x","o","u","u","u","ue","y","b","ss", "a","a","a","a","ae","a","ae","c","e","e","e","e","i","i","i","i", "o","n","o","o","o","o","oe","_","o","u","u","u","ue","y","b","y" }; /** * Non-valid handle characters: / \ : * ? " < > | and carriage return.
* (That's slash, backslash, colon, asterisk, question mark, quotation mark, * less than symbol, greater than symbol, pipe, and carriage return.) */ public final static String NON_VALID_CHARS = "/\\:*?\"<>|\n"; /** * Create a valid label out of an arbitrary string. * @param labelhint the given label string that we will examine * and convert any illegal character if necessary * @return a valid label string */ public static String createValidLabel(String labelhint) { char[] chrs=labelhint.toCharArray(); StringBuffer labelbase = new StringBuffer(chrs.length); for (int idx=0; idx=0 && c16) { break; } labelbase.append(repl); } return labelbase.toString(); } /** * Checks if the label is not empty and contains all valid label chars. * @param labelhint the labelhint to check * @throws IllegalArgumentException if the label does not contain all valid * chars. */ public static void validateLabel(String labelhint) { if (labelhint==null || labelhint.equals("")) { throw new IllegalArgumentException("Exact Label must not be empty."); } // bug 8262 - do not allow '.' and '..' if (".".equals(labelhint) || "..".equals(labelhint)) { throw new IllegalArgumentException("Exact Label must not be ''.'' or ''..''"); } for (int idx=0; idx=0 || labelhint.charAt(idx) > 127) { throw new IllegalArgumentException("Exact Label contains illegal character: \'" + labelhint.charAt(idx) + "\'"); } } } //---------- internal ------------------------------------------------------ /** * Convert a Date formatting string in POSIX strftime() format to the * pattern format used by the Java SimpleDateFormat class. * * These are the symbols used in SimpleDateFormat to which we convert * our strftime() symbols. * *

* * * * * * * * * * * * * * * * * * * * * *
SymbolMeaningPresentationExample
Gera designator (Text) AD
yyear (Number) 1996
Mmonth in year (Text & Number)July & 07
dday in month (Number) 10
hhour in am/pm (1~12)(Number) 12
Hhour in day (0~23) (Number) 0
mminute in hour (Number) 30
ssecond in minute (Number) 55
Smillisecond (Number) 978
Eday in week (Text) Tuesday
Dday in year (Number) 189
Fday of week in month(Number) 2 (2nd Wed in July)
wweek in year (Number) 27
Wweek in month (Number) 2
aam/pm marker (Text) PM
khour in day (1~24) (Number) 24
Khour in am/pm (0~11)(Number) 0
ztime zone (Text) Pacific Standard Time
'escape for text (Delimiter) 
''single quote (Literal) '
* * @param posixFormat The formatting pattern in POSIX strftime() format. * * @return The Date formatting pattern in SimpleDateFormat pattern format. * * todo: Check for the more or less complete support of all pattern tags. */ private static String convertFormat(String posixFormat) { char[] format = posixFormat.toCharArray(); StringBuffer jFormat = new StringBuffer(format.length); boolean inString = false; for (int i = 0; i < format.length; i++) { if (format[i] == '\'') { // insert a second tick jFormat.append('\''); } else if (format[i] == '%') { if (inString) { jFormat.append('\''); inString = false; } switch (format[++i]) { case'%': // just a single '%' jFormat.append('%'); break; case 'a': // locale's abbreviated weekday name jFormat.append("EEE"); break; case 'A': // locale's full weekday name jFormat.append("EEEE"); break; case 'b': // locale's abbreviated month name jFormat.append("MMM"); break; case 'B': // locale's full month name jFormat.append("MMMM"); break; case 'c': // locale's appropriate date and time representation break; case 'C': // century number as a decimal number (00-99) // Not supported break; case 'd': // day of month (01-31) jFormat.append("dd"); break; case 'D': // date as %m/%d/%y jFormat.append("MM/dd/yy"); break; case 'e': // day of month ( 1-31) jFormat.append("d"); break; case 'h': // locale's abbreviated month name jFormat.append("MMM"); break; case 'H': // hour (00-23) jFormat.append("HH"); break; case 'I': // hour (01-12) jFormat.append("hh"); break; case 'j': // day number of year (001-366) jFormat.append("DDD"); break; case 'K': // plus C !!! if (format[++i] == 'C') { // locale's appropriate date and time representation } break; case 'm': // month number (01-12) jFormat.append("MM"); break; case 'M': // minute (00-59) jFormat.append("mm"); break; case 'n': // new line jFormat.append( System.getProperty("line.separator", "\n") ); break; case 'p': // locale's equivalent of either AM and PM jFormat.append("aa"); break; case 'r': // locale's 12-hour time representation, default %I:%M:%S [AM|PM] jFormat.append("hh:mm:ss aa"); break; case 'R': // time as %H:%M jFormat.append("hh:mm"); break; case 'S': // seconds (00-61) [ leap seconds ;-) ] jFormat.append("ss"); break; case 't': // tab character jFormat.append( '\t' ); break; case 'T': // time as %H:%M:%S jFormat.append("HH:mm:ss"); break; case 'U': // week number of year (00-53), sunday is first day of week jFormat.append("ww"); break; case 'w': // weekday number (0-6, 0=sunday) jFormat.append("E"); break; case 'W': // week number of year (00-53), monday is first day of week jFormat.append("ww"); break; case 'x': // locale's appropriate date representation break; case 'X': // locale's appropriate time representation break; case 'y': // year within century (00-99) jFormat.append("yy"); break; case 'Y': // year as %c%y (e.g. 1986) jFormat.append("yyyy"); break; case 'Z': // time zone name or no characters if no time zone exists jFormat.append("zzz"); break; default: // ignore } } else if (Character.isLetterOrDigit(format[i])) { if (!inString) { inString = true; jFormat.append('\''); } jFormat.append(format[i]); } else { jFormat.append(format[i]); } } if (inString) { jFormat.append('\''); } return jFormat.toString(); } /** * Implements the integral number formatting part of the * sprintf() method, that is, this method handles d, i, o, u, * x, and X formatting characters. * * @param buf The formatted number is appended to this string buffer * @param num The number object to format * @param fmt The format character defining the radix of the number * @param width The minimum field width for the number * @param precision The minimum number of digits to print for the number, * this does not include any signs or prefix characters * @param flags The flags governing the formatting. This is a combination * of the FLAG_* constants above. * * @return The formatted string * * @see sprintf() */ private static StringBuffer format(StringBuffer buf, Number num, char fmt, int width, int precision, BitSet flags) { String numStr; String prefStr = ""; boolean toUpper = (fmt == 'X'); // Check precision and make default if (precision >= 0) { flags.clear(FLAG_ZE); } else { precision = 1; } // Get the value and adjust size interpretation long val; long sizeMask; if (flags.get(FLAG_SHORT)) { val = num.shortValue(); sizeMask = 0xffffL; } else if (flags.get(FLAG_LONG)) { val = num.longValue(); sizeMask = 0xffffffffffffffffL; } else { val = num.intValue(); sizeMask = 0xffffffffL; } // check formatting type if (fmt == 'x' || fmt == 'X') { numStr = Long.toHexString(val & sizeMask); if (toUpper) { numStr = numStr.toUpperCase(); } if (flags.get(FLAG_AL)) { prefStr = toUpper ? "0X" : "0x"; } } else if (fmt == 'o') { numStr = Long.toOctalString(val & sizeMask); if (flags.get(FLAG_AL) && val != 0 && precision <= numStr.length()) { precision = numStr.length() + 1; } } else { numStr = Long.toString(val); // move sign to prefix if negative, or set '+' if (val < 0) { prefStr = "-"; numStr = numStr.substring(1); } else if (flags.get(FLAG_SI)) { prefStr = "+"; } } // prefix 0 for precision if (precision > numStr.length()) { StringBuffer tmp = new StringBuffer(precision); for (precision -= numStr.length(); precision > 0; precision--) { tmp.append('0'); } numStr = tmp.append(numStr).toString(); } return format(buf, numStr, prefStr, width, flags); } /** * Implements the floating point number formatting part of the * sprintf() method, that is, this method handles f, e, E, g, * and G formatting characters. * * @param buf The formatted number is appended to this string buffer * @param num The numeric value to format * @param fmt The format character defining the floating point format * @param width The minimum field width for the number * @param precision Depending on fmt either the number of * digits after the decimal point or the number of significant * digits. * @param flags The flags governing the formatting. This is a combination * of the FLAG_* constants above. * * @return The formatted string * * @see sprintf() */ private static StringBuffer format(StringBuffer buf, double num, char fmt, int width, int precision, BitSet flags) { BigDecimal val = new BigDecimal(num).abs(); // the exponent character, will be defined if exponent is needed char expChar = 0; // the exponent value int exp; if (fmt != 'f') { exp = val.unscaledValue().toString().length() - val.scale() - 1; } else { exp = 0; } // force display of the decimal dot, if otherwise omitted boolean needDot = (precision == 0 && flags.get(FLAG_AL)); // for fmt==g|G : treat trailing 0 and decimal dot specially boolean checkTrails = false; // get a sensible precision value if (precision < 0) { precision = 6; } switch (fmt) { case 'G': // fall through case 'g': // decrement precision, to simulate significance if (precision > 0) { precision--; } // we have to check trailing zeroes later checkTrails = true; // exponent does not stipulate exp notation, break here if (exp <= precision) { precision -= exp; break; } // fall through for exponent handling case 'E': // fall through case 'e': // place the dot after the first decimal place val = val.movePointLeft(exp); // define the exponent character expChar = (fmt == 'e' || fmt == 'g') ? 'e' : 'E'; break; } // only rescale if the precision is positive, may be negative // for g|G if (precision >= 0) { val = val.setScale(precision, BigDecimal.ROUND_HALF_UP); } // convert the number to a string String numStr = val.toString(); // for g|G : check trailing zeroes if (checkTrails) { if (flags.get(FLAG_AL)) { // need a dot, if not existing for alternative format needDot |= (numStr.indexOf('.') < 0); } else { // remove trailing dots and zeros int dot = numStr.indexOf('.'); if (dot >= 0) { int i = numStr.length()-1; while (i>=dot && numStr.charAt(i)=='0') { i--; } // if stopped at dot, remove it if (i > dot) { i++; } numStr = numStr.substring(0, i); } } } // Get a buffer with the number up to now StringBuffer numBuf = new StringBuffer(numStr); // if we need a decimal dot, add it if (needDot) { numBuf.append('.'); } // we have an exponent to add if (expChar != 0) { numBuf.append(expChar); numBuf.append(exp < 0 ? '-' : '+'); if (exp < 10) { numBuf.append('0'); } numBuf.append(exp); } // define the number's sign as the prefix for later formatting String prefStr; if (num < 0) { prefStr = "-"; } else if (flags.get(FLAG_SI)) { prefStr = "+"; } else { prefStr = ""; } // now format it and up we go return format(buf, numBuf.toString(), prefStr, width, flags); } /** * Formats the String appending to the * StringBuffer at least the String and * justifying according to the flags. *

* The flags will be interpreted as follows :
* * if {@link #FLAG_LJ} is set, append blanks for left justification
* * else if {@link #FLAG_ZE} is set, insert zeroes between prefStr and str * * else prepend blanks for right justification * * @param buf The StringBuffer to append the formatted result * to. * @param str The String to be appended with surrounding * blanks, zeroes and prefStr depending on the * flags. This is usually the real string to print * like "ape" or "4.5E99". * @param prefStr An optional prefix String to be appended * in front of the String. This is usually the prefix * string for numeric str values, for example * "-", "0x", or "+". The reason for this separation is that the * {@link #FLAG_ZE} flag will insert zeroes between the prefix and * the string itself to fill the field to the width. * @param width The minimal field width. If the field width is larger than * the sum of the lengths of str and * prefStr, blanks or zeroes will be prepended or * appended according to the flags value. * @param flags The flags indicating where blanks or zeroes will be filled. * See above for the interpretation of flags. * * @throws NullPointerException if any of buf, * str, prefStr or flags * is null. */ private static StringBuffer format(StringBuffer buf, String str, String prefStr, int width, BitSet flags) { int numFill = width - prefStr.length() - str.length(); int preZero = 0; int preBlank = 0; int postBlank = 0; if (flags.get(FLAG_LJ)) { postBlank = numFill; } else if (flags.get(FLAG_ZE)) { preZero = numFill; } else { preBlank = numFill; } for ( ; preBlank > 0; preBlank--) buf.append(' '); buf.append(prefStr); for ( ; preZero > 0; preZero--) buf.append('0'); buf.append(str); for ( ; postBlank > 0; postBlank--) buf.append(' '); return buf; } /** * Join two paths or handles, fixing the slashes. * All these will go to "/a/b": * /a/ + /b/ (trailing slashes are removed) * /a + /b * /a/ + b * /a/ + b/ * /a// + /////b (i am paranoid) * * Note that leading slashes are untouched, so variations * on the above without the leading slash on 'a' would go * to "a/b". * @deprecated use {@link #makeCanonicalPath(String)} */ public static String joinFixSlash(String first, String second) { StringBuffer result = new StringBuffer((first.startsWith("/"))?"/":""); String[] firstSplit = explode(first, '/'); String[] secondSplit = explode(second, '/'); for (int i=0; i 1) { result.setLength(result.length() - 1); } return result.toString(); } /** * Performs variable replacement on the given string value. * Each ${...} sequence within the given value is replaced * with the value of the named parser variable. If a variable is not found * in the properties an IllegalArgumentException is thrown unless * ignoreMissing is true. In the later case, the * missing variable is replaced by the empty string. * * @param value the original value * @param ignoreMissing if true, missing variables are replaced * by the empty string. * @return value after variable replacements * @throws IllegalArgumentException if the replacement of a referenced * variable is not found */ public static String replaceVariables(Properties variables, String value, boolean ignoreMissing) throws IllegalArgumentException { StringBuffer result = new StringBuffer(); // Value: // +--+-+--------+-+-----------------+ // | |p|--> |q|--> | // +--+-+--------+-+-----------------+ int p = 0, q = value.indexOf("${"); // Find first ${ while (q != -1) { result.append(value.substring(p, q)); // Text before ${ p = q; q = value.indexOf("}", q + 2); // Find } if (q != -1) { String variable = value.substring(p + 2, q); String replacement = variables.getProperty(variable); if (replacement == null) { if (ignoreMissing) { replacement = ""; } else { throw new IllegalArgumentException( "Replacement not found for ${" + variable + "}."); } } result.append(replacement); p = q + 1; q = value.indexOf("${", p); // Find next ${ } } result.append(value.substring(p, value.length())); // Trailing text return result.toString(); } /** * Parses an ISO8601-compliant date/time string. * (see ISO 8601). *

* The currently supported format is: *

     *   ±YYYY-MM-DDThh:mm:ss.SSSTZD
     * 
* where: *
     *   ±YYYY = four-digit year with optional sign where values <= 0 are
     *           denoting years BCE and values > 0 are denoting years CE,
     *           e.g. -0001 denotes the year 2 BCE, 0000 denotes the year 1 BCE,
     *           0001 denotes the year 1 CE, and so on...
     *   MM    = two-digit month (01=January, etc.)
     *   DD    = two-digit day of month (01 through 31)
     *   hh    = two digits of hour (00 through 23) (am/pm NOT allowed)
     *   mm    = two digits of minute (00 through 59)
     *   ss    = two digits of second (00 through 59)
     *   SSS   = three digits of milliseconds (000 through 999)
     *   TZD   = time zone designator, Z for Zulu (i.e. UTC) or an offset from UTC
     *           in the form of +hh:mm or -hh:mm
     * 
* * @param text the date/time string to be parsed * @return a Calendar, or null if the input could * not be parsed * @throws IllegalArgumentException if a null argument is passed */ public static Calendar parseISO8601(String text) { if (text == null) { throw new IllegalArgumentException("argument can not be null"); } // check optional leading sign char sign; int start; if (text.startsWith("-")) { sign = '-'; start = 1; } else if (text.startsWith("+")) { sign = '+'; start = 1; } else { sign = '+'; // no sign specified, implied '+' start = 0; } /** * the expected format of the remainder of the string is: * YYYY-MM-DDThh:mm:ss.SSSTZD * * note that we cannot use java.text.SimpleDateFormat for * parsing because it can't handle years <= 0 and TZD's */ int year, month, day, hour, min, sec, ms; String tzID; try { // year (YYYY) year = Integer.parseInt(text.substring(start, start + 4)); start += 4; // delimiter '-' if (text.charAt(start) != '-') { return null; } start++; // month (MM) month = Integer.parseInt(text.substring(start, start + 2)); start += 2; // delimiter '-' if (text.charAt(start) != '-') { return null; } start++; // day (DD) day = Integer.parseInt(text.substring(start, start + 2)); start += 2; // delimiter 'T' if (text.charAt(start) != 'T') { return null; } start++; // hour (hh) hour = Integer.parseInt(text.substring(start, start + 2)); start += 2; // delimiter ':' if (text.charAt(start) != ':') { return null; } start++; // minute (mm) min = Integer.parseInt(text.substring(start, start + 2)); start += 2; // delimiter ':' if (text.charAt(start) != ':') { return null; } start++; // second (ss) sec = Integer.parseInt(text.substring(start, start + 2)); start += 2; // delimiter '.' if (text.charAt(start) != '.') { return null; } start++; // millisecond (SSS) ms = Integer.parseInt(text.substring(start, start + 3)); start += 3; // time zone designator (Z or +00:00 or -00:00) if (text.charAt(start) == '+' || text.charAt(start) == '-') { // offset to UTC specified in the format +00:00/-00:00 tzID = "GMT" + text.substring(start); } else if (text.substring(start).equals("Z")) { tzID = "GMT"; } else { // invalid time zone designator return null; } } catch (IndexOutOfBoundsException e) { return null; } catch (NumberFormatException e) { return null; } TimeZone tz = TimeZone.getTimeZone(tzID); // verify id of returned time zone (getTimeZone defaults to "GMT") if (!tz.getID().equals(tzID)) { // invalid time zone return null; } // initialize Calendar object Calendar cal = Calendar.getInstance(tz); cal.setLenient(false); // year and era if (sign == '-' || year == 0) { // not CE, need to set era (BCE) and adjust year cal.set(Calendar.YEAR, year + 1); cal.set(Calendar.ERA, GregorianCalendar.BC); } else { cal.set(Calendar.YEAR, year); cal.set(Calendar.ERA, GregorianCalendar.AD); } // month (0-based!) cal.set(Calendar.MONTH, month - 1); // day of month cal.set(Calendar.DAY_OF_MONTH, day); // hour cal.set(Calendar.HOUR_OF_DAY, hour); // minute cal.set(Calendar.MINUTE, min); // second cal.set(Calendar.SECOND, sec); // millisecond cal.set(Calendar.MILLISECOND, ms); try { /** * the following call will trigger an IllegalArgumentException * if any of the set values are illegal or out of range */ cal.getTime(); } catch (IllegalArgumentException e) { return null; } return cal; } /** * Formats a Calendar value into an ISO8601-compliant * date/time string. * * @param cal the time value to be formatted into a date/time string. * @return the formatted date/time string. * @throws IllegalArgumentException if a null argument is passed */ public static String formatISO8601(Calendar cal) { if (cal == null) { throw new IllegalArgumentException("argument can not be null"); } // determine era and adjust year if necessary int year = cal.get(Calendar.YEAR); if (cal.isSet(Calendar.ERA) && cal.get(Calendar.ERA) == GregorianCalendar.BC) { /** * calculate year using astronomical system: * year n BCE => astronomical year -n + 1 */ year = 0 - year + 1; } /** * the format of the date/time string is: * YYYY-MM-DDThh:mm:ss.SSSTZD * * note that we cannot use java.text.SimpleDateFormat for * formatting because it can't handle years <= 0 and TZD's */ StringBuffer buf = new StringBuffer(); // year ([-]YYYY) pad0(buf, 4, year); buf.append('-'); // month (MM) pad0(buf, 2, cal.get(Calendar.MONTH) + 1); buf.append('-'); // day (DD) pad0(buf, 2, cal.get(Calendar.DAY_OF_MONTH)); buf.append('T'); // hour (hh) pad0(buf, 2, cal.get(Calendar.HOUR_OF_DAY)); buf.append(':'); // minute (mm) pad0(buf, 2, cal.get(Calendar.MINUTE)); buf.append(':'); // second (ss) pad0(buf, 2, cal.get(Calendar.SECOND)); buf.append('.'); // millisecond (SSS) pad0(buf, 3, cal.get(Calendar.MILLISECOND)); // time zone designator (Z or +00:00 or -00:00) TimeZone tz = cal.getTimeZone(); // determine offset of timezone from UTC (incl. daylight saving) int offset = tz.getOffset(cal.getTimeInMillis()); if (offset != 0) { int hours = Math.abs((offset / (60 * 1000)) / 60); int minutes = Math.abs((offset / (60 * 1000)) % 60); buf.append(offset < 0 ? '-' : '+'); pad0(buf, 2, hours); buf.append(':'); pad0(buf, 2, minutes); } else { buf.append('Z'); } return buf.toString(); } private static void pad0(StringBuffer buf, int len, int nr) { String val = String.valueOf(nr); while (len-- > val.length()) { buf.append('0'); } buf.append(val); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy