diff options
Diffstat (limited to 'base/util/src/com/netscape/cmsutil/util/Fmt.java')
-rw-r--r-- | base/util/src/com/netscape/cmsutil/util/Fmt.java | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/base/util/src/com/netscape/cmsutil/util/Fmt.java b/base/util/src/com/netscape/cmsutil/util/Fmt.java new file mode 100644 index 000000000..a24b8d090 --- /dev/null +++ b/base/util/src/com/netscape/cmsutil/util/Fmt.java @@ -0,0 +1,605 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cmsutil.util; + +// Fmt - some simple single-arg sprintf-like routines +// +// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// Visit the ACME Labs Java page for up-to-date versions of this and other +// fine Java utilities: http://www.acme.com/java/ + +/// Some simple single-arg sprintf-like routines. +// <P> +// It is apparently impossible to declare a Java method that accepts +// variable numbers of any type of argument. You can declare it to take +// Objects, but numeric variables and constants are not in fact Objects. +// <P> +// However, using the built-in string concatenation, it's almost as +// convenient to make a series of single-argument formatting routines. +// <P> +// Fmt can format the following types: +// <BLOCKQUOTE><CODE> +// byte short int long float double char String Object +// </CODE></BLOCKQUOTE> +// For each type there is a set of overloaded methods, each returning +// a formatted String. There's the plain formatting version: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x ) +// </PRE></BLOCKQUOTE> +// There's a version specifying a minimum field width: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x, minWidth ) +// </PRE></BLOCKQUOTE> +// And there's a version that takes flags: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( x, minWidth, flags ) +// </PRE></BLOCKQUOTE> +// Currently available flags are: +// <BLOCKQUOTE><PRE> +// Fmt.ZF - zero-fill +// Fmt.LJ - left justify +// Fmt.HX - hexadecimal +// Fmt.OC - octal +// </PRE></BLOCKQUOTE> +// The HX and OC flags imply unsigned output. +// <P> +// For doubles and floats, there's a significant-figures parameter before +// the flags: +// <BLOCKQUOTE><PRE> +// Fmt.fmt( d ) +// Fmt.fmt( d, minWidth ) +// Fmt.fmt( d, minWidth, sigFigs ) +// Fmt.fmt( d, minWidth, sigFigs, flags ) +// </PRE></BLOCKQUOTE> +// <P> +// <A HREF="/resources/classes/Acme/Fmt.java">Fetch the software.</A><BR> +// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A> +// <HR> +// Similar classes: +// <UL> +// <LI> Andrew Scherpbier's <A HREF="http://www.sdsu.edu/doc/java-SDSU/sdsu.FormatString.html">FormatString</A> +// Tries to allow variable numbers of arguments by +// supplying overloaded routines with different combinations of parameters, +// but doesn't actually supply that many. The floating point conversion +// is described as "very incomplete". +// <LI> Core Java's <A HREF="http://www.apl.jhu.edu/~hall/java/CoreJava-Format.html">Format</A>. +// The design seems a little weird. They want you to create an instance, +// passing the format string to the constructor, and then call an instance +// method with your data to do the actual formatting. The extra steps are +// pointless; better to just use static methods. +// </UL> + +public class Fmt { + + // Flags. + /// Zero-fill. + public static final int ZF = 1; + /// Left justify. + public static final int LJ = 2; + /// Hexadecimal. + public static final int HX = 4; + /// Octal. + public static final int OC = 8; + // Was a number - internal use. + private static final int WN = 16; + + // byte + public static String fmt(byte b) { + return fmt(b, 0, 0); + } + + public static String fmt(byte b, int minWidth) { + return fmt(b, minWidth, 0); + } + + public static String fmt(byte b, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt(Integer.toString(b & 0xff, 16), minWidth, flags | WN); + else if (octal) + return fmt(Integer.toString(b & 0xff, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(b & 0xff), minWidth, flags | WN); + } + + // short + public static String fmt(short s) { + return fmt(s, 0, 0); + } + + public static String fmt(short s, int minWidth) { + return fmt(s, minWidth, 0); + } + + public static String fmt(short s, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt( + Integer.toString(s & 0xffff, 16), minWidth, flags | WN); + else if (octal) + return fmt( + Integer.toString(s & 0xffff, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(s), minWidth, flags | WN); + } + + // int + public static String fmt(int i) { + return fmt(i, 0, 0); + } + + public static String fmt(int i, int minWidth) { + return fmt(i, minWidth, 0); + } + + public static String fmt(int i, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) + return fmt( + Long.toString(i & 0xffffffffL, 16), minWidth, flags | WN); + else if (octal) + return fmt( + Long.toString(i & 0xffffffffL, 8), minWidth, flags | WN); + else + return fmt(Integer.toString(i), minWidth, flags | WN); + } + + // long + public static String fmt(long l) { + return fmt(l, 0, 0); + } + + public static String fmt(long l, int minWidth) { + return fmt(l, minWidth, 0); + } + + public static String fmt(long l, int minWidth, int flags) { + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + + if (hexadecimal) { + if ((l & 0xf000000000000000L) != 0) + return fmt( + Long.toString(l >>> 60, 16) + + fmt(l & 0x0fffffffffffffffL, 15, HX | ZF), + minWidth, flags | WN); + else + return fmt(Long.toString(l, 16), minWidth, flags | WN); + } else if (octal) { + if ((l & 0x8000000000000000L) != 0) + return fmt( + Long.toString(l >>> 63, 8) + + fmt(l & 0x7fffffffffffffffL, 21, OC | ZF), + minWidth, flags | WN); + else + return fmt(Long.toString(l, 8), minWidth, flags | WN); + } else + return fmt(Long.toString(l), minWidth, flags | WN); + } + + // float + public static String fmt(float f) { + return fmt(f, 0, 0, 0); + } + + public static String fmt(float f, int minWidth) { + return fmt(f, minWidth, 0, 0); + } + + public static String fmt(float f, int minWidth, int sigFigs) { + return fmt(f, minWidth, sigFigs, 0); + } + + public static String fmt(float f, int minWidth, int sigFigs, int flags) { + if (sigFigs != 0) + return fmt( + sigFigFix(Float.toString(f), sigFigs), minWidth, + flags | WN); + else + return fmt(Float.toString(f), minWidth, flags | WN); + } + + // double + public static String fmt(double d) { + return fmt(d, 0, 0, 0); + } + + public static String fmt(double d, int minWidth) { + return fmt(d, minWidth, 0, 0); + } + + public static String fmt(double d, int minWidth, int sigFigs) { + return fmt(d, minWidth, sigFigs, 0); + } + + public static String fmt(double d, int minWidth, int sigFigs, int flags) { + if (sigFigs != 0) + return fmt( + sigFigFix(doubleToString(d), sigFigs), minWidth, + flags | WN); + else + return fmt(doubleToString(d), minWidth, flags | WN); + } + + // char + public static String fmt(char c) { + return fmt(c, 0, 0); + } + + public static String fmt(char c, int minWidth) { + return fmt(c, minWidth, 0); + } + + public static String fmt(char c, int minWidth, int flags) { + // return fmt( Character.toString( c ), minWidth, flags ); + // Character currently lacks a static toString method. Workaround + // is to make a temporary instance and use the instance toString. + return fmt(Character.valueOf(c).toString(), minWidth, flags); + } + + // Object + public static String fmt(Object o) { + return fmt(o, 0, 0); + } + + public static String fmt(Object o, int minWidth) { + return fmt(o, minWidth, 0); + } + + public static String fmt(Object o, int minWidth, int flags) { + return fmt(o.toString(), minWidth, flags); + } + + // String + public static String fmt(String s) { + return fmt(s, 0, 0); + } + + public static String fmt(String s, int minWidth) { + return fmt(s, minWidth, 0); + } + + public static String fmt(String s, int minWidth, int flags) { + int len = s.length(); + boolean zeroFill = ((flags & ZF) != 0); + boolean leftJustify = ((flags & LJ) != 0); + boolean hexadecimal = ((flags & HX) != 0); + boolean octal = ((flags & OC) != 0); + boolean wasNumber = ((flags & WN) != 0); + + if ((hexadecimal || octal || zeroFill) && !wasNumber) + throw new InternalError("Acme.Fmt: number flag on a non-number"); + if (zeroFill && leftJustify) + throw new InternalError("Acme.Fmt: zero-fill left-justify is silly"); + if (hexadecimal && octal) + throw new InternalError("Acme.Fmt: can't do both hex and octal"); + if (len >= minWidth) + return s; + int fillWidth = minWidth - len; + StringBuffer fill = new StringBuffer(fillWidth); + + for (int i = 0; i < fillWidth; ++i) + if (zeroFill) + fill.append('0'); + else + fill.append(' '); + if (leftJustify) + return s + fill; + else if (zeroFill && s.startsWith("-")) + return "-" + fill + s.substring(1); + else + return fill + s; + } + + // Internal routines. + + private static String sigFigFix(String s, int sigFigs) { + // First dissect the floating-point number string into sign, + // integer part, fraction part, and exponent. + String sign; + String unsigned; + + if (s.startsWith("-") || s.startsWith("+")) { + sign = s.substring(0, 1); + unsigned = s.substring(1); + } else { + sign = ""; + unsigned = s; + } + String mantissa; + String exponent; + int eInd = unsigned.indexOf('e'); + + if (eInd == -1) { + mantissa = unsigned; + exponent = ""; + } else { + mantissa = unsigned.substring(0, eInd); + exponent = unsigned.substring(eInd); + } + StringBuffer number, fraction; + int dotInd = mantissa.indexOf('.'); + + if (dotInd == -1) { + number = new StringBuffer(mantissa); + fraction = new StringBuffer(""); + } else { + number = new StringBuffer(mantissa.substring(0, dotInd)); + fraction = new StringBuffer(mantissa.substring(dotInd + 1)); + } + + int numFigs = number.length(); + int fracFigs = fraction.length(); + + if ((numFigs == 0 || number.toString().equals("0")) && + fracFigs > 0) { + // Don't count leading zeros in the fraction. + numFigs = 0; + for (int i = 0; i < fraction.length(); ++i) { + if (fraction.charAt(i) != '0') + break; + --fracFigs; + } + } + int mantFigs = numFigs + fracFigs; + + if (sigFigs > mantFigs) { + // We want more figures; just append zeros to the fraction. + for (int i = mantFigs; i < sigFigs; ++i) + fraction.append('0'); + } else if (sigFigs < mantFigs && sigFigs >= numFigs) { + // Want fewer figures in the fraction; chop. + fraction.setLength( + fraction.length() - (fracFigs - (sigFigs - numFigs))); + // Round? + } else if (sigFigs < numFigs) { + // Want fewer figures in the number; turn them to zeros. + fraction.setLength(0); // should already be zero, but make sure + for (int i = sigFigs; i < numFigs; ++i) + number.setCharAt(i, '0'); + // Round? + } + // Else sigFigs == mantFigs, which is fine. + + if (fraction.length() == 0) + return sign + number + exponent; + else + return sign + number + "." + fraction + exponent; + } + + /// Improved version of Double.toString(), returns more decimal places. + // <P> + // The JDK 1.0.2 version of Double.toString() returns only six decimal + // places on some systems. In JDK 1.1 full precision is returned on + // all platforms. + // @deprecated + // @see java.lang.Double.toString + public static String doubleToString(double d) { + // Handle special numbers first, to avoid complications. + if (Double.isNaN(d)) + return "NaN"; + if (d == Double.NEGATIVE_INFINITY) + return "-Inf"; + if (d == Double.POSITIVE_INFINITY) + return "Inf"; + + // Grab the sign, and then make the number positive for simplicity. + boolean negative = false; + + if (d < 0.0D) { + negative = true; + d = -d; + } + + // Get the native version of the unsigned value, as a template. + String unsStr = Double.toString(d); + + // Dissect out the exponent. + String mantStr, expStr; + int exp; + int eInd = unsStr.indexOf('e'); + + if (eInd == -1) { + mantStr = unsStr; + expStr = ""; + exp = 0; + } else { + mantStr = unsStr.substring(0, eInd); + expStr = unsStr.substring(eInd + 1); + if (expStr.startsWith("+")) + exp = Integer.parseInt(expStr.substring(1)); + else + exp = Integer.parseInt(expStr); + } + + // Dissect out the number part. + String numStr; + int dotInd = mantStr.indexOf('.'); + + if (dotInd == -1) + numStr = mantStr; + else + numStr = mantStr.substring(0, dotInd); + long num; + + if (numStr.length() == 0) + num = 0; + else + num = Integer.parseInt(numStr); + + // Build the new mantissa. + StringBuffer newMantBuf = new StringBuffer(numStr + "."); + double p = Math.pow(10, exp); + double frac = d - num * p; + String digits = "0123456789"; + int nDigits = 16 - numStr.length(); // about 16 digits in a double + + for (int i = 0; i < nDigits; ++i) { + p /= 10.0D; + int dig = (int) (frac / p); + + if (dig < 0) + dig = 0; + if (dig > 9) + dig = 9; + newMantBuf.append(digits.charAt(dig)); + frac -= dig * p; + } + + if ((int) (frac / p + 0.5D) == 1) { + // Round up. + boolean roundMore = true; + + for (int i = newMantBuf.length() - 1; i >= 0; --i) { + int dig = digits.indexOf(newMantBuf.charAt(i)); + + if (dig == -1) + continue; + ++dig; + if (dig == 10) { + newMantBuf.setCharAt(i, '0'); + continue; + } + newMantBuf.setCharAt(i, digits.charAt(dig)); + roundMore = false; + break; + } + if (roundMore) { + // If this happens, we need to prepend a 1. But I haven't + // found a test case yet, so I'm leaving it out for now. + // But if you get this message, please let me know! + newMantBuf.append("ROUNDMORE"); + } + } + + // Chop any trailing zeros. + int len = newMantBuf.length(); + + while (newMantBuf.charAt(len - 1) == '0') + newMantBuf.setLength(--len); + // And chop a trailing dot, if any. + if (newMantBuf.charAt(len - 1) == '.') + newMantBuf.setLength(--len); + + // Done. + return (negative ? "-" : "") + + newMantBuf + + (expStr.length() != 0 ? ("e" + expStr) : ""); + } + + /****************************************************************************** + * /// Test program. + * public static void main( String[] args ) + * { + * System.out.println( "Starting tests." ); + * show( Fmt.fmt( "Hello there." ) ); + * show( Fmt.fmt( 123 ) ); + * show( Fmt.fmt( 123, 10 ) ); + * show( Fmt.fmt( 123, 10, Fmt.ZF ) ); + * show( Fmt.fmt( 123, 10, Fmt.LJ ) ); + * show( Fmt.fmt( -123 ) ); + * show( Fmt.fmt( -123, 10 ) ); + * show( Fmt.fmt( -123, 10, Fmt.ZF ) ); + * show( Fmt.fmt( -123, 10, Fmt.LJ ) ); + * show( Fmt.fmt( (byte) 0xbe, 22, Fmt.OC ) ); + * show( Fmt.fmt( (short) 0xbabe, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0xcafebabe, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0xdeadbeefcafebabeL, 22, Fmt.OC ) ); + * show( Fmt.fmt( 0x8000000000000000L, 22, Fmt.OC ) ); + * show( Fmt.fmt( (byte) 0xbe, 16, Fmt.HX ) ); + * show( Fmt.fmt( (short) 0xbabe, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0xcafebabe, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0xdeadbeefcafebabeL, 16, Fmt.HX ) ); + * show( Fmt.fmt( 0x8000000000000000L, 16, Fmt.HX ) ); + * show( Fmt.fmt( 'c' ) ); + * show( Fmt.fmt( new java.util.Date() ) ); + * show( Fmt.fmt( 123.456F ) ); + * show( Fmt.fmt( 123456000000000000.0F ) ); + * show( Fmt.fmt( 123.456F, 0, 8 ) ); + * show( Fmt.fmt( 123.456F, 0, 7 ) ); + * show( Fmt.fmt( 123.456F, 0, 6 ) ); + * show( Fmt.fmt( 123.456F, 0, 5 ) ); + * show( Fmt.fmt( 123.456F, 0, 4 ) ); + * show( Fmt.fmt( 123.456F, 0, 3 ) ); + * show( Fmt.fmt( 123.456F, 0, 2 ) ); + * show( Fmt.fmt( 123.456F, 0, 1 ) ); + * show( Fmt.fmt( 123456000000000000.0F, 0, 4 ) ); + * show( Fmt.fmt( -123.456F, 0, 4 ) ); + * show( Fmt.fmt( -123456000000000000.0F, 0, 4 ) ); + * show( Fmt.fmt( 123.0F ) ); + * show( Fmt.fmt( 123.0D ) ); + * show( Fmt.fmt( 1.234567890123456789F ) ); + * show( Fmt.fmt( 1.234567890123456789D ) ); + * show( Fmt.fmt( 1234567890123456789F ) ); + * show( Fmt.fmt( 1234567890123456789D ) ); + * show( Fmt.fmt( 0.000000000000000000001234567890123456789F ) ); + * show( Fmt.fmt( 0.000000000000000000001234567890123456789D ) ); + * show( Fmt.fmt( 12300.0F ) ); + * show( Fmt.fmt( 12300.0D ) ); + * show( Fmt.fmt( 123000.0F ) ); + * show( Fmt.fmt( 123000.0D ) ); + * show( Fmt.fmt( 1230000.0F ) ); + * show( Fmt.fmt( 1230000.0D ) ); + * show( Fmt.fmt( 12300000.0F ) ); + * show( Fmt.fmt( 12300000.0D ) ); + * show( Fmt.fmt( Float.NaN ) ); + * show( Fmt.fmt( Float.POSITIVE_INFINITY ) ); + * show( Fmt.fmt( Float.NEGATIVE_INFINITY ) ); + * show( Fmt.fmt( Double.NaN ) ); + * show( Fmt.fmt( Double.POSITIVE_INFINITY ) ); + * show( Fmt.fmt( Double.NEGATIVE_INFINITY ) ); + * show( Fmt.fmt( 1.0F / 8.0F ) ); + * show( Fmt.fmt( 1.0D / 8.0D ) ); + * System.out.println( "Done with tests." ); + * } + * + * private static void show( String str ) + * { + * System.out.println( "#" + str + "#" ); + * } + ******************************************************************************/ + +} |