From 7f10d78b8971c812127b996fdb1d556a53fd378f Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Thu, 13 Mar 2008 19:09:08 +0800
Subject: [PATCH] update for EOFM-16

---
 ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java   |  184 +
 .gitattributes                                                                       |   12 
 ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java   |  291 ++
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java                      |  323 ++
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java                  |   30 
 ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java  |   53 
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java              |  354 +++
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java         |  486 ++++
 /dev/null                                                                            |   51 
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java       |   97 
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java                  |  104 
 ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java              | 4774 +++++++++++++++++++++++++++++++++++++++++
 ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java |  108 
 13 files changed, 6,815 insertions(+), 52 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 848ed91..8bdb178 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -38,4 +38,14 @@
 ximple-jobcarrier/src/main/java/com/ximple/eofms/App.java svneol=native#text/plain
 ximple-jobcarrier/src/test/java/com/ximple/eofms/AppTest.java svneol=native#text/plain
 ximple-spatialjob/pom.xml svneol=native#text/xml
-ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/ConvertDgn2ShpJob.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java svneol=native#text/plain
+ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java svneol=native#text/plain
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
new file mode 100644
index 0000000..1699047
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
@@ -0,0 +1,108 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.sql.SQLException;
+
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import com.vividsolutions.jts.util.Assert;
+
+import oracle.sql.BLOB;
+
+public abstract class AbstractOracleDatabaseJob implements Job
+{
+    private static final String SHPDATA_DIR = "SHPDATA_DIR";
+    private static final String SPATAILSCHEMA = "ORGSCHEMA";
+    private static final String ORAHOST = "ORAHOST";
+    private static final String ORAINST = "ORAINST";
+    private static final String ORAPORT = "ORAPORT";
+    private static final String ORAUSER = "ORAUSER";
+    private static final String ORAPASS = "ORAPASS";
+
+    protected String _dataPath;
+    protected String _oracleHost;
+    protected String _oracleInstance;
+    protected String _oraclePort;
+    protected String _username;
+    protected String _password;
+    protected String _orgSchema;
+
+    public abstract void execute(JobExecutionContext context) throws JobExecutionException;
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        // The directory to scan is stored in the job map
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _dataPath = dataMap.getString(SHPDATA_DIR);
+        _oracleHost = dataMap.getString(ORAHOST);
+        _oracleInstance = dataMap.getString(ORAINST);
+        _oraclePort = dataMap.getString(ORAPORT);
+        _username = dataMap.getString(ORAUSER);
+        _password = dataMap.getString(ORAPASS);
+        _orgSchema = dataMap.getString(SPATAILSCHEMA);
+
+        // Validate the required input
+        if (_dataPath == null)
+        {
+            throw new JobExecutionException("Directory not configured");
+        }
+
+        // Make sure the directory exists
+        File dir = new File(_dataPath);
+        if (!dir.exists())
+        {
+            throw new JobExecutionException("Invalid Dir " + _dataPath);
+        }
+    }
+
+    protected OracleConvertJobContext prepareJobContext()
+    {
+        return new OracleConvertJobContext();
+    }
+
+    protected static byte[] getBytesFromBLOB(BLOB blob) throws SQLException
+    {
+        byte[] raw = null;
+
+        // BLOB        blob        = (BLOB) rs.getBlob(1);
+        int         optimalSize = blob.getChunkSize();
+        byte[]      chunk       = new byte[optimalSize];
+        InputStream is          = blob.getBinaryStream(0);
+        ByteBuffer buffer      = null;    // ByteBuffer.allocate(optimalSize);
+        int         len         = 0;
+
+        try
+        {
+            while ((len = (is.read(chunk))) != -1)
+            {
+                if (buffer != null)
+                {
+                    buffer.limit(buffer.limit() + len);
+                } else
+                {
+                    buffer = ByteBuffer.allocate(len);
+                }
+
+                buffer.put(chunk);
+            }
+
+            is.close();
+            buffer.position(0);
+            raw = buffer.array();
+        } catch (IOException e)
+        {
+            e.printStackTrace();    // To change body of catch statement use File | Settings | File Templates.
+            Assert.shouldNeverReachHere();
+        }
+
+        return raw;
+    }
+    
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/ConvertDgn2ShpJob.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/ConvertDgn2ShpJob.java
deleted file mode 100644
index 23a9524..0000000
--- a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/ConvertDgn2ShpJob.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.ximple.eofms.jobs;
-
-import java.io.File;
-import java.util.Date;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.quartz.Job;
-import org.quartz.JobDataMap;
-import org.quartz.JobDetail;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-
-/**
- *
- */
-public class ConvertDgn2ShpJob implements Job
-{
-    static Log logger = LogFactory.getLog(ConvertDgn2ShpJob.class);
-
-    public void execute(JobExecutionContext context) throws JobExecutionException
-    {
-        // Every job has its own job detail
-        JobDetail jobDetail = context.getJobDetail();
-
-        // The name is defined in the job definition
-        String jobName = jobDetail.getName();
-
-        // Log the time the job started
-        logger.info(jobName + " fired at " + new Date());
-
-        // The directory to scan is stored in the job map
-        JobDataMap dataMap = jobDetail.getJobDataMap();
-        String dirName = dataMap.getString("SHPDATA_DIR");
-
-        // Validate the required input
-        if (dirName == null)
-        {
-            throw new JobExecutionException("Directory not configured");
-        }
-
-        // Make sure the directory exists
-        File dir = new File(dirName);
-        if (!dir.exists())
-        {
-            throw new JobExecutionException("Invalid Dir " + dirName);
-        }
-
-
-    }
-}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
new file mode 100644
index 0000000..3bed880
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
@@ -0,0 +1,184 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OracleResultSet;
+import oracle.sql.ARRAY;
+import oracle.sql.BLOB;
+
+import com.ximple.eofms.util.BinConverter;
+import com.ximple.eofms.util.ByteArrayCompressor;
+import com.ximple.util.PrintfFormat;
+
+/**
+ *
+ */
+public class OracleConvertDgn2ShpJob extends AbstractOracleDatabaseJob
+{
+    static Log logger = LogFactory.getLog(OracleConvertDgn2ShpJob.class);
+
+    private static final int FETCHSIZE = 30;
+    private static final int BATCHSIZE = 25;
+
+    public void execute(JobExecutionContext context) throws JobExecutionException
+    {
+        // Every job has its own job detail
+        JobDetail jobDetail = context.getJobDetail();
+
+        // The name is defined in the job definition
+        String jobName = jobDetail.getName();
+
+        // Log the time the job started
+        logger.info(jobName + " fired at " + new Date());
+        extractJobConfiguration(jobDetail);
+
+        OracleConvertJobContext jobContext = prepareJobContext();
+        jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+        jobContext.setLogin(_username, _password);
+
+        try
+        {
+            exetcuteConvert(jobContext, _orgSchema, _dataPath);
+        } catch (SQLException e)
+        {
+            throw new JobExecutionException("Database error.", e);
+        }
+
+    }
+
+    private void exetcuteConvert(OracleConvertJobContext jobContext,
+                                 String querySchema, String dataPath) throws SQLException
+    {
+        // verify igdsset_seed
+        String srcTable = "IGSET_1";
+        String destTable = OracleConvertJobContext.TABLE_PREFIX + "ELMSET_1";
+        long startTime = System.currentTimeMillis();
+        ArrayList srcElms = queryIgsetElement(jobContext, querySchema, srcTable);
+        long currentTime = System.currentTimeMillis();
+        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS");
+
+        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+        long elapsed = currentTime - startTime;
+
+        System.out.println("Old Format:" + dateFormat.format(new Date(elapsed)));
+        logger.info("query source element complete.");
+        startTime = System.currentTimeMillis();
+
+        ArrayList dstElms = queryRawElement(jobContext, querySchema, destTable);
+
+        currentTime = System.currentTimeMillis();
+        dateFormat = new SimpleDateFormat("HH:mm:ss:SSS");
+        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+        elapsed = currentTime - startTime;
+        System.out.println("New Format:" + dateFormat.format(new Date(elapsed)));
+        logger.info("query dest element complete.");
+        // Assert.isTrue(srcElms.size() == dstElms.size(), "src[" + srcElms.size() + "] != dest[" + dstElms.size() + "]");
+    }
+
+    private ArrayList queryIgsetElement(OracleConvertJobContext jobContext,
+                                        String srcschema, String srctable) throws SQLException
+    {
+        ArrayList result = new ArrayList();
+        OracleConnection connection = jobContext.getOracleConnection();
+        String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchSrcStmtFmt);
+        String fetchSrcStmt = spf.sprintf(new Object[]{srcschema, srctable});
+        Statement stmtSrc = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmtSrc.setFetchSize(FETCHSIZE);
+
+        ResultSet rsSrc = stmtSrc.executeQuery(fetchSrcStmt);
+
+        while (rsSrc.next())
+        {
+            byte[] raw = null;
+
+            if (rsSrc.getMetaData().getColumnType(1) == Types.BLOB)
+            {
+                BLOB blob = (BLOB) rsSrc.getBlob(1);
+
+                raw = getBytesFromBLOB(blob);
+                blob.close();
+            } else
+            {
+                raw = rsSrc.getBytes(1);
+            }
+
+            result.add(raw);
+        }
+
+        return result;
+    }
+
+    private ArrayList queryRawElement(OracleConvertJobContext jobContext,
+                                      String srcschema, String srctable) throws SQLException
+    {
+        ArrayList result = new ArrayList();
+        OracleConnection connection = jobContext.getOracleConnection();
+        String fetchDestStmtFmt = "SELECT ELEMENT FROM \"%s\".\"%s\" ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchDestStmtFmt);
+        String fetchDestStmt = spf.sprintf(new Object[]{srcschema, srctable});
+        Statement stmtDest = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmtDest.setFetchSize(FETCHSIZE);
+
+        ResultSet rsDest = stmtDest.executeQuery(fetchDestStmt);
+
+        while (rsDest.next())
+        {
+            ARRAY rawsValue = ((OracleResultSet) rsDest).getARRAY(1);
+            long[] rawData = rawsValue.getLongArray();
+            byte[] comparessedValue;
+
+            /*
+            if (dataMode == TransferTask.DataMode.Normal)
+            {
+                comparessedValue = BinConverter.unmarshalByteArray(rawData, true);
+            } else
+            {
+                comparessedValue = BinConverter.unmarshalCompactByteArray(rawData);
+            }
+            */
+            comparessedValue = BinConverter.unmarshalByteArray(rawData, true);
+
+            byte[] rawDest = ByteArrayCompressor.decompressByteArray(comparessedValue);
+
+            result.add(rawDest);
+        }
+
+        return result;
+    }
+
+    private boolean equalRawData(byte[] rawSrc, byte[] rawDest)
+    {
+        if (rawSrc.length != rawDest.length)
+        {
+            return false;
+        }
+
+        for (int i = 0; i < rawSrc.length; i++)
+        {
+            if (rawSrc[i] != rawDest[i])
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
new file mode 100644
index 0000000..fee578a
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
@@ -0,0 +1,291 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.vividsolutions.jts.util.Assert;
+
+import oracle.jdbc.OracleConnection;
+
+public class OracleConvertJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertJobContext.class);
+
+    private static final String ORACLE_URL = "jdbc:oracle:thin:@";
+    private static final String PROPUsrKey = "user";
+    private static final String PROPPassKey = "password";
+
+    /**
+     * Table Prefiex
+     */
+    protected static final String TABLE_PREFIX = "GEO$";
+    protected static final String UDT_SCHEMA = "SPATIALDB";
+
+    /**
+     * User Types
+     */
+    protected static final String UDT_RAWS = "CREATE OR REPLACE TYPE \"" + UDT_SCHEMA + "\".\"RAWS\" AS VARRAY (1048576) OF NUMBER(38)";
+    protected static final String UDT_OFMID = "CREATE OR REPLACE TYPE  \"" + UDT_SCHEMA + "\".\"OFMID\" AS OBJECT ("
+            + "\"CLSID\" NUMBER(5), \"OID\" NUMBER(10), \"STATUS\" NUMBER(5), \"COMPID\" NUMBER(3), "
+            + "\"RULEID\" NUMBER(3), \"OCCID\" NUMBER(3))";
+
+    protected static final String UDT_RAWSNAME = "RAWS";
+    protected static final String UDT_OFMIDNAME = "OFMID";
+
+    /**
+     * Utility SQL
+     */
+    protected static final String TAB_DROP = "DROP TABLE %s.%s CASCADE CONSTRAINTS";
+
+    protected static final String TAB_DELETE = "DELETE FROM %s.%s";
+
+    /**
+     * Table Schema
+     */
+    protected static final String TAB_RANGENODEINDEX_1 = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RPID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWX\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWY\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHX\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHY\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"EXTENTS\" MDSYS.SDO_GEOMETRY,\n"
+            + "    \"RNDESCR\" VARCHAR2(255), \n"
+            + "    PRIMARY KEY ( \"RNID\" ) ENABLE )";
+
+    protected static final String TAB_RANGENODEINDEX = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RPID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWX\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWY\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHX\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHY\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNDESCR\" VARCHAR2(255), \n"
+            + "    PRIMARY KEY ( \"RNID\" ) ENABLE )";
+
+    protected static final String TAB_RANGENODESTORAGE = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"LAYERID\" NUMBER(5,0) NOT NULL ENABLE,\n"
+            + "    \"LASTUPDATE\" DATE NOT NULL ENABLE,\n"
+            + "    \"SPACETABLE\" VARCHAR2(255)\n" + "   )";
+
+    protected static final String TAB_ELEMENTINDEX_1 = "CREATE TABLE %s.%s (\n"
+            + "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"TYPE\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"ZLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"ZHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n"
+            + "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"SPACENAME\" VARCHAR2(255) NOT NULL ENABLE,\n"
+            + "     PRIMARY KEY (\"ELMNO\") ENABLE\n"
+            + "   )";
+
+    protected static final String TAB_ELEMENTINDEX = "CREATE TABLE %s.%s (\n"
+            + "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"TYPE\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"XLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"YLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"ZLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"XHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"YHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"ZHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n"
+            + "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"SPACENAME\" VARCHAR2(255) NOT NULL ENABLE,\n"
+            + "     PRIMARY KEY (\"ELMNO\") ENABLE\n"
+            + "   )";
+
+    protected static final String TAB_IGDSSEED = "CREATE TABLE  %s.%s (\n"
+            + "   \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "   \"SEEDELM\" \"" + UDT_SCHEMA
+            + "\".\"RAWS\" NOT NULL ENABLE\n" + "   )";
+
+    protected static final String TAB_STORAGE_1 = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"EXTENTS\" MDSYS.SDO_GEOMETRY, \n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n" +
+            "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE, \n" +
+            "    \"GEOM\" MDSYS.SDO_GEOMETRY \n" +
+            "   )";
+
+    protected static final String TAB_STORAGE = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"YLOW\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n" +
+            "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE \n" +
+            "   )";
+
+    protected static final String TAB_STORAGE2 = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"EXTENTS\" MDSYS.SDO_GEOMETRY, \n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"ID\" " + UDT_SCHEMA + ".OFMID NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE \n" +
+            "   )";
+    /**
+     * Trigger
+     */
+    protected static final String TRG_SPACENODE = "CREATE OR REPLACE TRIGGER \"%s\".\"%s\"\n"
+            + "    AFTER DELETE OR INSERT OR UPDATE ON \"%s\".\"%s\"\n" + "    BEGIN\n"
+            + "        UPDATE SPACENODES SET LASTUPDATE = SYSDATE\n" + "        WHERE SNID = \"%d\";\n"
+            + "    END;";
+
+    protected static final String TRG_ELMINDEX =
+                    "CREATE OR REPLACE TRIGGER \"%s\".\"%s\"\n"
+                    + "    AFTER INSERT OR UPDATE OR DELETE ON \"%s\".\"%s\"\n"
+                    + "    REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW\n"
+                    + "    BEGIN\n"
+                    + "        IF INSERTING THEN\n"
+                    + "            INSERT INTO \"%s\".\"%s\" (ELMNO, TYPE, XLOW, YLOW, XHIGH, YHIGH,\n"
+                    + "                UFID, FSC, COMPID, OCCID, SPACENAME)\n"
+                    + "            VALUES (SD$ELEMENTNUMBER_SEQ.NEXTVAL, :new.ELMTYPE, :new.XLOW, :new.YLOW, :new.XHIGH, :new.YHIGH,\n"
+                    + "                :new.UFID, :new.FSC, :new.COMPID, :new.OCCID, '%s');\n"
+                    + "        ELSIF DELETING THEN\n"
+                    + "            DELETE FROM \"%s\".\"%s\"\n"
+                    + "            WHERE \"%s\".UFID = :old.UFID AND\n"
+                    + "                \"%s\".FSC = :old.FSC AND\n"
+                    + "                \"%s\".COMPID = :old.COMPID AND\n"
+                    + "                \"%s\".OCCID = :old.OCCID;\n"
+                    + "        ELSE\n" + "            UPDATE \"%s\"\n"
+                    + "            SET XLOW = :new.XLOW, YLOW = :new.YLOW, XHIGH = :new.XHIGH, YHIGH = :new.YHIGH\n"
+                    + "            WHERE FSC = :new.FSC AND UFID = :new.UFID AND COMPID = :new.COMPID AND OCCID = :new.OCCID;\n"
+                    + "        END IF;\n" + "    END;";
+
+    /**
+     *
+     */
+    protected static final String TAB_ELEMENTSET_PREFIX = "ELMSET_";
+
+    protected static final String STMT_CLEARCYCLEBIN = "PURGE RECYCLEBIN";
+
+    protected static final String SMTM_GRANTOBJECTTYPE = "GRANT EXECUTE ANY TYPE TO \"" + UDT_SCHEMA + "\"";
+
+    private String _oracleHost;
+    private String _oracleInstance;
+    private String _oraclePort;
+
+    static
+    {
+        try
+        {
+            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
+        } catch (SQLException e)
+        {
+            Assert.shouldNeverReachHere(e.getMessage());
+        }
+    }
+
+    public static String getCurrentURL(String oracleHost, String oraclePort, String oracleInstance)
+    {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(ORACLE_URL);
+        builder.append(oracleHost);
+        builder.append(":");
+        builder.append(oraclePort);
+        builder.append(":");
+        builder.append(oracleInstance);
+
+        return builder.toString();
+    }
+
+    private OracleConnection oracleConnection = null;
+    private Properties properties;
+
+    public OracleConvertJobContext()
+    {
+        properties = new Properties();
+    }
+
+    public void setLogin(String userName, String password)
+    {
+        properties.put(PROPUsrKey, userName);
+        properties.put(PROPPassKey, password);
+    }
+
+    public OracleConnection getOracleConnection()
+    {
+        try
+        {
+            if (oracleConnection == null)
+            {
+                oracleConnection = (OracleConnection) DriverManager.getConnection(
+                        getCurrentURL(_oracleHost, _oraclePort, _oracleInstance),
+                        properties);
+            }
+
+            return oracleConnection;
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+        }
+
+        oracleConnection = null;
+
+        return null;
+    }
+
+    public void closeConnection()
+    {
+        try
+        {
+            if (oracleConnection != null)
+            {
+                oracleConnection.close();
+                oracleConnection = null;
+            }
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public void setConnectionInfo(String oracleHost, String oraclePort, String oracleInstance)
+    {
+        _oracleHost = oracleHost;
+        _oracleInstance = oracleInstance;
+        _oraclePort = oraclePort;
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
new file mode 100644
index 0000000..eb653c0
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
@@ -0,0 +1,53 @@
+package com.ximple.eofms.jobs;
+
+import java.util.Date;
+import java.sql.SQLException;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.JobDetail;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OracleDatabaseMetaData;
+import oracle.jdbc.OracleStatement;
+
+public class OracleUpgradeBlob2UDTJob extends AbstractOracleDatabaseJob
+{
+    static Log logger = LogFactory.getLog(OracleUpgradeBlob2UDTJob.class);
+
+    public void execute(JobExecutionContext context) throws JobExecutionException
+    {
+        // Every job has its own job detail
+        JobDetail jobDetail = context.getJobDetail();
+
+        // The name is defined in the job definition
+        String jobName = jobDetail.getName();
+
+        // Log the time the job started
+        logger.info(jobName + " fired at " + new Date());
+        extractJobConfiguration(jobDetail);
+
+        OracleConvertJobContext jobContext = prepareJobContext();
+        jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+        jobContext.setLogin(_username, _password);
+
+        try
+        {
+            exetcuteConvert(jobContext, _orgSchema, _dataPath);
+        } catch (SQLException e)
+        {
+            throw new JobExecutionException("Database error.", e);
+        }
+    }
+
+    private void exetcuteConvert(OracleConvertJobContext jobContext,
+                                 String orgSchema, String dataPath) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        OracleDatabaseMetaData metaData   = (OracleDatabaseMetaData) connection.getMetaData();
+        OracleStatement statement  = (OracleStatement) connection.createStatement();
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
new file mode 100644
index 0000000..707ad61
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
@@ -0,0 +1,354 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.LongBuffer;
+
+import org.testng.Assert;
+
+/**
+ * BinConverter
+ * User: Ulysses
+ * Date: 2007/9/17
+ * Time: �W�� 01:13:13
+ */
+public class BinConverter
+{
+    // our table for binhex conversion
+    final static char[] HEXTAB =
+    {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    /**
+     *  gets bytes from an array into a long
+     *  @param buffer where to get the bytes
+     *  @param nStartIndex index from where to read the data
+     *  @return the 64bit integer
+     */
+    public static long byteArrayToLong(byte[] buffer, int nStartIndex)
+    {
+        return (((long) buffer[nStartIndex]) << 56) | (((long) buffer[nStartIndex + 1] & 0x0ffL) << 48)
+               | (((long) buffer[nStartIndex + 2] & 0x0ffL) << 40) | (((long) buffer[nStartIndex + 3] & 0x0ffL) << 32)
+               | (((long) buffer[nStartIndex + 4] & 0x0ffL) << 24) | (((long) buffer[nStartIndex + 5] & 0x0ffL) << 16)
+               | (((long) buffer[nStartIndex + 6] & 0x0ffL) << 8) | ((long) buffer[nStartIndex + 7] & 0x0ff);
+    }
+
+    /**
+     *  converts a long o bytes which are put into a given array
+     *  @param lValue the 64bit integer to convert
+     *  @param buffer the target buffer
+     *  @param nStartIndex where to place the bytes in the buffer
+     */
+    public static void longToByteArray(long lValue, byte[] buffer, int nStartIndex)
+    {
+        buffer[nStartIndex]     = (byte) (lValue >>> 56);
+        buffer[nStartIndex + 1] = (byte) ((lValue >>> 48) & 0x0ff);
+        buffer[nStartIndex + 2] = (byte) ((lValue >>> 40) & 0x0ff);
+        buffer[nStartIndex + 3] = (byte) ((lValue >>> 32) & 0x0ff);
+        buffer[nStartIndex + 4] = (byte) ((lValue >>> 24) & 0x0ff);
+        buffer[nStartIndex + 5] = (byte) ((lValue >>> 16) & 0x0ff);
+        buffer[nStartIndex + 6] = (byte) ((lValue >>> 8) & 0x0ff);
+        buffer[nStartIndex + 7] = (byte) lValue;
+    }
+
+    /**
+     *  converts values from an integer array to a long
+     *  @param buffer where to get the bytes
+     *  @param nStartIndex index from where to read the data
+     *  @return the 64bit integer
+     */
+    public static long intArrayToLong(int[] buffer, int nStartIndex)
+    {
+        return (((long) buffer[nStartIndex]) << 32) | (((long) buffer[nStartIndex + 1]) & 0x0ffffffffL);
+    }
+
+    /**
+     *  converts a long to integers which are put into a given array
+     *  @param lValue the 64bit integer to convert
+     *  @param buffer the target buffer
+     *  @param nStartIndex where to place the bytes in the buffer
+     */
+    public static void longToIntArray(long lValue, int[] buffer, int nStartIndex)
+    {
+        buffer[nStartIndex]     = (int) (lValue >>> 32);
+        buffer[nStartIndex + 1] = (int) lValue;
+    }
+
+    /**
+     *  makes a long from two integers (treated unsigned)
+     *  @param nLo lower 32bits
+     *  @param nHi higher 32bits
+     *  @return the built long
+     */
+    public static long makeLong(int nLo, int nHi)
+    {
+        return (((long) nHi << 32) | ((long) nLo & 0x00000000ffffffffL));
+    }
+
+    /**
+     *  gets the lower 32 bits of a long
+     *  @param lVal the long integer
+     *  @return lower 32 bits
+     */
+    public static int longLo32(long lVal)
+    {
+        return (int) lVal;
+    }
+
+    /**
+     *  gets the higher 32 bits of a long
+     *  @param lVal the long integer
+     *  @return higher 32 bits
+     */
+    public static int longHi32(long lVal)
+    {
+        return (int) ((long) (lVal >>> 32));
+    }
+
+    /**
+     *  converts a byte array to a binhex string
+     *  @param data the byte array
+     *  @return the binhex string
+     */
+    public static String bytesToBinHex(byte[] data)
+    {
+        // just map the call
+        return bytesToBinHex(data, 0, data.length);
+    }
+
+    /**
+     *  converts a byte array to a binhex string
+     *  @param data the byte array
+     *  @param nStartPos start index where to get the bytes
+     *  @param nNumOfBytes number of bytes to convert
+     *  @return the binhex string
+     */
+    public static String bytesToBinHex(byte[] data, int nStartPos, int nNumOfBytes)
+    {
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.setLength(nNumOfBytes << 1);
+
+        int nPos = 0;
+
+        for (int nI = 0; nI < nNumOfBytes; nI++)
+        {
+            sbuf.setCharAt(nPos++, HEXTAB[(data[nI + nStartPos] >> 4) & 0x0f]);
+            sbuf.setCharAt(nPos++, HEXTAB[data[nI + nStartPos] & 0x0f]);
+        }
+
+        return sbuf.toString();
+    }
+
+    /**
+     *  converts a binhex string back into a byte array (invalid codes will be skipped)
+     *  @param sBinHex binhex string
+     *  @param data the target array
+     *  @param nSrcPos from which character in the string the conversion should begin,
+     *                 remember that (nSrcPos modulo 2) should equals 0 normally
+     *  @param nDstPos to store the bytes from which position in the array
+     *  @param nNumOfBytes number of bytes to extract
+     *  @return number of extracted bytes
+     */
+    public static int binHexToBytes(String sBinHex, byte[] data, int nSrcPos, int nDstPos, int nNumOfBytes)
+    {
+        // check for correct ranges
+        int nStrLen     = sBinHex.length();
+        int nAvailBytes = (nStrLen - nSrcPos) >> 1;
+
+        if (nAvailBytes < nNumOfBytes)
+        {
+            nNumOfBytes = nAvailBytes;
+        }
+
+        int nOutputCapacity = data.length - nDstPos;
+
+        if (nNumOfBytes > nOutputCapacity)
+        {
+            nNumOfBytes = nOutputCapacity;
+        }
+
+        // convert now
+        int nResult = 0;
+
+        for (int nI = 0; nI < nNumOfBytes; nI++)
+        {
+            byte    bActByte    = 0;
+            boolean blConvertOK = true;
+
+            for (int nJ = 0; nJ < 2; nJ++)
+            {
+                bActByte <<= 4;
+
+                char cActChar = sBinHex.charAt(nSrcPos++);
+
+                if ((cActChar >= 'a') && (cActChar <= 'f'))
+                {
+                    bActByte |= (byte) (cActChar - 'a') + 10;
+                } else if ((cActChar >= '0') && (cActChar <= '9'))
+                {
+                    bActByte |= (byte) (cActChar - '0');
+                } else
+                {
+                    blConvertOK = false;
+                }
+            }
+
+            if (blConvertOK)
+            {
+                data[nDstPos++] = bActByte;
+                nResult++;
+            }
+        }
+
+        return nResult;
+    }
+
+    /**
+     *  converts a byte array into an UNICODE string
+     *  @param data the byte array
+     *  @param nStartPos where to begin the conversion
+     *  @param nNumOfBytes number of bytes to handle
+     *  @return the string
+     */
+    public static String byteArrayToUNCString(byte[] data, int nStartPos, int nNumOfBytes)
+    {
+        // we need two bytes for every character
+        nNumOfBytes &= ~1;
+
+        // enough bytes in the buffer?
+        int nAvailCapacity = data.length - nStartPos;
+
+        if (nAvailCapacity < nNumOfBytes)
+        {
+            nNumOfBytes = nAvailCapacity;
+        }
+
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.setLength(nNumOfBytes >> 1);
+
+        int nSBufPos = 0;
+
+        while (nNumOfBytes > 0)
+        {
+            sbuf.setCharAt(nSBufPos++, (char) (((int) data[nStartPos] << 8) | ((int) data[nStartPos + 1] & 0x0ff)));
+            nStartPos   += 2;
+            nNumOfBytes -= 2;
+        }
+
+        return sbuf.toString();
+    }
+
+    public static long[] marshalByteArray(byte[] raws, boolean hasSignature)
+    {
+        int        remainder = raws.length % 8;
+        ByteBuffer rawData   = ByteBuffer.wrap(raws);
+
+        rawData.rewind();
+        rawData.order(ByteOrder.LITTLE_ENDIAN);
+
+        LongBuffer longBuffer = ((ByteBuffer) rawData.rewind()).asLongBuffer();
+        int        resultSize = longBuffer.limit() + ((remainder != 0)
+            ? 1
+            : 0) + (hasSignature
+                    ? 1
+                    : 0);
+        long[]     result     = new long[resultSize];
+        int        i          = 0;
+
+        if (hasSignature)
+        {
+            result[i] = raws.length;
+            i++;
+        }
+
+        while (longBuffer.hasRemaining())
+        {
+            result[i] = longBuffer.get();
+            i++;
+        }
+
+        if (remainder != 0)
+        {
+            int pos = (i - (hasSignature
+                            ? 1
+                            : 0)) * 8;
+
+            // int pos = rawData.position();
+            byte[] temp = new byte[8];
+
+            for (int j = 0; j < remainder; j++)
+            {
+                temp[7 - j] = raws[pos + j];
+            }
+
+            // System.arraycopy(raws, pos, temp, 0, remainder);
+            result[i] = BinConverter.byteArrayToLong(temp, 0);
+        }
+
+        return result;
+    }
+
+    public static byte[] unmarshalByteArray(long[] raws, boolean hasSignature)
+    {
+        LongBuffer longBuffer       = LongBuffer.wrap(raws);
+        int        resultBufferSize = (raws.length - (hasSignature
+            ? 1
+            : 0)) * 8;
+        int        resultSize       = resultBufferSize;
+
+        if (hasSignature)
+        {
+            resultSize = (int) longBuffer.get();
+        }
+
+        ByteBuffer result = ByteBuffer.allocate(resultBufferSize);
+
+        result.order(ByteOrder.LITTLE_ENDIAN);
+
+        while (longBuffer.hasRemaining())
+        {
+            result.putLong(longBuffer.get());
+        }
+
+        if (resultSize == resultBufferSize)
+        {
+            return result.array();
+        }
+
+        byte[] resultData = new byte[resultSize];
+
+        result.position(0);
+        result.get(resultData, 0, resultSize);
+
+        return resultData;
+    }
+
+    public static long[] marshalCompactByteArray(byte[] raws)
+    {
+        byte[] compactRaws = new byte[raws.length + 2];
+        ByteBuffer bbCompact = ByteBuffer.wrap(compactRaws);
+        bbCompact.order(ByteOrder.LITTLE_ENDIAN);
+        bbCompact.putShort((short) raws.length);
+        bbCompact.put(raws);
+        long[] longData = BinConverter.marshalByteArray(compactRaws, false);
+        return longData;
+    }
+
+    public static byte[] unmarshalCompactByteArray(long[] raws)
+    {
+        byte[] rawData = BinConverter.unmarshalByteArray(raws, false);
+
+        ByteBuffer bbCompact = ByteBuffer.wrap(rawData);
+        bbCompact.order(ByteOrder.LITTLE_ENDIAN);
+        short originSize = bbCompact.getShort();
+
+        byte[] rawOriginData = new byte[originSize];
+        bbCompact.get(rawOriginData, 0, originSize);
+        return rawOriginData;
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java
new file mode 100644
index 0000000..092569c
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java
@@ -0,0 +1,323 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteOrder;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.misc.Unsafe;
+import sun.misc.VM;
+
+/**
+ * Bits
+ * User: Ulysses
+ * Date: 2007/6/17
+ * Time: �W�� 01:16:39
+ */
+public class Bits
+{
+    // -- Unsafe access --
+    // private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    // -- Processor and memory-system properties --
+    private static ByteOrder byteOrder = null;
+    private static int       pageSize  = -1;
+    private static boolean   unaligned;
+    private static boolean   unalignedKnown = false;
+
+    // -- Direct memory management --
+    // A user-settable upper limit on the maximum amount of allocatable
+//  direct buffer memory. This value may be changed during VM
+    // initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
+    private static volatile long maxMemory      = VM.maxDirectMemory();
+    private static volatile long reservedMemory = 0;
+    private static boolean       memoryLimitSet = false;
+
+    private Bits()
+    {
+    }
+
+    // -- Swapping --
+    public static short swap(short x)
+    {
+        return (short) ((x << 8) | ((x >> 8) & 0xff));
+    }
+
+    public static char swap(char x)
+    {
+        return (char) ((x << 8) | ((x >> 8) & 0xff));
+    }
+
+    public static int swap(int x)
+    {
+        return (int) ((swap((short) x) << 16) | (swap((short) (x >> 16)) & 0xffff));
+    }
+
+    public static long swap(long x)
+    {
+        return (long) (((long) swap((int) (x)) << 32) | ((long) swap((int) (x >> 32)) & 0xffffffffL));
+    }
+
+    // -- get/put char --
+    static private char makeChar(byte b1, byte b0)
+    {
+        return (char) ((b1 << 8) | (b0 & 0xff));
+    }
+
+    private static byte char1(char x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte char0(char x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // --get/put short--
+    public static short makeShort(byte b1, byte b0)
+    {
+        return (short) ((b1 << 8) | (b0 & 0xff));
+    }
+
+    private static byte short1(short x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    public static byte short0(short x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put int --
+    public static int makeInt(byte b3, byte b2, byte b1, byte b0)
+    {
+        return (int) ((((b3 & 0xff) << 24) | ((b2 & 0xff) << 16) | ((b1 & 0xff) << 8) | ((b0 & 0xff) << 0)));
+    }
+
+    public static int makeInt(short hiword, short loword)
+    {
+        return ((hiword & 0xffff) << 16) + (loword & 0xffff);
+    }
+
+    public static short getHiShort(int qwValue)
+    {
+        return ((short) (qwValue >>> 16));
+    }
+
+    public static short getLoShort(int qwValue)
+    {
+        return ((short) (qwValue & 0xFFFF));
+    }
+
+    public static byte int3(int x)
+    {
+        return (byte) (x >> 24);
+    }
+
+    public static byte int2(int x)
+    {
+        return (byte) (x >> 16);
+    }
+
+    private static byte int1(int x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte int0(int x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put long --
+    public static long makeLong(byte b7, byte b6, byte b5, byte b4, byte b3, byte b2, byte b1, byte b0)
+    {
+        return ((((long) b7 & 0xff) << 56) | (((long) b6 & 0xff) << 48) | (((long) b5 & 0xff) << 40) | (((long) b4 & 0xff) << 32)
+                | (((long) b3 & 0xff) << 24) | (((long) b2 & 0xff) << 16) | (((long) b1 & 0xff) << 8)
+                | (((long) b0 & 0xff) << 0));
+    }
+
+    public static long makeLong(int LoValue, int HiValue)
+    {
+        return (((long) HiValue & 0xFFFFFFFF) << 32) + (((long) LoValue) & 0xFFFFFFFF);
+    }
+
+    public static int getHiInt(long qwValue)
+    {
+        return ((int)(qwValue >>> 32));
+    }
+
+    public static int getLoInt(long qwValue)
+    {
+        return ((int) (qwValue & 0xFFFFFFFF));
+    }
+    
+    private static byte long7(long x)
+    {
+        return (byte) (x >> 56);
+    }
+
+    private static byte long6(long x)
+    {
+        return (byte) (x >> 48);
+    }
+
+    private static byte long5(long x)
+    {
+        return (byte) (x >> 40);
+    }
+
+    private static byte long4(long x)
+    {
+        return (byte) (x >> 32);
+    }
+
+    private static byte long3(long x)
+    {
+        return (byte) (x >> 24);
+    }
+
+    private static byte long2(long x)
+    {
+        return (byte) (x >> 16);
+    }
+
+    private static byte long1(long x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte long0(long x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put float --
+
+    // -- get/put double --
+
+    /*
+    private static byte _get(long a)
+    {
+        return unsafe.getByte(a);
+    }
+
+    private static void _put(long a, byte b)
+    {
+        unsafe.putByte(a, b);
+    }
+
+    static Unsafe unsafe()
+    {
+        return unsafe;
+    }
+
+    static ByteOrder byteOrder()
+    {
+        if (byteOrder != null)
+        {
+            return byteOrder;
+        }
+
+        long a = unsafe.allocateMemory(8);
+
+        try
+        {
+            unsafe.putLong(a, 0x0102030405060708L);
+
+            byte b = unsafe.getByte(a);
+
+            switch (b)
+            {
+            case 0x01 :
+                byteOrder = ByteOrder.BIG_ENDIAN;
+
+                break;
+
+            case 0x08 :
+                byteOrder = ByteOrder.LITTLE_ENDIAN;
+
+                break;
+
+            default :
+                throw new Error("Unknown byte order");
+            }
+        } finally
+        {
+            unsafe.freeMemory(a);
+        }
+
+        return byteOrder;
+    }
+    */
+    static boolean unaligned()
+    {
+        if (unalignedKnown)
+        {
+            return unaligned;
+        }
+
+        PrivilegedAction pa   = new sun.security.action.GetPropertyAction("os.arch");
+        String           arch = (String) AccessController.doPrivileged(pa);
+
+        unaligned      = arch.equals("i386") || arch.equals("x86");
+        unalignedKnown = true;
+
+        return unaligned;
+    }
+
+    // These methods should be called whenever direct memory is allocated or
+    //  freed. They allow the user to control the amount of direct memory
+    // which a process may access. All sizes are specified in bytes.
+    static void reserveMemory(long size)
+    {
+        synchronized (Bits.class)
+        {
+            if (!memoryLimitSet && VM.isBooted())
+            {
+                maxMemory      = VM.maxDirectMemory();
+                memoryLimitSet = true;
+            }
+
+            if (size <= maxMemory - reservedMemory)
+            {
+                reservedMemory += size;
+
+                return;
+            }
+        }
+
+        System.gc();
+
+        try
+        {
+            Thread.sleep(100);
+        } catch (InterruptedException x)
+        {
+            // Restore interrupt status
+            Thread.currentThread().interrupt();
+        }
+
+        synchronized (Bits.class)
+        {
+            if (reservedMemory + size > maxMemory)
+            {
+                throw new OutOfMemoryError("Direct buffer memory");
+            }
+
+            reservedMemory += size;
+        }
+    }
+
+    static synchronized void unreserveMemory(long size)
+    {
+        if (reservedMemory > 0)
+        {
+            reservedMemory -= size;
+            assert(reservedMemory > -1);
+        }
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java
new file mode 100644
index 0000000..f22eaec
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java
@@ -0,0 +1,97 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+/**
+ * ByteArrayCompressor
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �U�� 02:21:00
+ * To change this template use File | Settings | File Templates.
+ */
+public final class ByteArrayCompressor
+{
+    public static byte[] decompressByteArray(byte[] raw)
+    {
+        // Create the decompressor and give it the data to compress
+        Inflater decompressor = new Inflater();
+
+        decompressor.setInput(raw);
+
+        // Create an expandable byte array to hold the decompressed data
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(raw.length);
+
+        // Decompress the data
+        byte[] buf = new byte[1024];
+
+        while (!decompressor.finished())
+        {
+            try
+            {
+                int count = decompressor.inflate(buf);
+
+                bos.write(buf, 0, count);
+            } catch (DataFormatException e)
+            {
+            }
+        }
+
+        try
+        {
+            bos.close();
+        } catch (IOException e)
+        {
+        }
+
+        // Get the decompressed data
+        byte[] decompressedData = bos.toByteArray();
+
+        return decompressedData;
+    }
+
+    public static byte[] compressByteArray(byte[] raw)
+    {
+        // Create the compressor with highest level of compression
+        Deflater compressor = new Deflater();
+
+        compressor.setLevel(Deflater.BEST_SPEED);
+
+        // Give the compressor the data to compress
+        compressor.setInput(raw);
+        compressor.finish();
+
+        // Create an expandable byte array to hold the compressed data.
+        // You cannot use an array that's the same size as the orginal because
+        // there is no guarantee that the compressed data will be smaller than
+        // the uncompressed data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(raw.length);
+
+        // Compress the data
+        byte[] buf = new byte[1024];
+
+        while (!compressor.finished())
+        {
+            int count = compressor.deflate(buf);
+
+            bos.write(buf, 0, count);
+        }
+
+        try
+        {
+            bos.close();
+        } catch (IOException e)
+        {
+        }
+
+        // Get the compressed data
+        byte[] compressedData = bos.toByteArray();
+
+        return compressedData;
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java
new file mode 100644
index 0000000..023ee69
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java
@@ -0,0 +1,30 @@
+package com.ximple.eofms.util;
+
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �W�� 01:20:20
+ * To change this template use File | Settings | File Templates.
+ */
+public final class GeomUtil
+{
+    public static double convertLogicalValue(double value)
+    {
+        return value / 1000.0 + 2147483.648;
+    }
+
+    public static Coordinate convertLogicalCooridate(Coordinate value)
+    {
+        return new Coordinate(convertLogicalValue(value.x), convertLogicalValue(value.y), convertLogicalValue(value.z));
+    }
+
+    public static Envelope convertLogicalEnvelope(Envelope value)
+    {
+        return new Envelope(convertLogicalValue(value.getMinX()), convertLogicalValue(value.getMaxX()),
+                            convertLogicalValue(value.getMinY()), convertLogicalValue(value.getMaxY()));
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java
new file mode 100644
index 0000000..794c27b
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java
@@ -0,0 +1,104 @@
+package com.ximple.eofms.util;
+
+import java.util.Collection;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �W�� 01:21:25
+ * To change this template use File | Settings | File Templates.
+ */
+public class LangUtil
+{
+    private static Map primitiveToWrapperMap = new HashMap()
+    {
+
+        {
+            put(byte.class, Byte.class);
+            put(char.class, Character.class);
+            put(short.class, Short.class);
+            put(int.class, Integer.class);
+            put(long.class, Long.class);
+            put(float.class, Float.class);
+            put(double.class, Double.class);
+            put(boolean.class, Boolean.class);
+        }
+    };
+
+    public static String emptyStringIfNull(String s)
+    {
+        return (s == null) ? "" : s;
+    }
+
+    /**
+     * Useful because an expression used to generate o need only be
+     * evaluated once.
+     */
+    public static Object ifNull(Object o, Object alternative)
+    {
+        return (o == null) ? alternative : o;
+    }
+
+    public static Object ifNotNull(Object o, Object alternative)
+    {
+        return (o != null) ? alternative : o;
+    }
+
+    public static Class toPrimitiveWrapperClass(Class primitiveClass)
+    {
+        return (Class) primitiveToWrapperMap.get(primitiveClass);
+    }
+
+    public static boolean isPrimitive(Class c)
+    {
+        return primitiveToWrapperMap.containsKey(c);
+    }
+
+    public static boolean bothNullOrEqual(Object a, Object b)
+    {
+        return (a == null && b == null) || (a != null && b != null && a.equals(b));
+    }
+
+    public static Object newInstance(Class c)
+    {
+        try
+        {
+            return c.newInstance();
+        } catch (Exception e)
+        {
+            Assert.shouldNeverReachHere(e.toString());
+            return null;
+        }
+    }
+
+    public static Collection classesAndInterfaces(Class c)
+    {
+        ArrayList classesAndInterfaces = new ArrayList();
+        classesAndInterfaces.add(c);
+        superclasses(c, classesAndInterfaces);
+        for (Iterator i = new ArrayList(classesAndInterfaces).iterator(); i.hasNext();)
+        {
+            Class x = (Class) i.next();
+            classesAndInterfaces.addAll(Arrays.asList(x.getInterfaces()));
+        }
+        return classesAndInterfaces;
+    }
+
+    private static void superclasses(Class c, Collection results)
+    {
+        if (c.getSuperclass() == null)
+        {
+            return;
+        }
+        results.add(c.getSuperclass());
+        superclasses(c.getSuperclass(), results);
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
new file mode 100644
index 0000000..b7a6ade
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
@@ -0,0 +1,4774 @@
+//
+//(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 com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.text.DecimalFormatSymbols;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Vector;
+
+/**
+ * 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>&lt;space&gt;<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 final class PrintfFormat
+{
+    /** 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;
+
+    /**
+     * 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
+    {
+        /** Default precision. */
+        private final static int defaultDigits = 6;
+
+        /**
+         * 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;
+
+        /**
+         * 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;
+
+        /**
+         * 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.
+         * @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;
+                }
+            }
+        }
+    }
+}
diff --git a/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
new file mode 100644
index 0000000..aae577e
--- /dev/null
+++ b/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
@@ -0,0 +1,486 @@
+package com.ximple.eofms.util;
+
+/**
+ * TWDDatumConverter
+ * User: Ulysses
+ * Date: 2007/10/8
+ * Time: �U�� 01:35:03
+ * To change this template use File | Settings | File Templates.
+ */
+public class TWDDatumConverter
+{
+    /*
+     *   Definition of math related value
+     */
+    private static final double COS67_5 = 0.3826834323650897717284599840304e0;
+    private static final double PI      = 3.14159265358979323e0;
+    private static final double HALF_PI = 1.570796326794896615e0;
+    private static final double DEG_RAD = 0.01745329251994329572e0;
+    private static final double RAD_DEG = 57.295779513082321031e0;
+
+    /*
+     * Definition of datum related value
+     */
+    private static final double AD_C       = 1.0026000e0;
+    private static final double TWD67_A    = 6378160.0e0;
+    private static final double TWD67_B    = 6356774.7192e0;
+    private static final double TWD67_ECC  = 0.00669454185458e0;
+    private static final double TWD67_ECC2 = 0.00673966079586e0;
+
+    // different from garmin and already knowned value, but those all value only
+    private static final double TWD67_DX = -752.32e0;
+
+    // got 5-15m accuracy. the real offical value is holded by somebody and not
+    private static final double TWD67_DY = -361.32e0;
+
+    // release to public. if can got more enough twd67/twd97 control point coordinare,
+    private static final double TWD67_DZ   = -180.51e0;
+    private static final double TWD67_RX   = -0.00000117e0;    // then we can calculate a better value than now.
+    private static final double TWD67_RY   = 0.00000184e0;     //
+    private static final double TWD67_RZ   = 0.00000098e0;     // and, also lack twd67/twd97 altitude convertion value...
+    private static final double TWD67_S    = 0.00002329e0;     //
+    private static final double TWD97_A    = 6378137.0e0;
+    private static final double TWD97_B    = 6356752.3141e0;
+    private static final double TWD97_ECC  = 0.00669438002290e0;
+    private static final double TWD97_ECC2 = 0.00673949677556e0;
+    private static final double TWD67_TM2  = 0.9999e0;         // TWD67->TM2 scale
+    private static final double TWD97_TM2  = 0.9999e0;         // TWD97->TM2 scale
+
+    /*
+     * datum convert function
+     */
+    void toTWD97(double x, double y, double z)
+    {
+        double newX, newY, newZ;
+        double r, pole, sin_lat, cos_lat;
+        double lat, lon, height;
+        double x1, y1, z1, x2, y2, z2;
+        double q, q2, t, t1, s, s1, sum, sin_b, cos_b, sin_p, cos_p;
+
+        lon    = x * DEG_RAD;
+        lat    = y * DEG_RAD;
+        height = z * DEG_RAD;
+
+        if ((lat < -HALF_PI) && (lat > -1.001 * HALF_PI))
+        {
+            lat = -HALF_PI;
+        } else if ((lat > HALF_PI) && (lat < 1.001 * HALF_PI))
+        {
+            lat = HALF_PI;
+        } else if ((lat < -HALF_PI) || (lat > HALF_PI))
+        {
+            return;
+        }
+
+        if (lon > PI)
+        {
+            lon -= (2 * PI);
+        }
+
+        sin_lat = Math.sin(lat);
+        cos_lat = Math.cos(lat);
+        r       = TWD67_A / (Math.sqrt(1.0 - TWD67_ECC * sin_lat * sin_lat));
+        x1      = (r + height) * cos_lat * Math.cos(lon);
+        y1      = (r + height) * cos_lat * Math.sin(lon);
+        z1      = ((r * (1 - TWD67_ECC)) + height) * sin_lat;
+        x2      = x1 + TWD67_DX + TWD67_S * (lon + TWD67_RZ * lat - TWD67_RY * height);
+        y2      = y1 + TWD67_DY + TWD67_S * (-TWD67_RZ * lon + lat + TWD67_RX * height);
+        z2      = z1 + TWD67_DZ + TWD67_S * (TWD67_RY * lon - TWD67_RX * lat + height);
+        pole    = 0.0;
+
+        if (x2 != 0.0)
+        {
+            lon = Math.atan2(y2, x2);
+        } else
+        {
+            if (y2 > 0)
+            {
+                lon = HALF_PI;
+            } else if (y2 < 0)
+            {
+                lon = -HALF_PI;
+            } else
+            {
+                pole = 1;
+                lon  = 0;
+
+                if (z2 > 0)
+                {
+                    lat = HALF_PI;
+                } else if (z2 < 0)
+                {
+                    lat = -HALF_PI;
+                } else
+                {
+                    lat  = HALF_PI;
+                    newX = lon * RAD_DEG;
+                    newY = lat * RAD_DEG;
+                    newZ = -TWD97_B;
+
+                    return;
+                }
+            }
+        }
+
+        q2    = x2 * x2 + y2 * y2;
+        q     = Math.sqrt(q2);
+        t     = z2 * AD_C;
+        s     = Math.sqrt(t * t + q2);
+        sin_b = t / s;
+        cos_b = q / s;
+        t1    = z2 + TWD97_B * TWD97_ECC2 * sin_b * sin_b * sin_b;
+        sum   = q - TWD97_A * TWD97_ECC * cos_b * cos_b * cos_b;
+        s1    = Math.sqrt(t1 * t1 + sum * sum);
+        sin_p = t1 / s1;
+        cos_p = sum / s1;
+        r     = TWD97_A / Math.sqrt(1.0 - TWD97_ECC * sin_p * sin_p);
+
+        if (cos_p >= COS67_5)
+        {
+            height = q / cos_p - r;
+        } else if (cos_p <= -COS67_5)
+        {
+            height = q / -cos_p - r;
+        } else
+        {
+            height = z2 / sin_p + r * (TWD97_ECC - 1.0);
+        }
+
+        if (pole !=0.0)
+        {
+            lat = Math.atan(sin_p / cos_p);
+        }
+
+        newX = lon * RAD_DEG;
+        newY = lat * RAD_DEG;
+        newZ = height;
+    }
+
+    void toTWD67(double x, double y, double z)
+    {
+        double newX, newY, newZ;
+        double r, pole, sin_lat, cos_lat;
+        double lat, lon, height;
+        double x1, y1, z1, x2, y2, z2;
+        double q, q2, t, t1, s, s1, sum, sin_b, cos_b, sin_p, cos_p;
+
+        lon    = x * DEG_RAD;
+        lat    = y * DEG_RAD;
+        height = z * DEG_RAD;
+
+        if ((lat < -HALF_PI) && (lat > -1.001 * HALF_PI))
+        {
+            lat = -HALF_PI;
+        } else if ((lat > HALF_PI) && (lat < 1.001 * HALF_PI))
+        {
+            lat = HALF_PI;
+        } else if ((lat < -HALF_PI) || (lat > HALF_PI))
+        {
+            return;
+        }
+
+        if (lon > PI)
+        {
+            lon -= (2 * PI);
+        }
+
+        sin_lat = Math.sin(lat);
+        cos_lat = Math.cos(lat);
+        r       = TWD97_A / (Math.sqrt(1.0 - TWD97_ECC * sin_lat * sin_lat));
+        x1      = (r + height) * cos_lat * Math.cos(lon);
+        y1      = (r + height) * cos_lat * Math.sin(lon);
+        z1      = ((r * (1 - TWD97_ECC)) + height) * sin_lat;
+        x2      = x1 - TWD67_DX - TWD67_S * (lon + TWD67_RZ * lat - TWD67_RY * height);
+        y2      = y1 - TWD67_DY - TWD67_S * (-TWD67_RZ * lon + lat + TWD67_RX * height);
+        z2      = z1 - TWD67_DZ - TWD67_S * (TWD67_RY * lon - TWD67_RX * lat + height);
+        pole    = 0;
+
+        if (x2 != 0.0)
+        {
+            lon = Math.atan2(y2, x2);
+        } else
+        {
+            if (y2 > 0)
+            {
+                lon = HALF_PI;
+            } else if (y2 < 0)
+            {
+                lon = -HALF_PI;
+            } else
+            {
+                pole = 1;
+                lon  = 0;
+
+                if (z2 > 0)
+                {
+                    lat = HALF_PI;
+                } else if (z2 < 0)
+                {
+                    lat = -HALF_PI;
+                } else
+                {
+                    lat  = HALF_PI;
+                    newX = lon * RAD_DEG;
+                    newY = lat * RAD_DEG;
+                    newZ = -TWD67_B;
+
+                    return;
+                }
+            }
+        }
+
+        q2    = x2 * x2 + y2 * y2;
+        q     = Math.sqrt(q2);
+        t     = z2 * AD_C;
+        s     = Math.sqrt(t * t + q2);
+        sin_b = t / s;
+        cos_b = q / s;
+        t1    = z2 + TWD67_B * TWD67_ECC2 * sin_b * sin_b * sin_b;
+        sum   = q - TWD67_A * TWD67_ECC * cos_b * cos_b * cos_b;
+        s1    = Math.sqrt(t1 * t1 + sum * sum);
+        sin_p = t1 / s1;
+        cos_p = sum / s1;
+        r     = TWD67_A / Math.sqrt(1.0 - TWD67_ECC * sin_p * sin_p);
+
+        if (cos_p >= COS67_5)
+        {
+            height = q / cos_p - r;
+        } else if (cos_p <= -COS67_5)
+        {
+            height = q / -cos_p - r;
+        } else
+        {
+            height = z2 / sin_p + r * (TWD67_ECC - 1.0);
+        }
+
+        if (pole != 0.0)
+        {
+            lat = Math.atan(sin_p / cos_p);
+        }
+
+        newX = lon * RAD_DEG;
+        newY = lat * RAD_DEG;
+        newZ = height;
+    }
+
+    void toTM2(double a, double ecc, double ecc2, double lat, double lon, double scale, double x, double y)
+    {
+        double x0, y0, x1, y1, m0, m1;
+        double n, t, c, A;
+        double newX, newY;
+
+        x0   = x * DEG_RAD;
+        y0   = y * DEG_RAD;
+        x1   = lon * DEG_RAD;
+        y1   = lat * DEG_RAD;
+        m0   = mercator(y1, a, ecc);
+        m1   = mercator(y0, a, ecc);
+        n    = a / Math.sqrt(1 - ecc * Math.pow(Math.sin(y0), 2.0));
+        t    = Math.pow(Math.tan(y0), 2.0);
+        c    = ecc2 * Math.pow(Math.cos(y0), 2.0);
+        A    = (x0 - x1) * Math.cos(y0);
+        newX = scale * n
+               * (A + (1.0 - t + c) * A * A * A / 6.0
+                  + (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ecc2) * Math.pow(A, 5.0) / 120.0);
+        newY = scale
+               * (m1 - m0
+                  + n * Math.tan(y0)
+                    * (A * A / 2.0 + (5.0 - t + 9.0 * c + 4 * c * c) * Math.pow(A, 4.0) / 24.0
+                       + (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ecc2) * Math.pow(A, 6.0) / 720.0));
+    }
+
+    void fromTM2(double a, double ecc, double ecc2, double lat, double lon, double scale, double x, double y)
+    {
+        double newX, newY;
+        double x0, y0, x1, y1, phi, m, m0, mu, e1;
+        double c1, t1, n1, r1, d;
+
+        x0  = x;
+        y0  = y;
+        x1  = lon * DEG_RAD;
+        y1  = lat * DEG_RAD;
+        m0  = mercator(y1, a, ecc);
+        m   = m0 + y0 / scale;
+        e1  = (1.0 - Math.sqrt(1.0 - ecc)) / (1.0 + Math.sqrt(1.0 - ecc));
+        mu  = m / (a * (1.0 - ecc / 4.0 - 3.0 * ecc * ecc / 64.0 - 5.0 * ecc * ecc * ecc / 256.0));
+        phi = mu + (3.0 * e1 / 2.0 - 27.0 * Math.pow(e1, 3.0) / 32.0) * Math.sin(2.0 * mu)
+              + (21.0 * e1 * e1 / 16.0 - 55.0 * Math.pow(e1, 4.0) / 32.0) * Math.sin(4.0 * mu)
+              + 151.0 * Math.pow(e1, 3.0) / 96.0 * Math.sin(6.0 * mu) + 1097.0 * Math.pow(e1, 4.0) / 512.0 * Math.sin(8.0 * mu);
+        c1   = ecc2 * Math.pow(Math.cos(phi), 2.0);
+        t1   = Math.pow(Math.tan(phi), 2.0);
+        n1   = a / Math.sqrt(1 - ecc * Math.pow(Math.sin(phi), 2.0));
+        r1   = a * (1.0 - ecc) / Math.pow(1.0 - ecc * Math.pow(Math.sin(phi), 2.0), 1.5);
+        d    = x0 / (n1 * scale);
+        newX = (x1 + (d - (1.0 + 2.0 * t1 + c1) * Math.pow(d, 3.0) / 6.0
+                      + (5.0 - 2.0 * c1 + 28.0 * t1 - 3.0 * c1 * c1 + 8.0 * ecc2 + 24.0 * t1 * t1) * Math.pow(d, 5.0)
+                        / 120.0) / Math.cos(phi)) * RAD_DEG;
+        newY = (phi
+                - n1 * Math.tan(phi) / r1
+                  * (d * d / 2.0 - (5.0 + 3.0 * t1 + 10.0 * c1 - 4.0 * c1 * c1 - 9.0 * ecc2) * Math.pow(d, 4.0) / 24.0
+                     + (61.0 + 90.0 * t1 + 298.0 * c1 + 45.0 * t1 * t1 - 252.0 * ecc2 - 3.0 * c1 * c1) * Math.pow(d, 6.0)
+                       / 72.0)) * RAD_DEG;
+    }
+
+    double mercator(double y, double a, double ecc)
+    {
+        if (y == 0.0)
+        {
+            return 0.0;
+        } else
+        {
+            return a * ((1.0 - ecc / 4.0 - 3.0 * ecc * ecc / 64.0 - 5.0 * ecc * ecc * ecc / 256.0) * y
+                        - (3.0 * ecc / 8.0 + 3.0 * ecc * ecc / 32.0 + 45.0 * ecc * ecc * ecc / 1024.0) * Math.sin(2.0 * y)
+                        + (15.0 * ecc * ecc / 256.0 + 45.0 * ecc * ecc * ecc / 1024.0) * Math.sin(4.0 * y)
+                        - (35.0 * ecc * ecc * ecc / 3072.0) * Math.sin(6.0 * y));
+        }
+    }
+
+    /**
+     *
+     *  Sample code below, using coordinate in Dan Jacob's website.
+     *
+     *
+     * int main()
+     * {
+     *   double x1, y1, z1, x2, y2, z2;
+     *   double tx1, ty1, tx2, ty2;
+     *   double dx, dy, dz, dx1, dy1;
+     *
+     *   x1 = 120.85788004;          // TWD67
+     *   y1 = 24.18347242;
+     *   z1 = 777;
+     *
+     *   x2 = 120.86603958;          // TWD97
+     *   y2 = 24.18170479;
+     *   z2 = 777;
+     *
+     *   tx1 = 235561;               // TWD67->TM2
+     *   ty1 = 2675359;
+     *
+     *   tx2 = 236389.849;           // TWD97->TM2
+     *   ty2 = 2675153.168;
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD67->TM2
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x1;
+     *   dy = y1;
+     *
+     *   toTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx, &dy);
+     *   // center longitude of taiwan is 121, for penghu is 119
+     *
+     *   dx += 250000;   // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD67->TM2nTWD67   (%f, %f)nConvert (%.3f, %.3f)nOrigin  (%.3f, %.3f)n", x1, y1, dx, dy, tx1, ty1);
+     *   printf("Acuuracy (%.3f, X:%.3f, Y:%.3f)nn", sqrt((dx-tx1)*(dx-tx1)+(dy-ty1)*(dy-ty1)), (dx-tx1), (dy-ty1));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD97->TM2
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x2;
+     *   dy = y2;
+     *
+     *   toTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx, &dy);
+     *   // center longitude of taiwan is 121, for penghu is 119
+     *
+     *   dx += 250000;   // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD97->TM2nTWD97   (%f, %f)nConvert (%.3f, %.3f)nOrigin  (%.3f, %.3f)n", x2, y2, dx, dy, tx2, ty2);
+     *   printf("Acuuracy (%.3f, X:%.3f, Y:%.3f)nn", sqrt((dx-tx2)*(dx-tx2)+(dy-ty2)*(dy-ty2)), (dx-tx2), (dy-ty2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TM2->TWD67
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = tx1-250000;    // should minus 250000 first in Taiwan
+     *   dy = ty1;
+     *
+     *   fromTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx, &dy);
+     *
+     *   printf("TM2->TWD67nTM2     (%f, %f)nConvert (%.9f, %.9f)nOrigin  (%.9f, %.9f)n", tx1, ty1, dx, dy, x1, y1);
+     *   printf("Acuuracy (%.9f, X:%.9f, Y:%.9f)nn", sqrt((dx-x1)*(dx-x1)+(dy-y1)*(dy-y1)), (dx-x1), (dy-y1));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TM2->TWD97
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = tx2-250000;    // should minus 250000 first in Taiwan
+     *   dy = ty2;
+     *
+     *   fromTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx, &dy);
+     *
+     *   printf("TM2->TWD97nTM2     (%f, %f)nConvert (%.9f, %.9f)nOrigin  (%.9f, %.9f)n", tx2, ty2, dx, dy, x2, y2);
+     *   printf("Acuuracy (%.9f, X:%.9f, Y:%.9f)nn", sqrt((dx-x2)*(dx-x2)+(dy-y2)*(dy-y2)), (dx-x2), (dy-y2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD67->TWD97
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x1;
+     *   dy = y1;
+     *   dz = z1;
+     *
+     *   toTWD97(&dx, &dy, &dz);
+     *
+     *   dx1 = dx;
+     *   dy1 = dy;
+     *
+     *   toTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx1, &dy1);
+     *
+     *   dx1 += 250000;  // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD67->TWD97nTWD67   (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x1, y1, z1, tx1, ty1);
+     *   printf("Convert (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", dx, dy, dz, dx1, dy1);
+     *   printf("Origin  (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x2, y2, z2, tx2, ty2);
+     *   printf("Acuuracy (%.4f, X:%.4f, Y:%.4f)nn", sqrt((dx1-tx2)*(dx1-tx2)+(dy1-ty2)*(dy1-ty2)), (dx1-tx2), (dy1-ty2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD97->TWD67
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x2;
+     *   dy = y2;
+     *   dz = z2;
+     *
+     *   toTWD67(&dx, &dy, &dz);
+     *
+     *   dx1 = dx;
+     *   dy1 = dy;
+     *
+     *   toTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx1, &dy1);
+     *
+     *   dx1 += 250000;  // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD97->TWD67nTWD97   (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x2, y2, z2, tx2, ty2);
+     *   printf("Convert (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", dx, dy, dz, dx1, dy1);
+     *   printf("Origin  (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x1, y1, z1, tx1, ty1);
+     *   printf("Acuuracy (%.4f, X:%.4f, Y:%.4f)nn", sqrt((dx1-tx1)*(dx1-tx1)+(dy1-ty1)*(dy1-ty1)), (dx1-tx1), (dy1-ty1));
+     * }
+     */
+}

--
Gitblit v0.0.0-SNAPSHOT