diff options
Diffstat (limited to 'src/jake2/util')
-rw-r--r-- | src/jake2/util/Lib.java | 525 | ||||
-rw-r--r-- | src/jake2/util/Math3D.java | 564 | ||||
-rw-r--r-- | src/jake2/util/PrintfFormat.java | 3215 | ||||
-rw-r--r-- | src/jake2/util/Vargs.java | 121 |
4 files changed, 4425 insertions, 0 deletions
diff --git a/src/jake2/util/Lib.java b/src/jake2/util/Lib.java new file mode 100644 index 0000000..8ec58e1 --- /dev/null +++ b/src/jake2/util/Lib.java @@ -0,0 +1,525 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 09.12.2003 by RST. +// $Id: Lib.java,v 1.1 2004-07-07 19:59:52 hzi Exp $ + +package jake2.util; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.text.BreakIterator; +import java.util.Arrays; +import java.util.StringTokenizer; +import java.util.Vector; + +import jake2.*; +import jake2.client.*; +import jake2.game.*; +import jake2.qcommon.*; +import jake2.render.*; +import jake2.server.*; + +public class Lib { + + /* + ============= + TempVector + + This is just a convenience function + for making temporary vectors for function calls + ============= + */ + public static float tv_vecs[][] = new float[8][3]; + public static int tv_index; + public static float[] tv(float x, float y, float z) { + + float[] v; + + // use an array so that multiple tempvectors won't collide + // for a while + v = tv_vecs[tv_index]; + tv_index = (tv_index++) & 7; + + v[0] = x; + v[1] = y; + v[2] = z; + + return v; + } + /* + ============= + VectorToString + + This is just a convenience function + for printing vectors + ============= + */ + public static String vtos(float[] v) { + return (int) v[0] + " " + (int) v[1] + " " + (int) v[2]; + } + + public static String vtofs(float[] v) { + return v[0] + " " + v[1] + " " + v[2]; + } + + public static String vtofsbeaty(float[] v) { + return Com.sprintf("%8.2f %8.2f %8.2f", new Vargs().add(v[0]).add(v[1]).add(v[2])); + } + public static short rand() { + return (short) (Math.random() * 0x8000); + } + public static float crandom() { + return (float) (Math.random() - 0.5) * 2.0f; + } + public static float random() { + return (float) Math.random(); + } + public static float crand() { + return (float) (Math.random() - 0.5) * 2.0f; + } + public static float frand() { + return (float) Math.random(); + } + + public static int strcmp(String in1, String in2) { + return in1.compareTo(in2); + } + + public static int stricmp(String in1, String in2) { + return in1.compareToIgnoreCase(in2); + } + + public static boolean strstr(String i1, String i2) { + return (i1.indexOf(i2) != -1); + } + + public static int strncmp(String in1, String in2, int len) { + int i1 = Math.min(len, in1.length()); + int i2 = Math.min(len, in2.length()); + + if (i1 < i2) + return -1; + if (i1 > i2) + return 1; + + for (int n = 0; n < i1; n++) { + char c1 = in1.charAt(n); + char c2 = in2.charAt(n); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } + return 0; + } + public static float atof(String in) { + float res = 0; + + try { + res = Float.parseFloat(in); + } + catch (Exception e) { + } + + return res; + } + + public static int Q_stricmp(String in1, String in2) { + return in1.compareToIgnoreCase(in2); + } + + // ================================================================================= + + public static int atoi(String in) { + try { + return Integer.parseInt(in); + } + catch (Exception e) { + return 0; + } + } + + public static float[] atov(String v) { + float[] res = { 0, 0, 0 }; + + int i1 = v.indexOf(" "); + int i2 = v.indexOf(" ", i1 + 1); + + res[0] = atof(v.substring(0, i1)); + res[1] = atof(v.substring(i1 + 1, i2)); + res[2] = atof(v.substring(i2 + 1, v.length())); + + return res; + } + + public static int strlen(String in) { + return in.length(); + } + + public static int strlen(char in[]) { + for (int i = 0; i < in.length; i++) + if (in[i] == 0) + return i; + return in.length; + } + + public static int strlen(byte in[]) { + for (int i = 0; i < in.length; i++) + if (in[i] == 0) + return i; + return in.length; + } + + public static void strcat(String in, String i) { + in += i; + } + + public static void strcpy(char dest[], char src[]) { + for (int i = 0; i < dest.length && i < src.length; i++) + if (src[i] == 0) { + dest[i] = 0; + return; + } + else + dest[i] = src[i]; + + } + + public static void strcpy(byte dest[], byte src[]) { + for (int i = 0; i < dest.length && i < src.length; i++) + if (src[i] == 0) { + dest[i] = 0; + return; + } + else + dest[i] = src[i]; + + } + + public static String readString(RandomAccessFile file, int len) throws IOException { + byte buf[] = new byte[len]; + + file.read(buf, 0, len); + return new String(buf, 0, strlen(buf)); + } + + public static String readString(ByteBuffer bb, int len) throws IOException { + byte buf[] = new byte[len]; + bb.get(buf); + return new String(buf, 0, strlen(buf)); + } + + public static String hexdumpfile(ByteBuffer bb, int len) throws IOException { + + ByteBuffer bb1 = bb.slice(); + + byte buf[] = new byte[len]; + + bb1.get(buf); + + return hexDump(buf, len, false); + } + + // dump data as hexstring + public static String hexDump(byte data1[], int len, boolean showAddress) { + StringBuffer result = new StringBuffer(); + StringBuffer charfield = new StringBuffer(); + int i = 0; + while (i < len) { + if ((i & 0xf) == 0) { + if (showAddress) { + String address = Integer.toHexString(i); + address = ("0000".substring(0, 4 - address.length()) + address).toUpperCase(); + result.append(address + ": "); + } + } + int v = data1[i]; + + result.append(hex2(v)); + result.append(" "); + + charfield.append(readableChar(v)); + i++; + + // nach dem letzten, newline einfuegen + if ((i & 0xf) == 0) { + result.append(charfield); + result.append("\n"); + charfield.setLength(0); + } + // in der Mitte ein Luecke einfuegen ? + else if ((i & 0xf) == 8) { + result.append(" "); + } + } + return result.toString(); + } + + //formats an hex byte + public static String hex2(int i) { + String val = Integer.toHexString(i & 0xff); + return ("00".substring(0, 2 - val.length()) + val).toUpperCase(); + } + + public static char readableChar(int i) { + if ((i < 0x20) || (i > 0x7f)) + return '.'; + else + return (char) i; + } + + public static void printv(String in, float arr[]) { + for (int n = 0; n < arr.length; n++) { + Com.Println(in + "[" + n + "]: " + arr[n]); + } + } + + public static void memset(byte[] dest, byte c, int len) { + Arrays.fill(dest, 0, len, c); + } + + public static void memset(byte[] dest, int c, int len) { + Arrays.fill(dest, 0, len, (byte) c); + } + + public static void memcpy(byte[] bs, byte[] bs2, int i) { + System.arraycopy(bs2, 0, bs, 0, i); + } + + static byte nullfiller[] = new byte[8192]; + + public static void fwriteString(String s, int len, RandomAccessFile f) throws IOException { + if (s == null) + return; + int diff = len - s.length(); + if (diff > 0) { + f.write(s.getBytes()); + + f.write(nullfiller, 0, diff); + } + else + f.write(s.getBytes(), 0, len); + } + + public static String cut(String in, char c) { + int pos = in.indexOf(c); + + if (pos != -1) + return in.substring(0, pos); + return in; + } + + public static RandomAccessFile fopen(String name, String mode) { + try { + return new RandomAccessFile(name, mode); + } + catch (Exception e) { + Com.DPrintf("Could not open file:" + name); + return null; + } + } + + public static void fclose(RandomAccessFile f) { + try { + f.close(); + } + catch (Exception e) { + } + } + + public static String freadString(RandomAccessFile f, int len) { + byte buffer[] = new byte[len]; + FS.Read(buffer, len, f); + + return new String(buffer).trim(); + } + + public static String rightFrom(String in, char c) { + int pos = in.lastIndexOf(c); + if (pos == -1) + return ""; + else if (pos < in.length()) + return in.substring(pos + 1, in.length()); + return ""; + } + + public static String leftFrom(String in, char c) { + int pos = in.lastIndexOf(c); + if (pos == -1) + return ""; + else if (pos < in.length()) + return in.substring(0, pos); + return ""; + } + + public static String[] linesplit(String in) { + + StringTokenizer tk = new StringTokenizer(in, "\r\n"); + + int count = tk.countTokens(); + if (count == 0) + return new String[] { + }; + + String result[] = new String[count]; + + for (int i = 0; tk.hasMoreTokens(); i++) { + result[i] = tk.nextToken(); + } + + return result; + } + + public static void main(String[] args) { + System.out.println("testing Lib..."); + + String linetest = "line 1\r\n line zwo\nline drei\n\r line4\n.line5"; + + String line[] = linesplit(linetest); + + for (int n = 0; n < line.length; n++) { + System.out.println("[" + line[n] + "]"); + } + + String v = "0.234 1.23423 7.23423"; + + int i1 = v.indexOf(" "); + int i2 = v.indexOf(" ", i1 + 1); + + System.out.println("testing substring..."); + + System.out.println("[" + v.substring(0, i1) + "]"); + System.out.println("[" + v.substring(i1 + 1, i2) + "]"); + System.out.println("[" + v.substring(i2 + 1, v.length()) + "]"); + + System.out.println("rightfrom[" + rightFrom("abcdefg#hijklm", '#') + "]"); + System.out.println("leftfrom[" + leftFrom("abcdefghijk#12", '#') + "]"); + System.out.println("leftfrom[" + leftFrom("abcdefghi", '#') + "]"); + } + + public static int rename(String oldn, String newn) { + try { + File f1 = new File(oldn); + File f2 = new File(newn); + f1.renameTo(f2); + return 0; + } + catch (Exception e) { + return 1; + } + return 0; + } + + public static byte[] getIntBytes(int c) { + byte b[] = new byte[4]; + b[0] = (byte) ((c & 0xff)); + b[1] = (byte) ((c >>> 8) & 0xff); + b[2] = (byte) ((c >>> 16) & 0xff); + b[3] = (byte) ((c >>> 24) & 0xff); + return b; + } + + public static int getInt(byte b[]) { + return (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | ((b[3] & 0xff) << 24); + } + + public static void sleep(int sec) { + try { + Thread.sleep(sec * 1000); + } + catch (InterruptedException e) { + } + } + + public static byte[] clone(byte in[]) { + byte out[] = new byte[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static float[] clone(float in[]) { + float out[] = new float[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static short[] clone(short in[]) { + short out[] = new short[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static long[] clone(long in[]) { + long out[] = new long[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static boolean[] clone(boolean in[]) { + boolean out[] = new boolean[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static int[] clone(int in[]) { + int out[] = new int[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + public static double[] clone(double in[]) { + double out[] = new double[in.length]; + + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } + + // this works with Strings also. + public static String[] clone(String in[]) { + String out[] = new String[in.length]; + if (in.length != 0) + System.arraycopy(in, 0, out, 0, in.length); + + return out; + } +} diff --git a/src/jake2/util/Math3D.java b/src/jake2/util/Math3D.java new file mode 100644 index 0000000..117487c --- /dev/null +++ b/src/jake2/util/Math3D.java @@ -0,0 +1,564 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Created on 09.12.2003 by RST. +// $Id: Math3D.java,v 1.1 2004-07-07 19:59:53 hzi Exp $ + +package jake2.util; + +import jake2.Defines; +import jake2.game.cplane_t; +import jake2.qcommon.Com; + +public class Math3D extends Lib { + + public static void set(float v1[], float v2[]) + { + for (int i=0; i < v1.length; i++) + v1[i]=v2[i]; + } + + + public static void VectorSubtract(float[] a, float[] b, float[] c) { + c[0] = a[0] - b[0]; + c[1] = a[1] - b[1]; + c[2] = a[2] - b[2]; + } + + public static void VectorSubtract(short[] a, short[] b, int[] c) { + c[0] = a[0] - b[0]; + c[1] = a[1] - b[1]; + c[2] = a[2] - b[2]; + } + + public static void VectorAdd(float[] a, float[] b, float[] to) { + to[0] = a[0] + b[0]; + to[1] = a[1] + b[1]; + to[2] = a[2] + b[2]; + } + + public static void VectorCopy(float[] from, float[] to) { + to[0] = from[0]; + to[1] = from[1]; + to[2] = from[2]; + } + + public static void VectorCopy(short[] from, short[] to) { + to[0] = from[0]; + to[1] = from[1]; + to[2] = from[2]; + } + + public static void VectorCopy(short[] from, float[] to) { + to[0] = from[0]; + to[1] = from[1]; + to[2] = from[2]; + } + + public static void VectorCopy(float[] from, short[] to) { + to[0] = (short) from[0]; + to[1] = (short) from[1]; + to[2] = (short) from[2]; + } + + public static void VectorClear(float[] a) { + a[0] = a[1] = a[2] = 0; + } + public static int VectorCompare(float[] v1, float[] v2) { + if (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]) + return 0; + + return 1; + } + public static void VectorNegate(float[] from, float[] to) { + to[0] = -from[0]; + to[1] = -from[1]; + to[2] = -from[2]; + } + public static void VectorSet(float[] v, float x, float y, float z) { + v[0] = (x); + v[1] = (y); + v[2] = (z); + } + public static void VectorMA(float[] veca, float scale, float[] vecb, float[] to) { + to[0] = veca[0] + scale * vecb[0]; + to[1] = veca[1] + scale * vecb[1]; + to[2] = veca[2] + scale * vecb[2]; + } + public static final float VectorNormalize(float[] v) { + float length, ilength; + + length = VectorLength(v); + if (length != 0.0f) { + ilength = 1.0f / length; + v[0] *= ilength; + v[1] *= ilength; + v[2] *= ilength; + } + return length; + } + public static final float VectorNormalize2(float[] v, float[] to) { + float length, ilength; + + length = VectorLength(v); + if (length != 0.0f) { + ilength = 1.0f / length; + to[0] = v[0] * length; + to[1] = v[1] * length; + to[2] = v[2] * length; + } + return length; + } + public static final float VectorLength(float v[]) { + return (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + } + public static void VectorInverse(float[] v) { + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; + } + public static void VectorScale(float[] in, float scale, float[] out) { + out[0] = in[0] * scale; + out[1] = in[1] * scale; + out[2] = in[2] * scale; + } + + public static float vectoyaw(float[] vec) { + float yaw; + + if (/*vec[YAW] == 0 &&*/ + vec[Defines.PITCH] == 0) { + yaw = 0; + if (vec[Defines.YAW] > 0) + yaw = 90; + else if (vec[Defines.YAW] < 0) + yaw = -90; + } + else { + + yaw = (int) (Math.atan2(vec[Defines.YAW], vec[Defines.PITCH]) * 180 / Math.PI); + if (yaw < 0) + yaw += 360; + } + + return yaw; + } + + public static void vectoangles(float[] value1, float[] angles) { + float forward; + float yaw, pitch; + + if (value1[1] == 0 && value1[0] == 0) { + yaw = 0; + if (value1[2] > 0) + pitch = 90; + else + pitch = 270; + } + else { + if (value1[0] != 0) + yaw = (int) (Math.atan2(value1[1], value1[0]) * 180 / Math.PI); + else if (value1[1] > 0) + yaw = 90; + else + yaw = -90; + if (yaw < 0) + yaw += 360; + + forward = (float) Math.sqrt(value1[0] * value1[0] + value1[1] * value1[1]); + pitch = (int) (Math.atan2(value1[2], forward) * 180 / Math.PI); + if (pitch < 0) + pitch += 360; + } + + angles[Defines.PITCH] = -pitch; + angles[Defines.YAW] = yaw; + angles[Defines.ROLL] = 0; + } + + public static void RotatePointAroundVector(float[] dst, float[] dir, float[] point, float degrees) { + float m[][] = new float[3][3]; + float im[][] = new float[3][3]; + float zrot[][] = new float[3][3]; + float tmpmat[][] = new float[3][3]; + float rot[][] = new float[3][3]; + int i; + float[] vr = { 0.0f, 0.0f, 0.0f }; + float[] vup = { 0.0f, 0.0f, 0.0f }; + float[] vf = { 0.0f, 0.0f, 0.0f }; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + Math3D.PerpendicularVector(vr, dir); + Math3D.CrossProduct(vr, vf, vup); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + Math3D.MatCopy(m, im); // achtung: src -> dst + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + Math3D.MatClear(zrot); + + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + zrot[0][0] = (float) Math.cos(Math3D.DEG2RAD(degrees)); + zrot[0][1] = (float) Math.sin(Math3D.DEG2RAD(degrees)); + zrot[1][0] = - (float) Math.sin(Math3D.DEG2RAD(degrees)); + zrot[1][1] = (float) Math.cos(Math3D.DEG2RAD(degrees)); + + Math3D.R_ConcatRotations(m, zrot, tmpmat); + Math3D.R_ConcatRotations(tmpmat, im, rot); + + for (i = 0; i < 3; i++) { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } + } + + + public static void MakeNormalVectors(float[] forward, float[] right, float[] up) { + // this rotate and negat guarantees a vector + // not colinear with the original + right[1] = -forward[0]; + right[2] = forward[1]; + right[0] = forward[2]; + + float d = DotProduct(right, forward); + VectorMA(right, -d, forward, right); + VectorNormalize(right); + CrossProduct(right, forward, up); + } + + + public static float SHORT2ANGLE(int x) { + return ((x) * (360.0f / 65536)); + } + + /* + ================ + R_ConcatTransforms + ================ + */ + public static void R_ConcatTransforms(float in1[][], float in2[][], float out[][]) { + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; + } + + /** + * concatenates 2 matrices each [3][3]. + */ + public static void R_ConcatRotations(float in1[][], float in2[][], float out[][]) { + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; + } + + public static void ProjectPointOnPlane(float[] dst, float[] p, float[] normal) { + float d; + float[] n = { 0.0f, 0.0f, 0.0f }; + float inv_denom; + + inv_denom = 1.0F / Math3D.DotProduct(normal, normal); + + d = Math3D.DotProduct(normal, p) * inv_denom; + + n[0] = normal[0] * inv_denom; + n[1] = normal[1] * inv_denom; + n[2] = normal[2] * inv_denom; + + dst[0] = p[0] - d * n[0]; + dst[1] = p[1] - d * n[1]; + dst[2] = p[2] - d * n[2]; + } + + /** assumes "src" is normalized */ + public static void PerpendicularVector(float[] dst, float[] src) { + int pos; + int i; + float minelem = 1.0F; + float tempvec[] = { 0.0f, 0.0f, 0.0f }; + + // find the smallest magnitude axially aligned vector + for (pos = 0, i = 0; i < 3; i++) { + if (Math.abs(src[i]) < minelem) { + pos = i; + minelem = Math.abs(src[i]); + } + } + tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; + tempvec[pos] = 1.0F; + + // project the point onto the plane defined by src + ProjectPointOnPlane(dst, tempvec, src); + + //normalize the result + Math3D.VectorNormalize(dst); + } + + //===================================================================== + /** + stellt fest, auf welcher Seite sich die Kiste befindet, wenn die Ebene + durch Entfernung und Senkrechten-Normale gegeben ist. + erste Version mit vec3_t... */ + + public static final int BoxOnPlaneSide(float emins[], float emaxs[], cplane_t p) { + + assert (emins.length == 3 && emaxs.length == 3) : "vec3_t bug"; + + float dist1, dist2; + int sides; + + // fast axial cases + if (p.type < 3) { + if (p.dist <= emins[p.type]) + return 1; + if (p.dist >= emaxs[p.type]) + return 2; + return 3; + } + + // general case + switch (p.signbits) { + case 0 : + dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2]; + dist2 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2]; + break; + case 1 : + dist1 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2]; + dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2]; + break; + case 2 : + dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2]; + dist2 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2]; + break; + case 3 : + dist1 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2]; + dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2]; + break; + case 4 : + dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2]; + dist2 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2]; + break; + case 5 : + dist1 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2]; + dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2]; + break; + case 6 : + dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2]; + dist2 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2]; + break; + case 7 : + dist1 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2]; + dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2]; + break; + default : + dist1 = dist2 = 0; + assert (false) : "BoxOnPlaneSide bug"; + break; + } + + sides = 0; + if (dist1 >= p.dist) + sides = 1; + if (dist2 < p.dist) + sides |= 2; + + assert (sides != 0) : "BoxOnPlaneSide(): sides == 0 bug"; + + return sides; + } + + // this is the slow, general version + public static final int BoxOnPlaneSide2(float[] emins, float[] emaxs, cplane_t p) { + int i; + float dist1, dist2; + int sides; + float corners[][] = new float[2][3]; + + for (i = 0; i < 3; i++) { + if (p.normal[i] < 0) { + corners[0][i] = emins[i]; + corners[1][i] = emaxs[i]; + } + else { + corners[1][i] = emins[i]; + corners[0][i] = emaxs[i]; + } + } + dist1 = Math3D.DotProduct(p.normal, corners[0]) - p.dist; + dist2 = Math3D.DotProduct(p.normal, corners[1]) - p.dist; + sides = 0; + if (dist1 >= 0) + sides = 1; + if (dist2 < 0) + sides |= 2; + + return sides; + } + + public static void AngleVectors(float[] angles, float[] forward, float[] right, float[] up) { + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = (float) (angles[Defines.YAW] * (Math.PI * 2 / 360)); + sy = (float) Math.sin(angle); + cy = (float) Math.cos(angle); + angle = (float) (angles[Defines.PITCH] * (Math.PI * 2 / 360)); + sp = (float) Math.sin(angle); + cp = (float) Math.cos(angle); + angle = (float) (angles[Defines.ROLL] * (Math.PI * 2 / 360)); + sr = (float) Math.sin(angle); + cr = (float) Math.cos(angle); + + if (forward != null) { + forward[0] = cp * cy; + forward[1] = cp * sy; + forward[2] = -sp; + } + if (right != null) { + right[0] = (-1 * sr * sp * cy + -1 * cr * -sy); + right[1] = (-1 * sr * sp * sy + -1 * cr * cy); + right[2] = -1 * sr * cp; + } + if (up != null) { + up[0] = (cr * sp * cy + -sr * -sy); + up[1] = (cr * sp * sy + -sr * cy); + up[2] = cr * cp; + } + } + + public static void MatClear(float m[][]) { + m[0][0] = m[0][1] = m[0][2] = m[1][0] = m[1][1] = m[1][2] = m[2][0] = m[2][1] = m[2][2] = 0.0f; + } + + public static final void MatCopy(float src[][], float dst[][]) { + for (int i = 0; i < 3; i++) + { + VectorCopy(src[i], dst[i]); + } + } + + public static void G_ProjectSource(float[] point, float[] distance, float[] forward, float[] right, float[] result) { + result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1]; + result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1]; + result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2]; + } + + + + public static float DotProduct(float[] x, float[] y) { + return x[0] * y[0] + x[1] * y[1] + x[2] * y[2]; + } + + + + public static void CrossProduct(float[] v1, float[] v2, float[] cross) { + cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; + cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; + cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; + } + + + + public static int Q_log2(int val) { + int answer = 0; + while ((val >>= 1) > 0) + answer++; + return answer; + } + + + + public static float DEG2RAD(float in) { + return (in * (float) Math.PI) / 180.0f; + } + + + + public static float anglemod(float a) { + return (float) (360.0f / 65536) * ((int) (a * (65536 / 360.0f)) & 65535); + } + + + + public static int ANGLE2SHORT(float x) { + return ((int) ((x) * 65536f / 360f) & 65535); + } + + + + public static float LerpAngle(float a2, float a1, float frac) { + if (a1 - a2 > 180) + a1 -= 360; + if (a1 - a2 < -180) + a1 += 360; + return a2 + frac * (a1 - a2); + } + + + public static float CalcFov(float fov_x, float width, float height) { + double a = 0.0f; + double x; + + if (fov_x < 1.0f || fov_x > 179.0f) + Com.Error(Defines.ERR_DROP, "Bad fov: " + fov_x); + + x = width / Math.tan(fov_x / 360.0 * Math.PI); + + a = Math.atan(height / x); + + a = a * 360 / Math.PI; + + return (float)a; + } +} diff --git a/src/jake2/util/PrintfFormat.java b/src/jake2/util/PrintfFormat.java new file mode 100644 index 0000000..3414239 --- /dev/null +++ b/src/jake2/util/PrintfFormat.java @@ -0,0 +1,3215 @@ + +// +// (c) 2000 Sun Microsystems, Inc. +// ALL RIGHTS RESERVED +// +// License Grant- +// +// +// Permission to use, copy, modify, and distribute this Software and its +// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is +// hereby granted. +// +// This Software is provided "AS IS". All express warranties, including any +// implied warranty of merchantability, satisfactory quality, fitness for a +// particular purpose, or non-infringement, are disclaimed, except to the extent +// that such disclaimers are held to be legally invalid. +// +// You acknowledge that Software is not designed, licensed or intended for use in +// the design, construction, operation or maintenance of any nuclear facility +// ("High Risk Activities"). Sun disclaims any express or implied warranty of +// fitness for such uses. +// +// Please refer to the file http://www.sun.com/policies/trademarks/ for further +// important trademark information and to +// http://java.sun.com/nav/business/index.html for further important licensing +// information for the Java Technology. +// +package jake2.util; + +import java.util.Enumeration; +import java.util.Vector; +import java.util.Locale; +import java.text.DecimalFormatSymbols; + +/** + * PrintfFormat allows the formatting of an array of + * objects embedded within a string. Primitive types + * must be passed using wrapper types. The formatting + * is controlled by a control string. + *<p> + * A control string is a Java string that contains a + * control specification. The control specification + * starts at the first percent sign (%) in the string, + * provided that this percent sign + *<ol> + *<li>is not escaped protected by a matching % or is + * not an escape % character, + *<li>is not at the end of the format string, and + *<li>precedes a sequence of characters that parses as + * a valid control specification. + *</ol> + *</p><p> + * A control specification usually takes the form: + *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + *</pre> + * There are variants of this basic form that are + * discussed below.</p> + *<p> + * The format is composed of zero or more directives + * defined as follows: + *<ul> + *<li>ordinary characters, which are simply copied to + * the output stream; + *<li>escape sequences, which represent non-graphic + * characters; and + *<li>conversion specifications, each of which + * results in the fetching of zero or more arguments. + *</ul></p> + *<p> + * The results are undefined if there are insufficient + * arguments for the format. Usually an unchecked + * exception will be thrown. If the format is + * exhausted while arguments remain, the excess + * arguments are evaluated but are otherwise ignored. + * In format strings containing the % form of + * conversion specifications, each argument in the + * argument list is used exactly once.</p> + * <p> + * Conversions can be applied to the <code>n</code>th + * argument after the format in the argument list, + * rather than to the next unused argument. In this + * case, the conversion characer % is replaced by the + * sequence %<code>n</code>$, where <code>n</code> is + * a decimal integer giving the position of the + * argument in the argument list.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of conversion specifications, each argument + * in the argument list is used exactly once.</p> + * + *<h4>Escape Sequences</h4> + *<p> + * The following table lists escape sequences and + * associated actions on display devices capable of + * the action. + *<table> + *<tr><th align=left>Sequence</th> + * <th align=left>Name</th> + * <th align=left>Description</th></tr> + *<tr><td>\\</td><td>backlash</td><td>None. + *</td></tr> + *<tr><td>\a</td><td>alert</td><td>Attempts to alert + * the user through audible or visible + * notification. + *</td></tr> + *<tr><td>\b</td><td>backspace</td><td>Moves the + * printing position to one column before + * the current position, unless the + * current position is the start of a line. + *</td></tr> + *<tr><td>\f</td><td>form-feed</td><td>Moves the + * printing position to the initial + * printing position of the next logical + * page. + *</td></tr> + *<tr><td>\n</td><td>newline</td><td>Moves the + * printing position to the start of the + * next line. + *</td></tr> + *<tr><td>\r</td><td>carriage-return</td><td>Moves + * the printing position to the start of + * the current line. + *</td></tr> + *<tr><td>\t</td><td>tab</td><td>Moves the printing + * position to the next implementation- + * defined horizontal tab position. + *</td></tr> + *<tr><td>\v</td><td>vertical-tab</td><td>Moves the + * printing position to the start of the + * next implementation-defined vertical + * tab position. + *</td></tr> + *</table></p> + *<h4>Conversion Specifications</h4> + *<p> + * Each conversion specification is introduced by + * the percent sign character (%). After the character + * %, the following appear in sequence:</p> + *<p> + * Zero or more flags (in any order), which modify the + * meaning of the conversion specification.</p> + *<p> + * An optional minimum field width. If the converted + * value has fewer characters than the field width, it + * will be padded with spaces by default on the left; + * t will be padded on the right, if the left- + * adjustment flag (-), described below, is given to + * the field width. The field width takes the form + * of a decimal integer. If the conversion character + * is s, the field width is the the minimum number of + * characters to be printed.</p> + *<p> + * An optional precision that gives the minumum number + * of digits to appear for the d, i, o, x or X + * conversions (the field is padded with leading + * zeros); the number of digits to appear after the + * radix character for the e, E, and f conversions, + * the maximum number of significant digits for the g + * and G conversions; or the maximum number of + * characters to be written from a string is s and S + * conversions. The precision takes the form of an + * optional decimal digit string, where a null digit + * string is treated as 0. If a precision appears + * with a c conversion character the precision is + * ignored. + * </p> + *<p> + * An optional h specifies that a following d, i, o, + * x, or X conversion character applies to a type + * short argument (the argument will be promoted + * according to the integral promotions and its value + * converted to type short before printing).</p> + *<p> + * An optional l (ell) specifies that a following + * d, i, o, x, or X conversion character applies to a + * type long argument.</p> + *<p> + * A field width or precision may be indicated by an + * asterisk (*) instead of a digit string. In this + * case, an integer argument supplised the field width + * precision. The argument that is actually converted + * is not fetched until the conversion letter is seen, + * so the the arguments specifying field width or + * precision must appear before the argument (if any) + * to be converted. If the precision argument is + * negative, it will be changed to zero. A negative + * field width argument is taken as a - flag, followed + * by a positive field width.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of a conversion specification, a field width + * or precision may be indicated by the sequence + * *<code>m</code>$, where m is a decimal integer + * giving the position in the argument list (after the + * format argument) of an integer argument containing + * the field width or precision.</p> + * <p> + * The format can contain either numbered argument + * specifications (that is, %<code>n</code>$ and + * *<code>m</code>$), or unnumbered argument + * specifications (that is % and *), but normally not + * both. The only exception to this is that %% can + * be mixed with the %<code>n</code>$ form. The + * results of mixing numbered and unnumbered argument + * specifications in a format string are undefined.</p> + * + *<h4>Flag Characters</h4> + *<p> + * The flags and their meanings are:</p> + *<dl> + * <dt>'<dd> integer portion of the result of a + * decimal conversion (%i, %d, %f, %g, or %G) will + * be formatted with thousands' grouping + * characters. For other conversions the flag + * is ignored. The non-monetary grouping + * character is used. + * <dt>-<dd> result of the conversion is left-justified + * within the field. (It will be right-justified + * if this flag is not specified).</td></tr> + * <dt>+<dd> result of a signed conversion always + * begins with a sign (+ or -). (It will begin + * with a sign only when a negative value is + * converted if this flag is not specified.) + * <dt><space><dd> If the first character of a + * signed conversion is not a sign, a space + * character will be placed before the result. + * This means that if the space character and + + * flags both appear, the space flag will be + * ignored. + * <dt>#<dd> value is to be converted to an alternative + * form. For c, d, i, and s conversions, the flag + * has no effect. For o conversion, it increases + * the precision to force the first digit of the + * result to be a zero. For x or X conversion, a + * non-zero result has 0x or 0X prefixed to it, + * respectively. For e, E, f, g, and G + * conversions, the result always contains a radix + * character, even if no digits follow the radix + * character (normally, a decimal point appears in + * the result of these conversions only if a digit + * follows it). For g and G conversions, trailing + * zeros will not be removed from the result as + * they normally are. + * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G + * conversions, leading zeros (following any + * indication of sign or base) are used to pad to + * the field width; no space padding is + * performed. If the 0 and - flags both appear, + * the 0 flag is ignored. For d, i, o, x, and X + * conversions, if a precision is specified, the + * 0 flag will be ignored. For c conversions, + * the flag is ignored. + *</dl> + * + *<h4>Conversion Characters</h4> + *<p> + * Each conversion character results in fetching zero + * or more arguments. The results are undefined if + * there are insufficient arguments for the format. + * Usually, an unchecked exception will be thrown. + * If the format is exhausted while arguments remain, + * the excess arguments are ignored.</p> + * + *<p> + * The conversion characters and their meanings are: + *</p> + *<dl> + * <dt>d,i<dd>The int argument is converted to a + * signed decimal in the style [-]dddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>o<dd> The int argument is converted to unsigned + * octal format in the style ddddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>x<dd> The int argument is converted to unsigned + * hexadecimal format in the style dddd; the + * letters abcdef are used. The precision + * specifies the minimum numberof digits to + * appear; if the value being converted can be + * represented in fewer digits, it will be + * expanded with leading zeros. The default + * precision is 1. The result of converting 0 + * with an explicit precision of 0 is no + * characters. + * <dt>X<dd> Behaves the same as the x conversion + * character except that letters ABCDEF are + * used instead of abcdef. + * <dt>f<dd> The floating point number argument is + * written in decimal notation in the style + * [-]ddd.ddd, where the number of digits after + * the radix character (shown here as a decimal + * point) is equal to the precision + * specification. A Locale is used to determine + * the radix character to use in this format. + * If the precision is omitted from the + * argument, six digits are written after the + * radix character; if the precision is + * explicitly 0 and the # flag is not specified, + * no radix character appears. If a radix + * character appears, at least 1 digit appears + * before it. The value is rounded to the + * appropriate number of digits. + * <dt>e,E<dd>The floating point number argument is + * written in the style [-]d.ddde{+-}dd + * (the symbols {+-} indicate either a plus or + * minus sign), where there is one digit before + * the radix character (shown here as a decimal + * point) and the number of digits after it is + * equal to the precision. A Locale is used to + * determine the radix character to use in this + * format. When the precision is missing, six + * digits are written after the radix character; + * if the precision is 0 and the # flag is not + * specified, no radix character appears. The + * E conversion will produce a number with E + * instead of e introducing the exponent. The + * exponent always contains at least two digits. + * However, if the value to be written requires + * an exponent greater than two digits, + * additional exponent digits are written as + * necessary. The value is rounded to the + * appropriate number of digits. + * <dt>g,G<dd>The floating point number argument is + * written in style f or e (or in sytle E in the + * case of a G conversion character), with the + * precision specifying the number of + * significant digits. If the precision is + * zero, it is taken as one. The style used + * depends on the value converted: style e + * (or E) will be used only if the exponent + * resulting from the conversion is less than + * -4 or greater than or equal to the precision. + * Trailing zeros are removed from the result. + * A radix character appears only if it is + * followed by a digit. + * <dt>c,C<dd>The integer argument is converted to a + * char and the result is written. + * + * <dt>s,S<dd>The argument is taken to be a string and + * bytes from the string are written until the + * end of the string or the number of bytes + * indicated by the precision specification of + * the argument is reached. If the precision + * is omitted from the argument, it is taken to + * be infinite, so all characters up to the end + * of the string are written. + * <dt>%<dd>Write a % character; no argument is + * converted. + *</dl> + *<p> + * If a conversion specification does not match one of + * the above forms, an IllegalArgumentException is + * thrown and the instance of PrintfFormat is not + * created.</p> + *<p> + * If a floating point value is the internal + * representation for infinity, the output is + * [+]Infinity, where Infinity is either Infinity or + * Inf, depending on the desired output string length. + * Printing of the sign follows the rules described + * above.</p> + *<p> + * If a floating point value is the internal + * representation for "not-a-number," the output is + * [+]NaN. Printing of the sign follows the rules + * described above.</p> + *<p> + * In no case does a non-existent or small field width + * cause truncation of a field; if the result of a + * conversion is wider than the field width, the field + * is simply expanded to contain the conversion result. + *</p> + *<p> + * The behavior is like printf. One exception is that + * the minimum number of exponent digits is 3 instead + * of 2 for e and E formats when the optional L is used + * before the e, E, g, or G conversion character. The + * optional L does not imply conversion to a long long + * double. </p> + * <p> + * The biggest divergence from the C printf + * specification is in the use of 16 bit characters. + * This allows the handling of characters beyond the + * small ASCII character set and allows the utility to + * interoperate correctly with the rest of the Java + * runtime environment.</p> + *<p> + * Omissions from the C printf specification are + * numerous. All the known omissions are present + * because Java never uses bytes to represent + * characters and does not have pointers:</p> + *<ul> + * <li>%c is the same as %C. + * <li>%s is the same as %S. + * <li>u, p, and n conversion characters. + * <li>%ws format. + * <li>h modifier applied to an n conversion character. + * <li>l (ell) modifier applied to the c, n, or s + * conversion characters. + * <li>ll (ell ell) modifier to d, i, o, u, x, or X + * conversion characters. + * <li>ll (ell ell) modifier to an n conversion + * character. + * <li>c, C, d,i,o,u,x, and X conversion characters + * apply to Byte, Character, Short, Integer, Long + * types. + * <li>f, e, E, g, and G conversion characters apply + * to Float and Double types. + * <li>s and S conversion characters apply to String + * types. + * <li>All other reference types can be formatted + * using the s or S conversion characters only. + *</ul> + * <p> + * Most of this specification is quoted from the Unix + * man page for the sprintf utility.</p> + * + * @author Allan Jacobs + * @version 1 + * Release 1: Initial release. + * Release 2: Asterisk field widths and precisions + * %n$ and *m$ + * Bug fixes + * g format fix (2 digits in e form corrupt) + * rounding in f format implemented + * round up when digit not printed is 5 + * formatting of -0.0f + * round up/down when last digits are 50000... + */ +public class PrintfFormat { + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(String fmtArg) throws IllegalArgumentException { + this(Locale.getDefault(), fmtArg); + } + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException { + dfs = new DecimalFormatSymbols(locale); + int ePos = 0; + ConversionSpecification sFmt = null; + String unCS = this.nonControl(fmtArg, 0); + if (unCS != null) { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + while (cPos != -1 && cPos < fmtArg.length()) { + for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++) { + char c = 0; + c = fmtArg.charAt(ePos); + if (c == 'i') + break; + if (c == 'd') + break; + if (c == 'f') + break; + if (c == 'g') + break; + if (c == 'G') + break; + if (c == 'o') + break; + if (c == 'x') + break; + if (c == 'X') + break; + if (c == 'e') + break; + if (c == 'E') + break; + if (c == 'c') + break; + if (c == 's') + break; + if (c == '%') + break; + } + ePos = Math.min(ePos + 1, fmtArg.length()); + sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos)); + vFmt.addElement(sFmt); + unCS = this.nonControl(fmtArg, ePos); + if (unCS != null) { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + } + } + /** + * Return a substring starting at + * <code>start</code> and ending at either the end + * of the String <code>s</code>, the next unpaired + * percent sign, or at the end of the String if the + * last character is a percent sign. + * @param s Control string. + * @param start Position in the string + * <code>s</code> to begin looking for the start + * of a control string. + * @return the substring from the start position + * to the beginning of the control string. + */ + private String nonControl(String s, int start) { + String ret = ""; + cPos = s.indexOf("%", start); + if (cPos == -1) + cPos = s.length(); + return s.substring(start, cPos); + } + /** + * Format an array of objects. Byte, Short, + * Integer, Long, Float, Double, and Character + * arguments are treated as wrappers for primitive + * types. + * @param o The array of objects to format. + * @return The formatted String. + */ + public String sprintf(Object[] o) { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + int i = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else { + if (cs.isPositionalSpecification()) { + i = cs.getArgumentPosition() - 1; + if (cs.isPositionalFieldWidth()) { + int ifw = cs.getArgumentPositionForFieldWidth() - 1; + cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue()); + } + if (cs.isPositionalPrecision()) { + int ipr = cs.getArgumentPositionForPrecision() - 1; + cs.setPrecisionWithArg(((Integer) o[ipr]).intValue()); + } + } else { + if (cs.isVariableFieldWidth()) { + cs.setFieldWidthWithArg(((Integer) o[i]).intValue()); + i++; + } + if (cs.isVariablePrecision()) { + cs.setPrecisionWithArg(((Integer) o[i]).intValue()); + i++; + } + } + if (o[i] instanceof Byte) + sb.append(cs.internalsprintf(((Byte) o[i]).byteValue())); + else if (o[i] instanceof Short) + sb.append(cs.internalsprintf(((Short) o[i]).shortValue())); + else if (o[i] instanceof Integer) + sb.append(cs.internalsprintf(((Integer) o[i]).intValue())); + else if (o[i] instanceof Long) + sb.append(cs.internalsprintf(((Long) o[i]).longValue())); + else if (o[i] instanceof Float) + sb.append(cs.internalsprintf(((Float) o[i]).floatValue())); + else if (o[i] instanceof Double) + sb.append(cs.internalsprintf(((Double) o[i]).doubleValue())); + else if (o[i] instanceof Character) + sb.append(cs.internalsprintf(((Character) o[i]).charValue())); + else if (o[i] instanceof String) + sb.append(cs.internalsprintf((String) o[i])); + else + sb.append(cs.internalsprintf(o[i])); + if (!cs.isPositionalSpecification()) + i++; + } + } + return sb.toString(); + } + /** + * Format nothing. Just use the control string. + * @return the formatted String. + */ + public String sprintf() { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + } + return sb.toString(); + } + /** + * Format an int. + * @param x The int to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(int x) throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else + sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an long. + * @param x The long to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(long x) throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else + sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a double. + * @param x The double to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, + * d, d, x, X, or o. + */ + public String sprintf(double x) throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else + sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a String. + * @param x The String to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + public String sprintf(String x) throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else + sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an Object. Convert wrapper types to + * their primitive equivalents and call the + * appropriate internal formatting method. Convert + * Strings using an internal formatting method for + * Strings. Otherwise use the default formatter + * (use toString). + * @param x the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is inappropriate for + * formatting an unwrapped value. + */ + public String sprintf(Object x) throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb = new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) e.nextElement(); + c = cs.getConversionCharacter(); + if (c == '\0') + sb.append(cs.getLiteral()); + else if (c == '%') + sb.append("%"); + else { + if (x instanceof Byte) + sb.append(cs.internalsprintf(((Byte) x).byteValue())); + else if (x instanceof Short) + sb.append(cs.internalsprintf(((Short) x).shortValue())); + else if (x instanceof Integer) + sb.append(cs.internalsprintf(((Integer) x).intValue())); + else if (x instanceof Long) + sb.append(cs.internalsprintf(((Long) x).longValue())); + else if (x instanceof Float) + sb.append(cs.internalsprintf(((Float) x).floatValue())); + else if (x instanceof Double) + sb.append(cs.internalsprintf(((Double) x).doubleValue())); + else if (x instanceof Character) + sb.append(cs.internalsprintf(((Character) x).charValue())); + else if (x instanceof String) + sb.append(cs.internalsprintf((String) x)); + else + sb.append(cs.internalsprintf(x)); + } + } + return sb.toString(); + } + /** + *<p> + * ConversionSpecification allows the formatting of + * a single primitive or object embedded within a + * string. The formatting is controlled by a + * format string. Only one Java primitive or + * object can be formatted at a time. + *<p> + * A format string is a Java string that contains + * a control string. The control string starts at + * the first percent sign (%) in the string, + * provided that this percent sign + *<ol> + *<li>is not escaped protected by a matching % or + * is not an escape % character, + *<li>is not at the end of the format string, and + *<li>precedes a sequence of characters that parses + * as a valid control string. + *</ol> + *<p> + * A control string takes the form: + *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + *</pre> + *<p> + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The + * optional L does not imply conversion to a long + * long double. + */ + private class ConversionSpecification { + /** + * Constructor. Used to prepare an instance + * to hold a literal, not a control string. + */ + ConversionSpecification() { + } + /** + * Constructor for a conversion specification. + * The argument must begin with a % and end + * with the conversion character for the + * conversion specification. + * @param fmtArg String specifying the + * conversion specification. + * @exception IllegalArgumentException if the + * input string is null, zero length, or + * otherwise malformed. + */ + ConversionSpecification(String fmtArg) throws IllegalArgumentException { + if (fmtArg == null) + throw new NullPointerException(); + if (fmtArg.length() == 0) + throw new IllegalArgumentException("Control strings must have positive" + " lengths."); + if (fmtArg.charAt(0) == '%') { + fmt = fmtArg; + pos = 1; + setArgPosition(); + setFlagCharacters(); + setFieldWidth(); + setPrecision(); + setOptionalHL(); + if (setConversionCharacter()) { + if (pos == fmtArg.length()) { + if (leadingZeros && leftJustify) + leadingZeros = false; + if (precisionSet && leadingZeros) { + if (conversionCharacter == 'd' + || conversionCharacter == 'i' + || conversionCharacter == 'o' + || conversionCharacter == 'x') { + leadingZeros = false; + } + } + } else + throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); + } else + throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg); + } else + throw new IllegalArgumentException("Control strings must begin with %."); + } + /** + * Set the String for this instance. + * @param s the String to store. + */ + void setLiteral(String s) { + fmt = s; + } + /** + * Get the String for this instance. Translate + * any escape sequences. + * + * @return s the stored String. + */ + String getLiteral() { + StringBuffer sb = new StringBuffer(); + int i = 0; + while (i < fmt.length()) { + if (fmt.charAt(i) == '\\') { + i++; + if (i < fmt.length()) { + char c = fmt.charAt(i); + switch (c) { + case 'a' : + sb.append((char) 0x07); + break; + case 'b' : + sb.append('\b'); + break; + case 'f' : + sb.append('\f'); + break; + case 'n' : + sb.append(System.getProperty("line.separator")); + break; + case 'r' : + sb.append('\r'); + break; + case 't' : + sb.append('\t'); + break; + case 'v' : + sb.append((char) 0x0b); + break; + case '\\' : + sb.append('\\'); + break; + } + i++; + } else + sb.append('\\'); + } else + i++; + } + return fmt; + } + /** + * Get the conversion character that tells what + * type of control character this instance has. + * + * @return the conversion character. + */ + char getConversionCharacter() { + return conversionCharacter; + } + /** + * Check whether the specifier has a variable + * field width that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * field width; otherwise + * <code>false</code>. + */ + boolean isVariableFieldWidth() { + return variableFieldWidth; + } + /** + * Set the field width with an argument. A + * negative field width is taken as a - flag + * followed by a positive field width. + * @param fw the field width. + */ + void setFieldWidthWithArg(int fw) { + if (fw < 0) + leftJustify = true; + fieldWidthSet = true; + fieldWidth = Math.abs(fw); + } + /** + * Check whether the specifier has a variable + * precision that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * precision; otherwise + * <code>false</code>. + */ + boolean isVariablePrecision() { + return variablePrecision; + } + /** + * Set the precision with an argument. A + * negative precision will be changed to zero. + * @param pr the precision. + */ + void setPrecisionWithArg(int pr) { + precisionSet = true; + precision = Math.max(pr, 0); + } + /** + * Format an int argument using this conversion + * specification. + * @param s the int to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(int s) throws IllegalArgumentException { + String s2 = ""; + switch (conversionCharacter) { + case 'd' : + case 'i' : + if (optionalh) + s2 = printDFormat((short) s); + else if (optionall) + s2 = printDFormat((long) s); + else + s2 = printDFormat(s); + break; + case 'x' : + case 'X' : + if (optionalh) + s2 = printXFormat((short) s); + else if (optionall) + s2 = printXFormat((long) s); + else + s2 = printXFormat(s); + break; + case 'o' : + if (optionalh) + s2 = printOFormat((short) s); + else if (optionall) + s2 = printOFormat((long) s); + else + s2 = printOFormat(s); + break; + case 'c' : + case 'C' : + s2 = printCFormat((char) s); + break; + default : + throw new IllegalArgumentException( + "Cannot format a int with a format using a " + conversionCharacter + " conversion character."); + } + return s2; + } + /** + * Format a long argument using this conversion + * specification. + * @param s the long to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(long s) throws IllegalArgumentException { + String s2 = ""; + switch (conversionCharacter) { + case 'd' : + case 'i' : + if (optionalh) + s2 = printDFormat((short) s); + else if (optionall) + s2 = printDFormat(s); + else + s2 = printDFormat((int) s); + break; + case 'x' : + case 'X' : + if (optionalh) + s2 = printXFormat((short) s); + else if (optionall) + s2 = printXFormat(s); + else + s2 = printXFormat((int) s); + break; + case 'o' : + if (optionalh) + s2 = printOFormat((short) s); + else if (optionall) + s2 = printOFormat(s); + else + s2 = printOFormat((int) s); + break; + case 'c' : + case 'C' : + s2 = printCFormat((char) s); + break; + default : + throw new IllegalArgumentException( + "Cannot format a long with a format using a " + conversionCharacter + " conversion character."); + } + return s2; + } + /** + * Format a double argument using this conversion + * specification. + * @param s the double to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, i, d, + * x, X, or o. + */ + String internalsprintf(double s) throws IllegalArgumentException { + String s2 = ""; + switch (conversionCharacter) { + case 'f' : + s2 = printFFormat(s); + break; + case 'E' : + case 'e' : + s2 = printEFormat(s); + break; + case 'G' : + case 'g' : + s2 = printGFormat(s); + break; + default : + throw new IllegalArgumentException( + "Cannot " + "format a double with a format using a " + conversionCharacter + " conversion character."); + } + return s2; + } + /** + * Format a String argument using this conversion + * specification. + * @param s the String to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(String s) throws IllegalArgumentException { + String s2 = ""; + if (conversionCharacter == 's' || conversionCharacter == 'S') + s2 = printSFormat(s); + else + throw new IllegalArgumentException( + "Cannot " + "format a String with a format using a " + conversionCharacter + " conversion character."); + return s2; + } + /** + * Format an Object argument using this conversion + * specification. + * @param s the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(Object s) { + String s2 = ""; + if (conversionCharacter == 's' || conversionCharacter == 'S') + s2 = printSFormat(s.toString()); + else + throw new IllegalArgumentException( + "Cannot format a String with a format using" + " a " + conversionCharacter + " conversion character."); + return s2; + } + /** + * For f format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both + * a '+' and a ' ' are specified, the blank flag + * is ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the number of digits + * to appear after the radix character. Padding is + * with trailing 0s. + */ + private char[] fFormatDigits(double x) { + // int defaultDigits=6; + String sx, sxOut; + int i, j, k; + int n1In, n2In; + int expon = 0; + boolean minusSign = false; + if (x > 0.0) + sx = Double.toString(x); + else if (x < 0.0) { + sx = Double.toString(-x); + minusSign = true; + } else { + sx = Double.toString(x); + if (sx.charAt(0) == '-') { + minusSign = true; + sx = sx.substring(1); + } + } + int ePos = sx.indexOf('E'); + int rPos = sx.indexOf('.'); + if (rPos != -1) + n1In = rPos; + else if (ePos != -1) + n1In = ePos; + else + n1In = sx.length(); + if (rPos != -1) { + if (ePos != -1) + n2In = ePos - rPos - 1; + else + n2In = sx.length() - rPos - 1; + } else + n2In = 0; + if (ePos != -1) { + int ie = ePos + 1; + expon = 0; + if (sx.charAt(ie) == '-') { + for (++ie; ie < sx.length(); ie++) + if (sx.charAt(ie) != '0') + break; + if (ie < sx.length()) + expon = -Integer.parseInt(sx.substring(ie)); + } else { + if (sx.charAt(ie) == '+') + ++ie; + for (; ie < sx.length(); ie++) + if (sx.charAt(ie) != '0') + break; + if (ie < sx.length()) + expon = Integer.parseInt(sx.substring(ie)); + } + } + int p; + if (precisionSet) + p = precision; + else + p = defaultDigits - 1; + char[] ca1 = sx.toCharArray(); + char[] ca2 = new char[n1In + n2In]; + char[] ca3, ca4, ca5; + for (j = 0; j < n1In; j++) + ca2[j] = ca1[j]; + i = j + 1; + for (k = 0; k < n2In; j++, i++, k++) + ca2[j] = ca1[i]; + if (n1In + expon <= 0) { + ca3 = new char[-expon + n2In]; + for (j = 0, k = 0; k < (-n1In - expon); k++, j++) + ca3[j] = '0'; + for (i = 0; i < (n1In + n2In); i++, j++) + ca3[j] = ca2[i]; + } else + ca3 = ca2; + boolean carry = false; + if (p < -expon + n2In) { + if (expon < 0) + i = p; + else + i = p + n1In; + carry = checkForCarry(ca3, i); + if (carry) + carry = startSymbolicCarry(ca3, i - 1, 0); + } + if (n1In + expon <= 0) { + ca4 = new char[2 + p]; + if (!carry) + ca4[0] = '0'; + else + ca4[0] = '1'; + if (alternateForm || !precisionSet || precision != 0) { + ca4[1] = '.'; + for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++) + ca4[j] = ca3[i]; + for (; j < ca4.length; j++) + ca4[j] = '0'; + } + } else { + if (!carry) { + if (alternateForm || !precisionSet || precision != 0) + ca4 = new char[n1In + expon + p + 1]; + else + ca4 = new char[n1In + expon]; + j = 0; + } else { + if (alternateForm || !precisionSet || precision != 0) + ca4 = new char[n1In + expon + p + 2]; + else + ca4 = new char[n1In + expon + 1]; + ca4[0] = '1'; + j = 1; + } + for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++) + ca4[j] = ca3[i]; + for (; i < n1In + expon; i++, j++) + ca4[j] = '0'; + if (alternateForm || !precisionSet || precision != 0) { + ca4[j] = '.'; + j++; + for (k = 0; i < ca3.length && k < p; i++, j++, k++) + ca4[j] = ca3[i]; + for (; j < ca4.length; j++) + ca4[j] = '0'; + } + } + int nZeros = 0; + if (!leftJustify && leadingZeros) { + int xThousands = 0; + if (thousands) { + int xlead = 0; + if (ca4[0] == '+' || ca4[0] == '-' || ca4[0] == ' ') + xlead = 1; + int xdp = xlead; + for (; xdp < ca4.length; xdp++) + if (ca4[xdp] == '.') + break; + xThousands = (xdp - xlead) / 3; + } + if (fieldWidthSet) + nZeros = fieldWidth - ca4.length; + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + nZeros--; + nZeros -= xThousands; + if (nZeros < 0) + nZeros = 0; + } + j = 0; + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) { + ca5 = new char[ca4.length + nZeros + 1]; + j++; + } else + ca5 = new char[ca4.length + nZeros]; + if (!minusSign) { + if (leadingSign) + ca5[0] = '+'; + if (leadingSpace) + ca5[0] = ' '; + } else + ca5[0] = '-'; + for (i = 0; i < nZeros; i++, j++) + ca5[j] = '0'; + for (i = 0; i < ca4.length; i++, j++) + ca5[j] = ca4[i]; + + int lead = 0; + if (ca5[0] == '+' || ca5[0] == '-' || ca5[0] == ' ') + lead = 1; + int dp = lead; + for (; dp < ca5.length; dp++) + if (ca5[dp] == '.') + break; + int nThousands = (dp - lead) / 3; + // Localize the decimal point. + if (dp < ca5.length) + ca5[dp] = dfs.getDecimalSeparator(); + char[] ca6 = ca5; + if (thousands && nThousands > 0) { + ca6 = new char[ca5.length + nThousands + lead]; + ca6[0] = ca5[0]; + for (i = lead, k = lead; i < dp; i++) { + if (i > 0 && (dp - i) % 3 == 0) { + // ca6[k]=','; + ca6[k] = dfs.getGroupingSeparator(); + ca6[k + 1] = ca5[i]; + k += 2; + } else { + ca6[k] = ca5[i]; + k++; + } + } + for (; i < ca5.length; i++, k++) { + ca6[k] = ca5[i]; + } + } + return ca6; + } + /** + * An intermediate routine on the way to creating + * an f format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @return the converted double value. + */ + private String fFormatString(double x) { + boolean noDigits = false; + char[] ca6, ca7; + if (Double.isInfinite(x)) { + if (x == Double.POSITIVE_INFINITY) { + if (leadingSign) + ca6 = "+Inf".toCharArray(); + else if (leadingSpace) + ca6 = " Inf".toCharArray(); + else + ca6 = "Inf".toCharArray(); + } else + ca6 = "-Inf".toCharArray(); + noDigits = true; + } else if (Double.isNaN(x)) { + if (leadingSign) + ca6 = "+NaN".toCharArray(); + else if (leadingSpace) + ca6 = " NaN".toCharArray(); + else + ca6 = "NaN".toCharArray(); + noDigits = true; + } else + ca6 = fFormatDigits(x); + ca7 = applyFloatPadding(ca6, false); + return new String(ca7); + } + /** + * For e format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The optional + * L does not imply conversion to a long long + * double. + */ + private char[] eFormatDigits(double x, char eChar) { + char[] ca1, ca2, ca3; + // int defaultDigits=6; + String sx, sxOut; + int i, j, k, p; + int n1In, n2In; + int expon = 0; + int ePos, rPos, eSize; + boolean minusSign = false; + if (x > 0.0) + sx = Double.toString(x); + else if (x < 0.0) { + sx = Double.toString(-x); + minusSign = true; + } else { + sx = Double.toString(x); + if (sx.charAt(0) == '-') { + minusSign = true; + sx = sx.substring(1); + } + } + ePos = sx.indexOf('E'); + if (ePos == -1) + ePos = sx.indexOf('e'); + rPos = sx.indexOf('.'); + if (rPos != -1) + n1In = rPos; + else if (ePos != -1) + n1In = ePos; + else + n1In = sx.length(); + if (rPos != -1) { + if (ePos != -1) + n2In = ePos - rPos - 1; + else + n2In = sx.length() - rPos - 1; + } else + n2In = 0; + if (ePos != -1) { + int ie = ePos + 1; + expon = 0; + if (sx.charAt(ie) == '-') { + for (++ie; ie < sx.length(); ie++) + if (sx.charAt(ie) != '0') + break; + if (ie < sx.length()) + expon = -Integer.parseInt(sx.substring(ie)); + } else { + if (sx.charAt(ie) == '+') + ++ie; + for (; ie < sx.length(); ie++) + if (sx.charAt(ie) != '0') + break; + if (ie < sx.length()) + expon = Integer.parseInt(sx.substring(ie)); + } + } + if (rPos != -1) + expon += rPos - 1; + if (precisionSet) + p = precision; + else + p = defaultDigits - 1; + if (rPos != -1 && ePos != -1) + ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray(); + else if (rPos != -1) + ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray(); + else if (ePos != -1) + ca1 = sx.substring(0, ePos).toCharArray(); + else + ca1 = sx.toCharArray(); + boolean carry = false; + int i0 = 0; + if (ca1[0] != '0') + i0 = 0; + else + for (i0 = 0; i0 < ca1.length; i0++) + if (ca1[i0] != '0') + break; + if (i0 + p < ca1.length - 1) { + carry = checkForCarry(ca1, i0 + p + 1); + if (carry) + carry = startSymbolicCarry(ca1, i0 + p, i0); + if (carry) { + ca2 = new char[i0 + p + 1]; + ca2[i0] = '1'; + for (j = 0; j < i0; j++) + ca2[j] = '0'; + for (i = i0, j = i0 + 1; j < p + 1; i++, j++) + ca2[j] = ca1[i]; + expon++; + ca1 = ca2; + } + } + if (Math.abs(expon) < 100 && !optionalL) + eSize = 4; + else + eSize = 5; + if (alternateForm || !precisionSet || precision != 0) + ca2 = new char[2 + p + eSize]; + else + ca2 = new char[1 + eSize]; + if (ca1[0] != '0') { + ca2[0] = ca1[0]; + j = 1; + } else { + for (j = 1; j < (ePos == -1 ? ca1.length : ePos); j++) + if (ca1[j] != '0') + break; + if ((ePos != -1 && j < ePos) || (ePos == -1 && j < ca1.length)) { + ca2[0] = ca1[j]; + expon -= j; + j++; + } else { + ca2[0] = '0'; + j = 2; + } + } + if (alternateForm || !precisionSet || precision != 0) { + ca2[1] = '.'; + i = 2; + } else + i = 1; + for (k = 0; k < p && j < ca1.length; j++, i++, k++) + ca2[i] = ca1[j]; + for (; i < ca2.length - eSize; i++) + ca2[i] = '0'; + ca2[i++] = eChar; + if (expon < 0) + ca2[i++] = '-'; + else + ca2[i++] = '+'; + expon = Math.abs(expon); + if (expon >= 100) { + switch (expon / 100) { + case 1 : + ca2[i] = '1'; + break; + case 2 : + ca2[i] = '2'; + break; + case 3 : + ca2[i] = '3'; + break; + case 4 : + ca2[i] = '4'; + break; + case 5 : + ca2[i] = '5'; + break; + case 6 : + ca2[i] = '6'; + break; + case 7 : + ca2[i] = '7'; + break; + case 8 : + ca2[i] = '8'; + break; + case 9 : + ca2[i] = '9'; + break; + } + i++; + } + switch ((expon % 100) / 10) { + case 0 : + ca2[i] = '0'; + break; + case 1 : + ca2[i] = '1'; + break; + case 2 : + ca2[i] = '2'; + break; + case 3 : + ca2[i] = '3'; + break; + case 4 : + ca2[i] = '4'; + break; + case 5 : + ca2[i] = '5'; + break; + case 6 : + ca2[i] = '6'; + break; + case 7 : + ca2[i] = '7'; + break; + case 8 : + ca2[i] = '8'; + break; + case 9 : + ca2[i] = '9'; + break; + } + i++; + switch (expon % 10) { + case 0 : + ca2[i] = '0'; + break; + case 1 : + ca2[i] = '1'; + break; + case 2 : + ca2[i] = '2'; + break; + case 3 : + ca2[i] = '3'; + break; + case 4 : + ca2[i] = '4'; + break; + case 5 : + ca2[i] = '5'; + break; + case 6 : + ca2[i] = '6'; + break; + case 7 : + ca2[i] = '7'; + break; + case 8 : + ca2[i] = '8'; + break; + case 9 : + ca2[i] = '9'; + break; + } + int nZeros = 0; + if (!leftJustify && leadingZeros) { + int xThousands = 0; + if (thousands) { + int xlead = 0; + if (ca2[0] == '+' || ca2[0] == '-' || ca2[0] == ' ') + xlead = 1; + int xdp = xlead; + for (; xdp < ca2.length; xdp++) + if (ca2[xdp] == '.') + break; + xThousands = (xdp - xlead) / 3; + } + if (fieldWidthSet) + nZeros = fieldWidth - ca2.length; + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) + nZeros--; + nZeros -= xThousands; + if (nZeros < 0) + nZeros = 0; + } + j = 0; + if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) { + ca3 = new char[ca2.length + nZeros + 1]; + j++; + } else + ca3 = new char[ca2.length + nZeros]; + if (!minusSign) { + if (leadingSign) + ca3[0] = '+'; + if (leadingSpace) + ca3[0] = ' '; + } else + ca3[0] = '-'; + for (k = 0; k < nZeros; j++, k++) + ca3[j] = '0'; + for (i = 0; i < ca2.length && j < ca3.length; i++, j++) + ca3[j] = ca2[i]; + + int lead = 0; + if (ca3[0] == '+' || ca3[0] == '-' || ca3[0] == ' ') + lead = 1; + int dp = lead; + for (; dp < ca3.length; dp++) + if (ca3[dp] == '.') + break; + int nThousands = dp / 3; + // Localize the decimal point. + if (dp < ca3.length) + ca3[dp] = dfs.getDecimalSeparator(); + char[] ca4 = ca3; + if (thousands && nThousands > 0) { + ca4 = new char[ca3.length + nThousands + lead]; + ca4[0] = ca3[0]; + for (i = lead, k = lead; i < dp; i++) { + if (i > 0 && (dp - i) % 3 == 0) { + // ca4[k]=','; + ca4[k] = dfs.getGroupingSeparator(); + ca4[k + 1] = ca3[i]; + k += 2; + } else { + ca4[k] = ca3[i]; + k++; + } + } + for (; i < ca3.length; i++, k++) + ca4[k] = ca3[i]; + } + return ca4; + } + /** + * Check to see if the digits that are going to + * be truncated because of the precision should + * force a round in the preceding digits. + * @param ca1 the array of digits + * @param icarry the index of the first digit that + * is to be truncated from the print + * @return <code>true</code> if the truncation forces + * a round that will change the print + */ + private boolean checkForCarry(char[] ca1, int icarry) { + boolean carry = false; + if (icarry < ca1.length) { + if (ca1[icarry] == '6' || ca1[icarry] == '7' || ca1[icarry] == '8' || ca1[icarry] == '9') + carry = true; + else if (ca1[icarry] == '5') { + int ii = icarry + 1; + for (; ii < ca1.length; ii++) + if (ca1[ii] != '0') + break; + carry = ii < ca1.length; + if (!carry && icarry > 0) { + carry = + (ca1[icarry - 1] == '1' + || ca1[icarry - 1] == '3' + || ca1[icarry - 1] == '5' + || ca1[icarry - 1] == '7' + || ca1[icarry - 1] == '9'); + } + } + } + return carry; + } + /** + * Start the symbolic carry process. The process + * is not quite finished because the symbolic + * carry may change the length of the string and + * change the exponent (in e format). + * @param cLast index of the last digit changed + * by the round + * @param cFirst index of the first digit allowed + * to be changed by this phase of the round + * @return <code>true</code> if the carry forces + * a round that will change the print still + * more + */ + private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst) { + boolean carry = true; + for (int i = cLast; carry && i >= cFirst; i--) { + carry = false; + switch (ca[i]) { + case '0' : + ca[i] = '1'; + break; + case '1' : + ca[i] = '2'; + break; + case '2' : + ca[i] = '3'; + break; + case '3' : + ca[i] = '4'; + break; + case '4' : + ca[i] = '5'; + break; + case '5' : + ca[i] = '6'; + break; + case '6' : + ca[i] = '7'; + break; + case '7' : + ca[i] = '8'; + break; + case '8' : + ca[i] = '9'; + break; + case '9' : + ca[i] = '0'; + carry = true; + break; + } + } + return carry; + } + /** + * An intermediate routine on the way to creating + * an e format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @param eChar an 'e' or 'E' to use in the + * converted double value. + * @return the converted double value. + */ + private String eFormatString(double x, char eChar) { + boolean noDigits = false; + char[] ca4, ca5; + if (Double.isInfinite(x)) { + if (x == Double.POSITIVE_INFINITY) { + if (leadingSign) + ca4 = "+Inf".toCharArray(); + else if (leadingSpace) + ca4 = " Inf".toCharArray(); + else + ca4 = "Inf".toCharArray(); + } else + ca4 = "-Inf".toCharArray(); + noDigits = true; + } else if (Double.isNaN(x)) { + if (leadingSign) + ca4 = "+NaN".toCharArray(); + else if (leadingSpace) + ca4 = " NaN".toCharArray(); + else + ca4 = "NaN".toCharArray(); + noDigits = true; + } else + ca4 = eFormatDigits(x, eChar); + ca5 = applyFloatPadding(ca4, false); + return new String(ca5); + } + /** + * Apply zero or blank, left or right padding. + * @param ca4 array of characters before padding is + * finished + * @param noDigits NaN or signed Inf + * @return a padded array of characters + */ + private char[] applyFloatPadding(char[] ca4, boolean noDigits) { + char[] ca5 = ca4; + if (fieldWidthSet) { + int i, j, nBlanks; + if (leftJustify) { + nBlanks = fieldWidth - ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length + nBlanks]; + for (i = 0; i < ca4.length; i++) + ca5[i] = ca4[i]; + for (j = 0; j < nBlanks; j++, i++) + ca5[i] = ' '; + } + } else if (!leadingZeros || noDigits) { + nBlanks = fieldWidth - ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length + nBlanks]; + for (i = 0; i < nBlanks; i++) + ca5[i] = ' '; + for (j = 0; j < ca4.length; i++, j++) + ca5[i] = ca4[j]; + } + } else if (leadingZeros) { + nBlanks = fieldWidth - ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length + nBlanks]; + i = 0; + j = 0; + if (ca4[0] == '-') { + ca5[0] = '-'; + i++; + j++; + } + for (int k = 0; k < nBlanks; i++, k++) + ca5[i] = '0'; + for (; j < ca4.length; i++, j++) + ca5[i] = ca4[j]; + } + } + } + return ca5; + } + /** + * Format method for the f conversion character. + * @param x the double to format. + * @return the formatted String. + */ + private String printFFormat(double x) { + return fFormatString(x); + } + /** + * Format method for the e or E conversion + * character. + * @param x the double to format. + * @return the formatted String. + */ + private String printEFormat(double x) { + if (conversionCharacter == 'e') + return eFormatString(x, 'e'); + else + return eFormatString(x, 'E'); + } + /** + * Format method for the g conversion character. + * + * For g format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * @param x the double to format. + * @return the formatted String. + */ + private String printGFormat(double x) { + String sx, sy, sz, ret; + int savePrecision = precision; + int i; + char[] ca4, ca5; + boolean noDigits = false; + if (Double.isInfinite(x)) { + if (x == Double.POSITIVE_INFINITY) { + if (leadingSign) + ca4 = "+Inf".toCharArray(); + else if (leadingSpace) + ca4 = " Inf".toCharArray(); + else + ca4 = "Inf".toCharArray(); + } else + ca4 = "-Inf".toCharArray(); + noDigits = true; + } else if (Double.isNaN(x)) { + if (leadingSign) + ca4 = "+NaN".toCharArray(); + else if (leadingSpace) + ca4 = " NaN".toCharArray(); + else + ca4 = "NaN".toCharArray(); + noDigits = true; + } else { + if (!precisionSet) + precision = defaultDigits; + if (precision == 0) + precision = 1; + int ePos = -1; + if (conversionCharacter == 'g') { + sx = eFormatString(x, 'e').trim(); + ePos = sx.indexOf('e'); + } else { + sx = eFormatString(x, 'E').trim(); + ePos = sx.indexOf('E'); + } + i = ePos + 1; + int expon = 0; + if (sx.charAt(i) == '-') { + for (++i; i < sx.length(); i++) + if (sx.charAt(i) != '0') + break; + if (i < sx.length()) + expon = -Integer.parseInt(sx.substring(i)); + } else { + if (sx.charAt(i) == '+') + ++i; + for (; i < sx.length(); i++) + if (sx.charAt(i) != '0') + break; + if (i < sx.length()) + expon = Integer.parseInt(sx.substring(i)); + } + // Trim trailing zeros. + // If the radix character is not followed by + // a digit, trim it, too. + if (!alternateForm) { + if (expon >= -4 && expon < precision) + sy = fFormatString(x).trim(); + else + sy = sx.substring(0, ePos); + i = sy.length() - 1; + for (; i >= 0; i--) + if (sy.charAt(i) != '0') + break; + if (i >= 0 && sy.charAt(i) == '.') + i--; + if (i == -1) + sz = "0"; + else if (!Character.isDigit(sy.charAt(i))) + sz = sy.substring(0, i + 1) + "0"; + else + sz = sy.substring(0, i + 1); + if (expon >= -4 && expon < precision) + ret = sz; + else + ret = sz + sx.substring(ePos); + } else { + if (expon >= -4 && expon < precision) + ret = fFormatString(x).trim(); + else + ret = sx; + } + // leading space was trimmed off during + // construction + if (leadingSpace) + if (x >= 0) + ret = " " + ret; + ca4 = ret.toCharArray(); + } + // Pad with blanks or zeros. + ca5 = applyFloatPadding(ca4, false); + precision = savePrecision; + return new String(ca5); + } + /** + * Format method for the d conversion specifer and + * short argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printDFormat(short x) { + return printDFormat(Short.toString(x)); + } + /** + * Format method for the d conversion character and + * long argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printDFormat(long x) { + return printDFormat(Long.toString(x)); + } + /** + * Format method for the d conversion character and + * int argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printDFormat(int x) { + return printDFormat(Integer.toString(x)); + } + /** + * Utility method for formatting using the d + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printDFormat(String sx) { + int nLeadingZeros = 0; + int nBlanks = 0, n = 0; + int i = 0, jFirst = 0; + boolean neg = sx.charAt(0) == '-'; + if (sx.equals("0") && precisionSet && precision == 0) + sx = ""; + if (!neg) { + if (precisionSet && sx.length() < precision) + nLeadingZeros = precision - sx.length(); + } else { + if (precisionSet && (sx.length() - 1) < precision) + nLeadingZeros = precision - sx.length() + 1; + } + if (nLeadingZeros < 0) + nLeadingZeros = 0; + if (fieldWidthSet) { + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + if (!neg && (leadingSign || leadingSpace)) + nBlanks--; + } + if (nBlanks < 0) + nBlanks = 0; + if (leadingSign) + n++; + else if (leadingSpace) + n++; + n += nBlanks; + n += nLeadingZeros; + n += sx.length(); + char[] ca = new char[n]; + if (leftJustify) { + if (neg) + ca[i++] = '-'; + else if (leadingSign) + ca[i++] = '+'; + else if (leadingSpace) + ca[i++] = ' '; + char[] csx = sx.toCharArray(); + jFirst = neg ? 1 : 0; + for (int j = 0; j < nLeadingZeros; i++, j++) + ca[i] = '0'; + for (int j = jFirst; j < csx.length; j++, i++) + ca[i] = csx[j]; + for (int j = 0; j < nBlanks; i++, j++) + ca[i] = ' '; + } else { + if (!leadingZeros) { + for (i = 0; i < nBlanks; i++) + ca[i] = ' '; + if (neg) + ca[i++] = '-'; + else if (leadingSign) + ca[i++] = '+'; + else if (leadingSpace) + ca[i++] = ' '; + } else { + if (neg) + ca[i++] = '-'; + else if (leadingSign) + ca[i++] = '+'; + else if (leadingSpace) + ca[i++] = ' '; + for (int j = 0; j < nBlanks; j++, i++) + ca[i] = '0'; + } + for (int j = 0; j < nLeadingZeros; j++, i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + jFirst = neg ? 1 : 0; + for (int j = jFirst; j < csx.length; j++, i++) + ca[i] = csx[j]; + } + return new String(ca); + } + /** + * Format method for the x conversion character and + * short argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printXFormat(short x) { + String sx = null; + if (x == Short.MIN_VALUE) + sx = "8000"; + else if (x < 0) { + String t; + if (x == Short.MIN_VALUE) + t = "0"; + else { + t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16); + if (t.charAt(0) == 'F' || t.charAt(0) == 'f') + t = t.substring(16, 32); + } + switch (t.length()) { + case 1 : + sx = "800" + t; + break; + case 2 : + sx = "80" + t; + break; + case 3 : + sx = "8" + t; + break; + case 4 : + switch (t.charAt(0)) { + case '1' : + sx = "9" + t.substring(1, 4); + break; + case '2' : + sx = "a" + t.substring(1, 4); + break; + case '3' : + sx = "b" + t.substring(1, 4); + break; + case '4' : + sx = "c" + t.substring(1, 4); + break; + case '5' : + sx = "d" + t.substring(1, 4); + break; + case '6' : + sx = "e" + t.substring(1, 4); + break; + case '7' : + sx = "f" + t.substring(1, 4); + break; + } + break; + } + } else + sx = Integer.toString((int) x, 16); + return printXFormat(sx); + } + /** + * Format method for the x conversion character and + * long argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printXFormat(long x) { + String sx = null; + if (x == Long.MIN_VALUE) + sx = "8000000000000000"; + else if (x < 0) { + String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16); + switch (t.length()) { + case 1 : + sx = "800000000000000" + t; + break; + case 2 : + sx = "80000000000000" + t; + break; + case 3 : + sx = "8000000000000" + t; + break; + case 4 : + sx = "800000000000" + t; + break; + case 5 : + sx = "80000000000" + t; + break; + case 6 : + sx = "8000000000" + t; + break; + case 7 : + sx = "800000000" + t; + break; + case 8 : + sx = "80000000" + t; + break; + case 9 : + sx = "8000000" + t; + break; + case 10 : + sx = "800000" + t; + break; + case 11 : + sx = "80000" + t; + break; + case 12 : + sx = "8000" + t; + break; + case 13 : + sx = "800" + t; + break; + case 14 : + sx = "80" + t; + break; + case 15 : + sx = "8" + t; + break; + case 16 : + switch (t.charAt(0)) { + case '1' : + sx = "9" + t.substring(1, 16); + break; + case '2' : + sx = "a" + t.substring(1, 16); + break; + case '3' : + sx = "b" + t.substring(1, 16); + break; + case '4' : + sx = "c" + t.substring(1, 16); + break; + case '5' : + sx = "d" + t.substring(1, 16); + break; + case '6' : + sx = "e" + t.substring(1, 16); + break; + case '7' : + sx = "f" + t.substring(1, 16); + break; + } + break; + } + } else + sx = Long.toString(x, 16); + return printXFormat(sx); + } + /** + * Format method for the x conversion character and + * int argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printXFormat(int x) { + String sx = null; + if (x == Integer.MIN_VALUE) + sx = "80000000"; + else if (x < 0) { + String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16); + switch (t.length()) { + case 1 : + sx = "8000000" + t; + break; + case 2 : + sx = "800000" + t; + break; + case 3 : + sx = "80000" + t; + break; + case 4 : + sx = "8000" + t; + break; + case 5 : + sx = "800" + t; + break; + case 6 : + sx = "80" + t; + break; + case 7 : + sx = "8" + t; + break; + case 8 : + switch (t.charAt(0)) { + case '1' : + sx = "9" + t.substring(1, 8); + break; + case '2' : + sx = "a" + t.substring(1, 8); + break; + case '3' : + sx = "b" + t.substring(1, 8); + break; + case '4' : + sx = "c" + t.substring(1, 8); + break; + case '5' : + sx = "d" + t.substring(1, 8); + break; + case '6' : + sx = "e" + t.substring(1, 8); + break; + case '7' : + sx = "f" + t.substring(1, 8); + break; + } + break; + } + } else + sx = Integer.toString(x, 16); + return printXFormat(sx); + } + /** + * Utility method for formatting using the x + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printXFormat(String sx) { + int nLeadingZeros = 0; + int nBlanks = 0; + if (sx.equals("0") && precisionSet && precision == 0) + sx = ""; + if (precisionSet) + nLeadingZeros = precision - sx.length(); + if (nLeadingZeros < 0) + nLeadingZeros = 0; + if (fieldWidthSet) { + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + if (alternateForm) + nBlanks = nBlanks - 2; + } + if (nBlanks < 0) + nBlanks = 0; + int n = 0; + if (alternateForm) + n += 2; + n += nLeadingZeros; + n += sx.length(); + n += nBlanks; + char[] ca = new char[n]; + int i = 0; + if (leftJustify) { + if (alternateForm) { + ca[i++] = '0'; + ca[i++] = 'x'; + } + for (int j = 0; j < nLeadingZeros; j++, i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + for (int j = 0; j < csx.length; j++, i++) + ca[i] = csx[j]; + for (int j = 0; j < nBlanks; j++, i++) + ca[i] = ' '; + } else { + if (!leadingZeros) + for (int j = 0; j < nBlanks; j++, i++) + ca[i] = ' '; + if (alternateForm) { + ca[i++] = '0'; + ca[i++] = 'x'; + } + if (leadingZeros) + for (int j = 0; j < nBlanks; j++, i++) + ca[i] = '0'; + for (int j = 0; j < nLeadingZeros; j++, i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + for (int j = 0; j < csx.length; j++, i++) + ca[i] = csx[j]; + } + String caReturn = new String(ca); + if (conversionCharacter == 'X') + caReturn = caReturn.toUpperCase(); + return caReturn; + } + /** + * Format method for the o conversion character and + * short argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printOFormat(short x) { + String sx = null; + if (x == Short.MIN_VALUE) + sx = "100000"; + else if (x < 0) { + String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8); + switch (t.length()) { + case 1 : + sx = "10000" + t; + break; + case 2 : + sx = "1000" + t; + break; + case 3 : + sx = "100" + t; + break; + case 4 : + sx = "10" + t; + break; + case 5 : + sx = "1" + t; + break; + } + } else + sx = Integer.toString((int) x, 8); + return printOFormat(sx); + } + /** + * Format method for the o conversion character and + * long argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printOFormat(long x) { + String sx = null; + if (x == Long.MIN_VALUE) + sx = "1000000000000000000000"; + else if (x < 0) { + String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8); + switch (t.length()) { + case 1 : + sx = "100000000000000000000" + t; + break; + case 2 : + sx = "10000000000000000000" + t; + break; + case 3 : + sx = "1000000000000000000" + t; + break; + case 4 : + sx = "100000000000000000" + t; + break; + case 5 : + sx = "10000000000000000" + t; + break; + case 6 : + sx = "1000000000000000" + t; + break; + case 7 : + sx = "100000000000000" + t; + break; + case 8 : + sx = "10000000000000" + t; + break; + case 9 : + sx = "1000000000000" + t; + break; + case 10 : + sx = "100000000000" + t; + break; + case 11 : + sx = "10000000000" + t; + break; + case 12 : + sx = "1000000000" + t; + break; + case 13 : + sx = "100000000" + t; + break; + case 14 : + sx = "10000000" + t; + break; + case 15 : + sx = "1000000" + t; + break; + case 16 : + sx = "100000" + t; + break; + case 17 : + sx = "10000" + t; + break; + case 18 : + sx = "1000" + t; + break; + case 19 : + sx = "100" + t; + break; + case 20 : + sx = "10" + t; + break; + case 21 : + sx = "1" + t; + break; + } + } else + sx = Long.toString(x, 8); + return printOFormat(sx); + } + /** + * Format method for the o conversion character and + * int argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printOFormat(int x) { + String sx = null; + if (x == Integer.MIN_VALUE) + sx = "20000000000"; + else if (x < 0) { + String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8); + switch (t.length()) { + case 1 : + sx = "2000000000" + t; + break; + case 2 : + sx = "200000000" + t; + break; + case 3 : + sx = "20000000" + t; + break; + case 4 : + sx = "2000000" + t; + break; + case 5 : + sx = "200000" + t; + break; + case 6 : + sx = "20000" + t; + break; + case 7 : + sx = "2000" + t; + break; + case 8 : + sx = "200" + t; + break; + case 9 : + sx = "20" + t; + break; + case 10 : + sx = "2" + t; + break; + case 11 : + sx = "3" + t.substring(1); + break; + } + } else + sx = Integer.toString(x, 8); + return printOFormat(sx); + } + /** + * Utility method for formatting using the o + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printOFormat(String sx) { + int nLeadingZeros = 0; + int nBlanks = 0; + if (sx.equals("0") && precisionSet && precision == 0) + sx = ""; + if (precisionSet) + nLeadingZeros = precision - sx.length(); + if (alternateForm) + nLeadingZeros++; + if (nLeadingZeros < 0) + nLeadingZeros = 0; + if (fieldWidthSet) + nBlanks = fieldWidth - nLeadingZeros - sx.length(); + if (nBlanks < 0) + nBlanks = 0; + int n = nLeadingZeros + sx.length() + nBlanks; + char[] ca = new char[n]; + int i; + if (leftJustify) { + for (i = 0; i < nLeadingZeros; i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + for (int j = 0; j < csx.length; j++, i++) + ca[i] = csx[j]; + for (int j = 0; j < nBlanks; j++, i++) + ca[i] = ' '; + } else { + if (leadingZeros) + for (i = 0; i < nBlanks; i++) + ca[i] = '0'; + else + for (i = 0; i < nBlanks; i++) + ca[i] = ' '; + for (int j = 0; j < nLeadingZeros; j++, i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + for (int j = 0; j < csx.length; j++, i++) + ca[i] = csx[j]; + } + return new String(ca); + } + /** + * Format method for the c conversion character and + * char argument. + * + * The only flag character that affects c format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. Padding is with + * blanks by default. The default width is 1. + * + * The precision, if set, is ignored. + * @param x the char to format. + * @return the formatted String. + */ + private String printCFormat(char x) { + int nPrint = 1; + int width = fieldWidth; + if (!fieldWidthSet) + width = nPrint; + char[] ca = new char[width]; + int i = 0; + if (leftJustify) { + ca[0] = x; + for (i = 1; i <= width - nPrint; i++) + ca[i] = ' '; + } else { + for (i = 0; i < width - nPrint; i++) + ca[i] = ' '; + ca[i] = x; + } + return new String(ca); + } + /** + * Format method for the s conversion character and + * String argument. + * + * The only flag character that affects s format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is the + * smaller of the number of characters in the the + * input and the precision. Padding is with blanks + * by default. + * + * The precision, if set, specifies the maximum + * number of characters to be printed from the + * string. A null digit string is treated + * as a 0. The default is not to set a maximum + * number of characters to be printed. + * @param x the String to format. + * @return the formatted String. + */ + private String printSFormat(String x) { + int nPrint = x.length(); + int width = fieldWidth; + if (precisionSet && nPrint > precision) + nPrint = precision; + if (!fieldWidthSet) + width = nPrint; + int n = 0; + if (width > nPrint) + n += width - nPrint; + if (nPrint >= x.length()) + n += x.length(); + else + n += nPrint; + char[] ca = new char[n]; + int i = 0; + if (leftJustify) { + if (nPrint >= x.length()) { + char[] csx = x.toCharArray(); + for (i = 0; i < x.length(); i++) + ca[i] = csx[i]; + } else { + char[] csx = x.substring(0, nPrint).toCharArray(); + for (i = 0; i < nPrint; i++) + ca[i] = csx[i]; + } + for (int j = 0; j < width - nPrint; j++, i++) + ca[i] = ' '; + } else { + for (i = 0; i < width - nPrint; i++) + ca[i] = ' '; + if (nPrint >= x.length()) { + char[] csx = x.toCharArray(); + for (int j = 0; j < x.length(); i++, j++) + ca[i] = csx[j]; + } else { + char[] csx = x.substring(0, nPrint).toCharArray(); + for (int j = 0; j < nPrint; i++, j++) + ca[i] = csx[j]; + } + } + return new String(ca); + } + /** + * Check for a conversion character. If it is + * there, store it. + * @param x the String to format. + * @return <code>true</code> if the conversion + * character is there, and + * <code>false</code> otherwise. + */ + private boolean setConversionCharacter() { + /* idfgGoxXeEcs */ + boolean ret = false; + conversionCharacter = '\0'; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c == 'i' + || c == 'd' + || c == 'f' + || c == 'g' + || c == 'G' + || c == 'o' + || c == 'x' + || c == 'X' + || c == 'e' + || c == 'E' + || c == 'c' + || c == 's' + || c == '%') { + conversionCharacter = c; + pos++; + ret = true; + } + } + return ret; + } + /** + * Check for an h, l, or L in a format. An L is + * used to control the minimum number of digits + * in an exponent when using floating point + * formats. An l or h is used to control + * conversion of the input to a long or short, + * respectively, before formatting. If any of + * these is present, store them. + */ + private void setOptionalHL() { + optionalh = false; + optionall = false; + optionalL = false; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c == 'h') { + optionalh = true; + pos++; + } else if (c == 'l') { + optionall = true; + pos++; + } else if (c == 'L') { + optionalL = true; + pos++; + } + } + } + /** + * Set the precision. + */ + private void setPrecision() { + int firstPos = pos; + precisionSet = false; + if (pos < fmt.length() && fmt.charAt(pos) == '.') { + pos++; + if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) { + pos++; + if (!setPrecisionArgPosition()) { + variablePrecision = true; + precisionSet = true; + } + return; + } else { + while (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (Character.isDigit(c)) + pos++; + else + break; + } + if (pos > firstPos + 1) { + String sz = fmt.substring(firstPos + 1, pos); + precision = Integer.parseInt(sz); + precisionSet = true; + } + } + } + } + /** + * Set the field width. + */ + private void setFieldWidth() { + int firstPos = pos; + fieldWidth = 0; + fieldWidthSet = false; + if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) { + pos++; + if (!setFieldWidthArgPosition()) { + variableFieldWidth = true; + fieldWidthSet = true; + } + } else { + while (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (Character.isDigit(c)) + pos++; + else + break; + } + if (firstPos < pos && firstPos < fmt.length()) { + String sz = fmt.substring(firstPos, pos); + fieldWidth = Integer.parseInt(sz); + fieldWidthSet = true; + } + } + } + /** + * Store the digits <code>n</code> in %n$ forms. + */ + private void setArgPosition() { + int xPos; + for (xPos = pos; xPos < fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos > pos && xPos < fmt.length()) { + if (fmt.charAt(xPos) == '$') { + positionalSpecification = true; + argumentPosition = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + } + } + } + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setFieldWidthArgPosition() { + boolean ret = false; + int xPos; + for (xPos = pos; xPos < fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos > pos && xPos < fmt.length()) { + if (fmt.charAt(xPos) == '$') { + positionalFieldWidth = true; + argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + ret = true; + } + } + return ret; + } + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setPrecisionArgPosition() { + boolean ret = false; + int xPos; + for (xPos = pos; xPos < fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos > pos && xPos < fmt.length()) { + if (fmt.charAt(xPos) == '$') { + positionalPrecision = true; + argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos)); + pos = xPos + 1; + ret = true; + } + } + return ret; + } + boolean isPositionalSpecification() { + return positionalSpecification; + } + int getArgumentPosition() { + return argumentPosition; + } + boolean isPositionalFieldWidth() { + return positionalFieldWidth; + } + int getArgumentPositionForFieldWidth() { + return argumentPositionForFieldWidth; + } + boolean isPositionalPrecision() { + return positionalPrecision; + } + int getArgumentPositionForPrecision() { + return argumentPositionForPrecision; + } + /** + * Set flag characters, one of '-+#0 or a space. + */ + private void setFlagCharacters() { + /* '-+ #0 */ + thousands = false; + leftJustify = false; + leadingSign = false; + leadingSpace = false; + alternateForm = false; + leadingZeros = false; + for (; pos < fmt.length(); pos++) { + char c = fmt.charAt(pos); + if (c == '\'') + thousands = true; + else if (c == '-') { + leftJustify = true; + leadingZeros = false; + } else if (c == '+') { + leadingSign = true; + leadingSpace = false; + } else if (c == ' ') { + if (!leadingSign) + leadingSpace = true; + } else if (c == '#') + alternateForm = true; + else if (c == '0') { + if (!leftJustify) + leadingZeros = true; + } else + break; + } + } + /** + * The integer portion of the result of a decimal + * conversion (i, d, u, f, g, or G) will be + * formatted with thousands' grouping characters. + * For other conversions the flag is ignored. + */ + private boolean thousands = false; + /** + * The result of the conversion will be + * left-justified within the field. + */ + private boolean leftJustify = false; + /** + * The result of a signed conversion will always + * begin with a sign (+ or -). + */ + private boolean leadingSign = false; + /** + * Flag indicating that left padding with spaces is + * specified. + */ + private boolean leadingSpace = false; + /** + * For an o conversion, increase the precision to + * force the first digit of the result to be a + * zero. For x (or X) conversions, a non-zero + * result will have 0x (or 0X) prepended to it. + * For e, E, f, g, or G conversions, the result + * will always contain a radix character, even if + * no digits follow the point. For g and G + * conversions, trailing zeros will not be removed + * from the result. + */ + private boolean alternateForm = false; + /** + * Flag indicating that left padding with zeroes is + * specified. + */ + private boolean leadingZeros = false; + /** + * Flag indicating that the field width is *. + */ + private boolean variableFieldWidth = false; + /** + * If the converted value has fewer bytes than the + * field width, it will be padded with spaces or + * zeroes. + */ + private int fieldWidth = 0; + /** + * Flag indicating whether or not the field width + * has been set. + */ + private boolean fieldWidthSet = false; + /** + * The minimum number of digits to appear for the + * d, i, o, u, x, or X conversions. The number of + * digits to appear after the radix character for + * the e, E, and f conversions. The maximum number + * of significant digits for the g and G + * conversions. The maximum number of bytes to be + * printed from a string in s and S conversions. + */ + private int precision = 0; + /** Default precision. */ + private final static int defaultDigits = 6; + /** + * Flag indicating that the precision is *. + */ + private boolean variablePrecision = false; + /** + * Flag indicating whether or not the precision has + * been set. + */ + private boolean precisionSet = false; + /* + */ + private boolean positionalSpecification = false; + private int argumentPosition = 0; + private boolean positionalFieldWidth = false; + private int argumentPositionForFieldWidth = 0; + private boolean positionalPrecision = false; + private int argumentPositionForPrecision = 0; + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type + * short int. + */ + private boolean optionalh = false; + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type lont + * int argument. + */ + private boolean optionall = false; + /** + * Flag specifying that a following e, E, f, g, or + * G conversion character applies to a type double + * argument. This is a noop in Java. + */ + private boolean optionalL = false; + /** Control string type. */ + private char conversionCharacter = '\0'; + /** + * Position within the control string. Used by + * the constructor. + */ + private int pos = 0; + /** Literal or control format string. */ + private String fmt; + } + /** Vector of control strings and format literals. */ + private Vector vFmt = new Vector(); + /** Character position. Used by the constructor. */ + private int cPos = 0; + /** Character position. Used by the constructor. */ + private DecimalFormatSymbols dfs = null; +} diff --git a/src/jake2/util/Vargs.java b/src/jake2/util/Vargs.java new file mode 100644 index 0000000..b1ded8b --- /dev/null +++ b/src/jake2/util/Vargs.java @@ -0,0 +1,121 @@ +/* + * Vargs.java + * Copyright (C) 2003 + * + * $Id: Vargs.java,v 1.1 2004-07-07 19:59:56 hzi Exp $ + */ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +package jake2.util; + +import java.util.Vector; + +/** + * Vargs is a helper class to encapsulate printf arguments. + * + * @author cwei + */ +public class Vargs { + + // initial capacity + static final int SIZE = 5; + + Vector v; + + public Vargs() { + this(SIZE); + } + + public Vargs(int initialSize) { + if (v != null) + v.clear(); // clear previous list for GC + v = new Vector(initialSize); + } + + public Vargs add(boolean value) { + v.add(new Boolean(value)); + return this; + } + + public Vargs add(byte value) { + v.add(new Byte(value)); + return this; + } + + public Vargs add(char value) { + v.add(new Character(value)); + return this; + } + + public Vargs add(short value) { + v.add(new Short(value)); + return this; + } + + public Vargs add(int value) { + v.add(new Integer(value)); + return this; + } + + public Vargs add(long value) { + v.add(new Long(value)); + return this; + } + + public Vargs add(float value) { + v.add(new Float(value)); + return this; + } + + public Vargs add(double value) { + v.add(new Double(value)); + return this; + } + + public Vargs add(String value) { + v.add(value); + return this; + } + + public Vargs add(Object value) { + v.add(value); + return this; + } + + public Vargs clear() { + v.clear(); + return this; + } + + public Vector toVector() { + // Vector tmp = v; + // v = null; + // return tmp; + return (Vector) v.clone(); + } + + public Object[] toArray() { + return v.toArray(); + } + + public int size() { + return v.size(); + } +} |