From 3a206c076a4b2085aa0a54e4c681041cf1784e63 Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Tue, 29 Apr 2008 11:27:11 +0800
Subject: [PATCH] tag version-0.2

---
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java            |  150 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java                  |   39 
 xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java              |   69 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn                |    0 
 .gitattributes                                                                                           |   75 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java                                      |  272 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java                                 |  323 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java                    |  490 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java                                 |  114 
 xdgnjobs/ximple-spatialjob/pom.xml                                                                       |   69 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java                                      |  238 
 xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml    |   32 
 xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java                         |   95 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java                                 |   56 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java              |   16 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java                         |  363 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java                             |  104 
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml                          |   17 
 xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java                     |   19 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java                                 |  162 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn                         |    0 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java                 |  132 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java                                  |  534 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java                            |   31 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java              |  248 
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml                           |  720 ++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java              |  405 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java                                  |  296 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java |   26 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java      |   75 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java           |  101 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java             |  294 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java                                   |  166 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java          |   12 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java                                         |  263 
 xdgnjobs/ximple-jobcarrier/pom.xml                                                                       |  137 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java                                    | 4774 ++++++++++++++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java                              |  291 
 xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties                                           |   15 
 xdgnjobs/ximple-dgnio/pom.xml                                                                            |   70 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java                              |   23 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn                      |    0 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java                            |   34 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java                          |   71 
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml         |   86 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java                    |  180 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java         |   75 
 xdgnjobs/pom.xml                                                                                         |  750 ++
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java                           |  119 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java                            |   18 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java                                |   45 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java              |   48 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java                             |  246 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java                               |  718 ++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java                               |   58 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java                             |   30 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java                               |   16 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java                                  |  129 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java                         |  125 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java             |   58 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn                           |    0 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java                                   |   90 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties                                          |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java          |   15 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java                          |  247 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java           |   72 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java  |   96 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn                      |    0 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java               |   48 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml                                            |   77 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java                         | 4774 ++++++++++++++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java                            |  166 
 xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml            |   74 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java                  |   97 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java                          |  203 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java       |   89 
 76 files changed, 20,198 insertions(+), 0 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 8e948c5..672505c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,76 @@
 * text=auto !eol
+xdgnjobs/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-dgnio/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn -text
+xdgnjobs/ximple-jobcarrier/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml svneol=native#text/xml
+xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml svneol=native#text/xml
diff --git a/xdgnjobs/pom.xml b/xdgnjobs/pom.xml
new file mode 100644
index 0000000..8dc352f
--- /dev/null
+++ b/xdgnjobs/pom.xml
@@ -0,0 +1,750 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- =======================================================================
+  Maven Project Configuration File
+  
+  The Ximple DgnIO Project
+  http://www.ximple.com.tw/
+  
+  Version: $Id$
+  ======================================================================= -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+  http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <properties>
+    <oracle.jdbc>true</oracle.jdbc>
+    <test.maxHeapSize>512M</test.maxHeapSize>
+    <src.output>${basedir}/target</src.output>
+    <java5>1.5</java5>
+    <xdgnio.version>0.0.2</xdgnio.version>
+    <gt.version>2.4.1</gt.version>
+    <failIfNoTests>false</failIfNoTests>
+  </properties>
+
+  <profiles>
+    <profile>
+      <id>java5</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <source>1.5</source>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+    <profile>
+      <id>site.ximple.tw</id>
+      <distributionManagement>
+        <site>
+          <id>artifactorysite-ximple-tw</id>
+          <name>Artifactory Web site for Maven reports</name>
+          <url>scp://www.ximple.com.tw/home/www/artfactory/libs-releases@repo</url>
+        </site>
+      </distributionManagement>
+    </profile>
+  </profiles>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/</url>
+  </scm>
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-dgnjobs</artifactId>
+  <packaging>pom</packaging>
+  <version>0.0.2</version>
+  <name>ximple-dgnjobs</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <description>Ximple Spatial Data Job for Quartz</description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <!-- =========================================================== -->
+  <!--     Issue managements and mailing lists.                    -->
+  <!-- =========================================================== -->
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://www.ximple.com.tw/jira/browse/EOFMS</url>
+  </issueManagement>
+
+  <!-- =========================================================== -->
+  <!--    Continuous Integration                                   -->
+  <!-- =========================================================== -->
+  <ciManagement>
+    <system>continuum</system>
+  </ciManagement>
+
+  <mailingLists>
+    <mailingList>
+    </mailingList>
+  </mailingLists>
+
+  <developers>
+    <developer>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependency Management                                   -->
+  <!--     If a POM declares one of those dependencies, then it    -->
+  <!--     will use the version specified here. Otherwise, those   -->
+  <!--     dependencies are ignored.                               -->
+  <!-- =========================================================== -->
+  <dependencyManagement>
+    <dependencies>
+      <!-- GeoAPI and its dependencies -->
+      <dependency>
+        <groupId>org.opengis</groupId>
+        <artifactId>geoapi-nogenerics</artifactId>
+        <version>2.1.0</version>
+      </dependency>
+      <dependency>
+        <groupId>javax.units</groupId>
+        <artifactId>jsr108</artifactId>
+        <version>0.01</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.vividsolutions</groupId>
+        <artifactId>jts</artifactId>
+        <version>1.9</version>
+      </dependency>
+
+      <!-- Apache -->
+      <!--
+      <dependency>
+        <groupId>commons-beanutils</groupId>
+        <artifactId>commons-beanutils</artifactId>
+        <version>1.7</version>
+      </dependency>
+      -->
+      <dependency>
+        <groupId>commons-collections</groupId>
+        <artifactId>commons-collections</artifactId>
+        <version>3.2</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-digester</groupId>
+        <artifactId>commons-digester</artifactId>
+        <version>1.8</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-pool</groupId>
+        <artifactId>commons-pool</artifactId>
+        <version>1.3</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-logging</groupId>
+        <artifactId>commons-logging</artifactId>
+        <version>1.1.1</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-transaction</groupId>
+        <artifactId>commons-transaction</artifactId>
+        <version>1.2</version>
+      </dependency>
+      <dependency>
+        <groupId>log4j</groupId>
+        <artifactId>log4j</artifactId>
+        <version>1.2.15</version>
+        <!-- Same as the dependency in commons-logging -->
+      </dependency>
+      <dependency>
+        <groupId>org.apache.poi</groupId>
+        <artifactId>poi-contrib</artifactId>
+        <version>3.0.1-FINAL</version>
+      </dependency>
+
+      <!-- geotools -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-api</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-main</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-shapefile</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-sample-data</artifactId>
+        <version>${gt.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-data</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+
+      <!-- because main and sample-data depend on referencing we need a tie breaker -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-referencing</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>jdom</groupId>
+        <artifactId>jdom</artifactId>
+        <version>1.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>velocity</groupId>
+        <artifactId>velocity</artifactId>
+        <version>1.4</version>
+      </dependency>
+
+      <!-- We need this to make the referencing module useful -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-epsg-hsql</artifactId>
+        <version>${gt.version}</version>
+        <scope>test</scope>
+      </dependency>
+
+      <!-- ORACLE -->
+      <!-- Download and install into your own repo -->
+      <dependency>
+        <artifactId>ojdbc5</artifactId>
+        <groupId>com.oracle</groupId>
+        <version>11.1.0</version>
+      </dependency>
+
+      <!-- opensymphony -->
+      <dependency>
+        <groupId>opensymphony</groupId>
+        <artifactId>quartz</artifactId>
+        <version>1.6.0</version>
+      </dependency>
+
+      <!-- Ximple Library -->
+      <dependency>
+        <artifactId>ximple-dgnio</artifactId>
+        <groupId>com.ximple</groupId>
+        <version>${xdgnio.version}</version>
+      </dependency>
+
+      <!-- Tests or legacy -->
+      <dependency>
+        <groupId>org.testng</groupId>
+        <artifactId>testng</artifactId>
+        <version>5.7</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>geoapi-nogenerics</artifactId>
+      <groupId>org.opengis</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>jsr108</artifactId>
+      <groupId>javax.units</groupId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.vividsolutions</groupId>
+      <artifactId>jts</artifactId>
+    </dependency>
+
+    <!-- Apache -->
+    <dependency>
+      <artifactId>commons-collections</artifactId>
+      <groupId>commons-collections</groupId>
+    </dependency>
+    <dependency>
+      <groupId>commons-digester</groupId>
+      <artifactId>commons-digester</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-pool</groupId>
+      <artifactId>commons-pool</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-transaction</groupId>
+      <artifactId>commons-transaction</artifactId>
+    </dependency>
+    <dependency>
+      <artifactId>log4j</artifactId>
+      <groupId>log4j</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>gt2-api</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-main</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-shapefile</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-sample-data</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-data</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+
+    <!-- because main and sample-data depend on referencing we need a tie breaker -->
+    <dependency>
+      <artifactId>gt2-referencing</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>jdom</artifactId>
+      <groupId>jdom</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>velocity</artifactId>
+      <groupId>velocity</groupId>
+    </dependency>
+
+    <!-- We need this to make the referencing module useful -->
+    <dependency>
+      <artifactId>gt2-epsg-hsql</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- ORACLE -->
+    <dependency>
+      <artifactId>ojdbc5</artifactId>
+      <groupId>com.oracle</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>testng</artifactId>
+      <groupId>org.testng</groupId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <!-- =========================================================== -->
+  <!--     Build Configuration                                     -->
+  <!-- =========================================================== -->
+  <build>
+    <!-- ========================================================= -->
+    <!--   Maven plugins dependencies management.                  -->
+    <!--   It should not be needed since Maven select by default   -->
+    <!--   the latest plugins. Unfortunatly, experience shows that -->
+    <!--   new plugin releases sometime introduce new bugs that    -->
+    <!--   break our build. So it is saferto specify plugin        -->
+    <!--   versions that are known to work.  This list is in       -->
+    <!--   alphabetical order for easier comparaison with latest   -->
+    <!--   plugins at                                              -->
+    <!--   http://www.ibiblio.org/maven2/org/apache/maven/plugins/ -->
+    <!-- ========================================================= -->
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <version>2.1</version>
+          <configuration>
+            <descriptors>
+              <descriptor>build/maven/assembly/binaryDist.xml</descriptor>
+              <descriptor>build/maven/assembly/sourceDist.xml</descriptor>
+            </descriptors>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clean-plugin</artifactId>
+          <version>2.1.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clover-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>2.0.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-install-plugin</artifactId>
+          <version>2.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jar-plugin</artifactId>
+          <version>2.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-plugin-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-project-info-reports-plugin</artifactId>
+          <version>2.0.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-resources-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-site-plugin</artifactId>
+          <version>2.0-beta-5</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>2.4.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-report-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+
+        <!-- http://www.ibiblio.org/maven2/org/codehaus/mojo/ -->
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>changelog-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>changes-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>jxr-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>taglist-maven-plugin</artifactId>
+          <version>2.0</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>jalopy-maven-plugin</artifactId>
+          <version>1.0-SNAPSHOT</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-source-plugin</artifactId>
+          <configuration>
+            <outputDirectory>${src.output}</outputDirectory>
+            <attach>false</attach>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-eclipse-plugin</artifactId>
+          <version>2.4</version>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+    <!-- http://www.ibiblio.org/maven2/org/apache/maven/wagon/ -->
+    <extensions>
+      <extension>
+        <groupId>org.apache.maven.wagon</groupId>
+        <artifactId>wagon-webdav</artifactId>
+        <version>1.0-beta-2</version>
+      </extension>
+    </extensions>
+
+    <plugins>
+      <!-- ======================================================= -->
+      <!--     Source reformat                                     -->
+      <!--       (activated only on request, jalopy:format)        -->
+      <!--     See developer's guide for automated activation      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>jalopy-maven-plugin</artifactId>
+        <configuration>
+          <convention>gt2/jalopygeotools.xml</convention>
+          <failOnError>false</failOnError>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.geotools.maven</groupId>
+            <artifactId>gt2-build-configs</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Compilation.                                        -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <!-- The -source argument for the Java compiler. -->
+          <target>1.5</target>
+          <!-- The -target argument for the Java compiler. -->
+          <debug>true</debug>
+          <!-- Whether to include debugging information.   -->
+          <encoding>ISO-8859-1</encoding>
+          <!-- The -encoding argument for the Java compiler. -->
+        </configuration>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Tests.                                              -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>**/*Test.java</include>
+          </includes>
+          <excludes>
+            <exclude>${online.skip.pattern}</exclude>
+            <exclude>${stress.skip.pattern}</exclude>
+          </excludes>
+          <argLine>-Xmx${test.maxHeapSize} -Djava.awt.headless=${java.awt.headless}
+          </argLine>
+          <!-- Ignores test failure only if we are generating a       -->
+          <!-- report for publication on the web site. See the        -->
+          <!-- profiles section at the begining of this pom.xml file. -->
+          <testFailureIgnore>
+            ${allow.test.failure.ignore}
+          </testFailureIgnore>
+
+          <!-- Option to print summary of test suites or just print the test cases that has errors. -->
+          <printSummary>true</printSummary>
+          <!-- Redirect the unit test standard output to a file. -->
+          <redirectTestOutputToFile>false</redirectTestOutputToFile>
+        </configuration>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Code coverage                                       -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-clover-plugin</artifactId>
+        <configuration>
+          <jdk>1.5</jdk>
+          <licenseLocation>
+            http://svn.geotools.org/geotools/branches/2.4.x/build/maven/build-configs/src/main/resources/gt2/clover.license
+          </licenseLocation>
+          <flushPolicy>directed</flushPolicy>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>pre-site</phase>
+            <goals>
+              <goal>instrument</goal>
+              <!-- aggregation is disabled due to the bug:     -->
+              <!-- http://jira.codehaus.org/browse/MCLOVER-34  -->
+            </goals>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>org.geotools.maven</groupId>
+            <artifactId>gt2-build-configs</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     JAR packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <!-- ======================================================= -->
+      <!--     Source packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <inherited>true</inherited>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <configuration>
+          <attach>true</attach>
+        </configuration>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <resources>
+      <!--
+      <resource>
+        <targetPath>conf</targetPath>
+        <filtering>false</filtering>
+        <directory>${basedir}/src/main/resources/conf</directory>
+        <includes>
+          <include>configuration.xml</include>
+        </includes>
+        <excludes>
+          <exclude>**/*.properties</exclude>
+        </excludes>
+      </resource>
+        -->
+    </resources>
+  </build>
+
+  <distributionManagement>
+    <repository>
+      <uniqueVersion>false</uniqueVersion>
+      <id>ximple</id>
+      <name>Ximple - Artifactory lib repo</name>
+      <url>dav:http://www.ximple.com.tw/artifactory/libs-releases</url>
+    </repository>
+    <snapshotRepository>
+      <uniqueVersion>false</uniqueVersion>
+      <id>ximple-snapshots</id>
+      <name>Ximple - Artifactory lib-snapshots repo</name>
+      <url>dav:http://www.ximple.com.tw/artifactory/libs-snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <!-- =========================================================== -->
+  <!--     Repositories (ibiblio, refractions...).                 -->
+  <!--     This is where Maven looks for dependencies.             -->
+  <!-- =========================================================== -->
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <name>Ximple Artifactory Maven Repository Switchboard</name>
+      <url>http://www.ximple.com.tw/artifactory/repo</url>
+    </repository>
+
+    <repository>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <id>snapshots</id>
+      <name>Ximple Artifactory Maven Repository Switchboard</name>
+      <url>http://www.ximple.com.tw/artifactory/repo</url>
+    </repository>
+  </repositories>
+
+
+  <!-- =========================================================== -->
+  <!--     Plugin repositories.                                    -->
+  <!--     This is where Maven looks for plugin dependencies.      -->
+  <!-- =========================================================== -->
+  <pluginRepositories>
+    <pluginRepository>
+      <id>ximple-snapshots</id>
+      <name>ximple-shapshots</name>
+      <url>http://www.ximple.com.tw/artifactory/vplugins-snapshots</url>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+    </pluginRepository>
+    <pluginRepository>
+      <id>ximple</id>
+      <name>Ximple Maven 2 Repository</name>
+      <url>http://www.ximple.com.tw/artifactory/vplugins-releases</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <releases>
+        <enabled>true</enabled>
+      </releases>
+    </pluginRepository>
+  </pluginRepositories>
+
+  <!-- =========================================================== -->
+  <!--     Modules for the build in approximate dependency order   -->
+  <!-- =========================================================== -->
+  <modules>
+    <module>ximple-dgnio</module>
+    <module>ximple-spatialjob</module>
+    <module>ximple-jobcarrier</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-dgnio/pom.xml b/xdgnjobs/ximple-dgnio/pom.xml
new file mode 100644
index 0000000..e100146
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.0.2</version>
+  </parent>
+  
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-dgnio</artifactId>
+  <version>0.0.2</version>
+  <packaging>jar</packaging>
+  <name>ximple-dgnio-1.0.x</name>
+  <url>http://www.ximple.com.tw</url>
+  
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url>
+  </scm>
+
+  <description>
+    Ximple Dgn IO Library
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+  </dependencies>
+
+  <!-- =========================================================== -->
+  <!--     Build Configuration                                     -->
+  <!-- =========================================================== -->
+  <build>
+    <plugins>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java
new file mode 100644
index 0000000..0e0a507
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java
@@ -0,0 +1,166 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * ArcElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/26 �U�� 06:41:45
+ */
+public class ArcElement extends Element implements GeometryConverter
+{
+    public ArcElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public double getPrimary()
+    {
+        short[] primary = new short[4];
+
+        System.arraycopy(raw, 22, primary, 0, 4);
+
+        return Utility.DGNToIEEEDouble(primary) / 1000.0;
+    }
+
+    public void setPrimary(double value)
+    {
+        double  temp    = value * 1000.0;
+        short[] primary = Utility.IEEEDoubleToDGN(temp);
+
+        System.arraycopy(primary, 0, raw, 22, 4);
+    }
+
+    public double getSecondary()
+    {
+        short[] secondary = new short[4];
+
+        System.arraycopy(raw, 26, secondary, 0, 4);
+
+        return Utility.DGNToIEEEDouble(secondary) / 1000.0;
+    }
+
+    public void setSecondary(double value)
+    {
+        double  temp      = value * 1000.0;
+        short[] secondary = Utility.IEEEDoubleToDGN(temp);
+
+        System.arraycopy(secondary, 0, raw, 26, 4);
+    }
+
+    public Coordinate getOrigin()
+    {
+        short[] x = new short[4];
+
+        System.arraycopy(raw, 32, x, 0, 4);
+
+        double  dx = Utility.ConverUnitToCoord((int) Utility.DGNToIEEEDouble(x));
+        short[] y  = new short[4];
+
+        System.arraycopy(raw, 36, y, 0, 4);
+
+        double dy = Utility.ConverUnitToCoord((int) Utility.DGNToIEEEDouble(y));
+
+        return new Coordinate(dx, dy);
+    }
+
+    public void setOrigin(Coordinate value)
+    {
+        double  temp = Utility.ConverCoordToUnit(value.x);
+        short[] x    = Utility.IEEEDoubleToDGN(temp);
+
+        System.arraycopy(x, 0, raw, 32, 4);
+        temp = Utility.ConverCoordToUnit(value.y);
+
+        short[] y = Utility.IEEEDoubleToDGN(temp);
+
+        System.arraycopy(y, 0, raw, 36, 4);
+    }
+
+    public double getStartAngle()
+    {
+        int angle = (int) (raw[18] << 16 & 0xffff0000);
+
+        angle += raw[19] & 0x0000ffff;
+
+        return Utility.ConverIntToRotation(angle);
+    }
+
+    public void setStartAngle(double value)
+    {
+        int angle = Utility.ConverRotatioToInt(value);
+
+        raw[18] = (short) (angle >>> 16 & 0x0000ffff);
+        raw[19] = (short) (angle & 0x0000ffff);
+    }
+
+    public double getSweepAngle()
+    {
+        int angle = (int) (raw[20] << 16 & 0xffff0000);
+
+        angle += raw[21] & 0x0000ffff;
+
+        return Utility.ConverIntToRotation(angle);
+    }
+
+    public void setSweepAngle(double value)
+    {
+        int angle = Utility.ConverRotatioToInt(value);
+
+        raw[20] = (short) (angle >> 16 & 0x0000ffff);
+        raw[21] = (short) (angle & 0x0000ffff);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (int) (raw[30] << 16 & 0xffff0000);
+
+        rotation += raw[31] & 0x0000ffff;
+
+        return Utility.ConverIntToRotation(rotation);
+    }
+
+    public void setRotationAngle(double value)
+    {
+        int angle = Utility.ConverRotatioToInt(value);
+
+        raw[30] = (short) (angle >> 16 & 0x0000ffff);
+        raw[31] = (short) (angle & 0x0000ffff);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        return null;    // To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.ARC);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new ArcElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
new file mode 100644
index 0000000..c4dec54
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
@@ -0,0 +1,247 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * ComplexChainElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 03:44:56
+ */
+public class ComplexChainElement extends Element implements ComplexElement, GeometryConverter
+{
+    protected ArrayList list = new ArrayList();
+
+    public ComplexChainElement(short[] raw)
+    {
+        super(raw);
+        attrOffset = 4;
+    }
+
+    public int size()
+    {
+        return list.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return list.isEmpty();
+    }
+
+    public boolean contains(Object o)
+    {
+        return list.contains(o);
+    }
+
+    public Iterator iterator()
+    {
+        return list.iterator();
+    }
+
+    public Object[] toArray()
+    {
+        return list.toArray();
+    }
+
+    public boolean add(Object o)
+    {
+        return list.add(o);
+    }
+
+    public boolean remove(Object o)
+    {
+        return list.remove(o);
+    }
+
+    public boolean addAll(Collection c)
+    {
+        return list.addAll(c);
+    }
+
+    public boolean addAll(int index, Collection c)
+    {
+        return list.addAll(index, c);
+    }
+
+    public void clear()
+    {
+        list.clear();
+    }
+
+    public Object get(int index)
+    {
+        return list.get(index);
+    }
+
+    public Object set(int index, Object element)
+    {
+        return list.set(index, element);
+    }
+
+    public void add(int index, Object element)
+    {
+        list.add(index, element);
+    }
+
+    public Object remove(int index)
+    {
+        return list.remove(index);
+    }
+
+    public int indexOf(Object o)
+    {
+        return list.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o)
+    {
+        return list.lastIndexOf(o);
+    }
+
+    public ListIterator listIterator()
+    {
+        return list.listIterator();
+    }
+
+    public ListIterator listIterator(int index)
+    {
+        return list.listIterator(index);
+    }
+
+    public List subList(int fromIndex, int toIndex)
+    {
+        return list.subList(fromIndex, toIndex);
+    }
+
+    public boolean retainAll(Collection c)
+    {
+        return list.retainAll(c);
+    }
+
+    public boolean removeAll(Collection c)
+    {
+        return list.removeAll(c);
+    }
+
+    public boolean containsAll(Collection c)
+    {
+        return list.containsAll(c);
+    }
+
+    public Object[] toArray(Object[] a)
+    {
+        return list.toArray(a);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        ArrayList list = new ArrayList();
+
+        if (size() == 1)
+        {
+            Element element = (Element) get(0);
+
+            if (element instanceof LineStringElement)
+            {
+               if( ((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+               {
+                  return ((LineStringElement) element).toGeometry(factory);
+               }
+
+            } else if (element instanceof LineElement)
+            {
+               if( ((LineElement) element).getVertices().length  == 0 || ((LineElement) element).getVertices().length  > 1)
+               {
+                   return ((LineElement) element).toGeometry(factory);
+               }
+
+            } else
+            {
+                if (element instanceof GeometryConverter)
+                {
+                    return ((GeometryConverter) element).toGeometry(factory);
+                }
+
+                return null;
+            }
+        }
+
+        for (ListIterator it = listIterator(); it.hasNext(); )
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof LineStringElement)
+            {
+               if( ((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+               {
+                 list.add(((LineStringElement) element).toGeometry(factory));
+               }
+
+            } else if (element instanceof LineElement)
+            {
+
+               if( ((LineElement) element).getVertices().length  == 0 || ((LineElement) element).getVertices().length > 1)
+               {
+                list.add(((LineElement) element).toGeometry(factory));
+               }
+
+            }
+        }
+
+        Geometry[]         ga   = (Geometry[]) list.toArray(new Geometry[list.size()]);
+        GeometryCollection geos = new GeometryCollection(ga, factory);
+
+        return geos;
+    }
+
+    public double getElementSize()
+    {
+        return raw[18];
+    }
+
+    public boolean isClosed()
+    {
+        if (isEmpty())
+        {
+            return false;
+        }
+
+        return false;
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.COMPLEXCHAIN);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new ComplexChainElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java
new file mode 100644
index 0000000..08fa5ae
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java
@@ -0,0 +1,16 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.List;
+
+/**
+ * ComplexElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 04:17:37
+ */
+public interface ComplexElement extends List
+{
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
new file mode 100644
index 0000000..e4149f4
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
@@ -0,0 +1,203 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * ComplexShapeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 03:45:15
+ */
+public class ComplexShapeElement extends Element implements ComplexElement, GeometryConverter
+{
+    ArrayList list = new ArrayList();
+
+    public ComplexShapeElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public int size()
+    {
+        return list.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return list.isEmpty();
+    }
+
+    public boolean contains(Object o)
+    {
+        return list.contains(o);
+    }
+
+    public Iterator iterator()
+    {
+        return list.iterator();
+    }
+
+    public Object[] toArray()
+    {
+        return list.toArray();
+    }
+
+    public boolean add(Object o)
+    {
+        return list.add(o);
+    }
+
+    public boolean remove(Object o)
+    {
+        return list.remove(o);
+    }
+
+    public boolean addAll(Collection c)
+    {
+        return list.addAll(c);
+    }
+
+    public boolean addAll(int index, Collection c)
+    {
+        return list.addAll(index, c);
+    }
+
+    public void clear()
+    {
+        list.clear();
+    }
+
+    public Object get(int index)
+    {
+        return list.get(index);
+    }
+
+    public Object set(int index, Object element)
+    {
+        return list.set(index, element);
+    }
+
+    public void add(int index, Object element)
+    {
+        list.add(index, element);
+    }
+
+    public Object remove(int index)
+    {
+        return list.remove(index);
+    }
+
+    public int indexOf(Object o)
+    {
+        return list.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o)
+    {
+        return list.lastIndexOf(o);
+    }
+
+    public ListIterator listIterator()
+    {
+        return list.listIterator();
+    }
+
+    public ListIterator listIterator(int index)
+    {
+        return list.listIterator(index);
+    }
+
+    public List subList(int fromIndex, int toIndex)
+    {
+        return list.subList(fromIndex, toIndex);
+    }
+
+    public boolean retainAll(Collection c)
+    {
+        return list.retainAll(c);
+    }
+
+    public boolean removeAll(Collection c)
+    {
+        return list.removeAll(c);
+    }
+
+    public boolean containsAll(Collection c)
+    {
+        return list.containsAll(c);
+    }
+
+    public Object[] toArray(Object[] a)
+    {
+        return list.toArray(a);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        ArrayList list = new ArrayList();
+
+        for (ListIterator it = listIterator(); it.hasNext(); )
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof LineStringElement)
+            {
+               if( ((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+               {
+                 list.add(((LineStringElement) element).toGeometry(factory));
+               }
+
+
+            } else if (element instanceof LineElement)
+            {
+
+               if( ((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1 )
+               {
+                 list.add(((LineElement) element).toGeometry(factory));
+               }
+
+            }
+        }
+
+        Geometry[]         ga   = (Geometry[]) list.toArray(new Geometry[list.size()]);
+        GeometryCollection geos = new GeometryCollection(ga, factory);
+
+        return geos;
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.COMPLEXSHAPE);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new ComplexShapeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java
new file mode 100644
index 0000000..f5936c7
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java
@@ -0,0 +1,246 @@
+package com.ximple.io.dgn7;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import oracle.jdbc.OracleConnection;
+import oracle.sql.BLOB;
+
+/**
+ * Dgn7OracleReader
+ * User: Ulysses
+ * Date: 2007/10/24
+ * Time: �U�� 01:01:08
+ */
+public class Dgn7OracleReader implements Iterator<Element>
+{
+    private final static Logger logger = Logger.getLogger(Dgn7OracleReader.class);
+
+    private String _sql;
+    private String _fieldName;
+    private Connection _connection;
+    private ResultSet _resultSet;
+    private static final int FETCHSIZE = 20;
+    private Element _element;
+
+    public Dgn7OracleReader(String sql, String fieldName, OracleConnection connection)
+    {
+        this._sql = sql;
+        this._fieldName = fieldName;
+        this._connection = connection;
+    }
+
+    public String getSql()
+    {
+        return _sql;
+    }
+
+    public void setSql(String sql)
+    {
+        this._sql = sql;
+    }
+
+    public String getFieldName()
+    {
+        return _fieldName;
+    }
+
+    public void setFieldName(String fieldName)
+    {
+        this._fieldName = fieldName;
+    }
+
+    public boolean hasNext()
+    {
+        if (_resultSet == null)
+        {
+            try
+            {
+                initializeReader();
+            } catch (SQLException e)
+            {
+                throw new RuntimeException("initialize oralce error.", e);
+            } catch (Dgn7fileException e)
+            {
+                throw new RuntimeException("initialize oralce error.", e);
+            }
+        }
+        if (_element == null)
+        {
+            return false;
+        }
+        return true;
+    }
+
+    public Element next()
+    {
+        Element result = _element;
+
+        try
+        {
+            fetchElement();
+        } catch (SQLException e)
+        {
+            throw new RuntimeException("Error:" + e.getMessage(), e);
+        } catch (Dgn7fileException e)
+        {
+            throw new RuntimeException("Error:" + e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    public void remove()
+    {
+        throw new RuntimeException("Not Support this method.");
+    }
+
+    private boolean initializeReader() throws SQLException, Dgn7fileException
+    {
+        if (_resultSet != null) return true;
+        Statement stmtSrc = _connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmtSrc.setFetchSize(FETCHSIZE);
+        _resultSet = stmtSrc.executeQuery(_sql);
+
+        fetchElement();
+
+        return true;
+    }
+
+    private boolean fetchElement() throws SQLException, Dgn7fileException
+    {
+        if (_resultSet.next())
+        {
+            byte[] raw = null;
+            Object value = _resultSet.getObject(this._fieldName);
+
+            if (value instanceof BLOB)
+            {
+                BLOB blob = (BLOB) value;
+
+                try
+                {
+                    raw = getBytesFromBLOB(blob);
+                } catch (IOException e)
+                {
+                    throw new SQLException("IOError", e);
+                }
+                blob.close();
+            } else if (value instanceof byte[])
+            {
+                raw = (byte[]) value;
+            }
+            if (raw == null)
+            {
+                _element = null;
+                return false;
+            }
+
+            ByteBuffer buffer = ByteBuffer.wrap(raw);
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
+            short signature = buffer.getShort();
+
+            // byte type = (byte) (buffer.get() & 0x7f);
+            byte type = (byte) ((signature >>> 8) & 0x007f);
+
+            // silly Bentley say contentLength is in 2-byte words
+            // and ByteByffer uses bytes.
+            // track the record location
+            int elementLength = (buffer.getShort() * 2) + 4;
+            ElementType recordType = ElementType.forID(type);
+            IElementHandler handler = recordType.getElementHandler();
+            _element = (Element) handler.read(buffer, signature, elementLength);
+            if (recordType.isComplexElement() && (elementLength < raw.length))
+            {
+                int offset = elementLength;
+                while (offset < (raw.length - 4))
+                {
+                    buffer.position(offset);
+                    signature = buffer.getShort();
+                    type = (byte) ((signature >>> 8) & 0x007f);
+                    elementLength = (buffer.getShort() * 2) + 4;
+                    if (raw.length < (offset + elementLength))
+                    {
+                        System.out.println("Length not match:" + offset +":"+ buffer.position() +":"+buffer.limit());
+                        break;
+                    }
+                    recordType = ElementType.forID(type);
+                    handler = recordType.getElementHandler();
+                    if (handler != null)
+                    {
+                        Element subElement = (Element) handler.read(buffer, signature, elementLength);
+                        ((ComplexElement) _element).add(subElement);
+                        offset += elementLength;
+                    } else
+                    {
+                        byte[] remain = new byte[buffer.remaining()];
+                        System.arraycopy(raw, offset, remain, 0, buffer.remaining());
+                        for (int i = 0; i < remain.length; i++)
+                        {
+                            if (remain[i] != 0)
+                            {
+                                logger.info("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                                System.out.println("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+
+        } else
+        {
+            _element = null;
+            return false;
+        }
+        return true;
+    }
+
+    protected static byte[] getBytesFromBLOB(BLOB blob) throws SQLException, IOException
+    {
+        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();
+            throw e;
+        }
+
+        return raw;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java
new file mode 100644
index 0000000..0d9f1b8
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java
@@ -0,0 +1,31 @@
+package com.ximple.io.dgn7;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Ulysses
+ * Date: 2007/9/13
+ * Time: �W�� 11:19:08
+ * To change this template use File | Settings | File Templates.
+ */
+public class Dgn7fileException extends Exception
+{
+
+    public Dgn7fileException()
+    {
+    }
+
+    public Dgn7fileException(String message)
+    {
+        super(message);
+    }
+
+    public Dgn7fileException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+    public Dgn7fileException(Throwable cause)
+    {
+        super(cause);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java
new file mode 100644
index 0000000..0d50d54
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java
@@ -0,0 +1,58 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Dgn7fileHeader
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:21:00
+ */
+public class Dgn7fileHeader
+{
+    private short  elmtype;
+    private byte[] raw;
+
+    public Dgn7fileHeader()
+    {
+    }
+
+    public void read(ByteBuffer file, boolean strict) throws IOException
+    {
+        file.order(ByteOrder.LITTLE_ENDIAN);
+        elmtype = file.getShort();
+
+        short wtf    = file.getShort();
+        int   length = (wtf * 2);
+
+        if (file.remaining() != (length))
+        {
+            Assert.shouldNeverReachHere();
+        }
+
+        raw = new byte[length];
+        file.get(raw, 0, file.remaining());
+    }
+
+    public String toString()
+    {
+        return "Dgn7fileHeader{" + "raw=" + ((raw == null) ? "null" : raw.length) + '}';
+    }
+
+    public int size()
+    {
+        if (raw == null)
+        {
+            return 0;
+        }
+
+        return raw.length + 4;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java
new file mode 100644
index 0000000..475932c
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java
@@ -0,0 +1,718 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import javax.swing.*;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+
+/**
+ * Dgn7fileReader
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:24:10
+ */
+public class Dgn7fileReader
+{
+    private static final Logger logger = LogManager.getLogger("com.isimple.glyphjump.io.dgn7");
+    private Dgn7fileHeader      header;
+    private ReadableByteChannel channel;
+    ByteBuffer                  buffer;
+    private ElementType         fileElementType = ElementType.UNDEFINED;
+    private ByteBuffer          headerTransfer;
+    private final Record        record = new Record();
+    private final boolean       randomAccessEnabled;
+    private Lock                lock;
+    private boolean             useMemoryMappedBuffer;
+    private long                currentOffset = 0L;
+    private StreamLogging       streamLogger  = new StreamLogging("Shapefile Reader");
+    private int                 maxElementId  = 0;
+
+    public Dgn7fileReader(ReadableByteChannel channel, boolean strict, boolean useMemoryMapped, Lock lock)
+        throws IOException, Dgn7fileException
+    {
+        this.channel               = channel;
+        this.useMemoryMappedBuffer = useMemoryMapped;
+        streamLogger.open();
+        randomAccessEnabled = channel instanceof FileChannel;
+        this.lock           = lock;
+        lock.lockRead();
+        init(strict);
+    }
+
+    public Dgn7fileReader(ReadableByteChannel channel, Lock lock) throws IOException, Dgn7fileException
+    {
+        this(channel, true, true, lock);
+    }
+
+    // ensure the capacity of the buffer is of size by doubling the original
+    // capacity until it is big enough
+    // this may be naiive and result in out of MemoryError as implemented...
+    public static ByteBuffer ensureCapacity(ByteBuffer buffer, int size, boolean useMemoryMappedBuffer)
+    {
+        // This sucks if you accidentally pass is a MemoryMappedBuffer of size
+        // 80M
+        // like I did while messing around, within moments I had 1 gig of
+        // swap...
+        if (buffer.isReadOnly() || useMemoryMappedBuffer)
+        {
+            return buffer;
+        }
+
+        int limit = buffer.limit();
+
+        while (limit < size)
+        {
+            limit *= 2;
+        }
+
+        if (limit != buffer.limit())
+        {
+            // if (record.ready) {
+            buffer = ByteBuffer.allocateDirect(limit);
+
+            // }
+            // else {
+            // throw new IllegalArgumentException("next before hasNext");
+            // }
+        }
+
+        return buffer;
+    }
+
+    // for filling a ReadableByteChannel
+    public static int fill(ByteBuffer buffer, ReadableByteChannel channel) throws IOException
+    {
+        int r = buffer.remaining();
+
+        // channel reads return -1 when EOF or other error
+        // because they a non-blocking reads, 0 is a valid return value!!
+        while ((buffer.remaining() > 0) && (r != -1))
+        {
+            r = channel.read(buffer);
+        }
+
+        if (r == -1)
+        {
+            buffer.limit(buffer.position());
+        }
+
+        return r;
+    }
+
+    public static Dgn7fileHeader readHeader(ReadableByteChannel channel, boolean strict) throws IOException
+    {
+        ByteBuffer buffer = ByteBuffer.allocateDirect(4);
+
+        if (fill(buffer, channel) == -1)
+        {
+            throw new EOFException("Premature end of header");
+        }
+
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+        int        length = buffer.getShort(2) * 2;
+        ByteBuffer old    = buffer;
+
+        old.position(0);
+
+        // ensure enough capacity for one more record header
+        buffer = ByteBuffer.allocateDirect(length + 4);
+        buffer.put(old);
+
+        if (fill(buffer, channel) == -1)
+        {
+            throw new EOFException("Premature end of header");
+        }
+
+        buffer.position(0);
+
+        Dgn7fileHeader header = new Dgn7fileHeader();
+
+        header.read(buffer, strict);
+
+        return header;
+    }
+
+    public Dgn7fileHeader getHeader()
+    {
+        return header;
+    }
+
+    public void close() throws IOException
+    {
+        lock.unlockRead();
+
+        if (channel.isOpen())
+        {
+            channel.close();
+            streamLogger.close();
+        }
+
+        if (buffer instanceof MappedByteBuffer)
+        {
+            NIOUtilities.clean(buffer);
+        }
+
+        channel = null;
+        header  = null;
+    }
+
+    public boolean supportsRandomAccess()
+    {
+        return randomAccessEnabled;
+    }
+
+    public Record nextElement() throws IOException, Dgn7fileException
+    {
+        // need to update position
+        buffer.position(this.toBufferOffset(record.end));
+
+        // record header is big endian
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+        // read shape record header
+        int   recordNumber = ++maxElementId;
+        short signature    = buffer.getShort();
+
+        // byte type = (byte) (buffer.get() & 0x7f);
+        byte type = (byte) ((signature >>> 8) & 0x007f);
+
+        // silly Bentley say contentLength is in 2-byte words
+        // and ByteByffer uses bytes.
+        // track the record location
+        int elementLength = (buffer.getShort() * 2) + 4;
+
+        if (!buffer.isReadOnly() &&!useMemoryMappedBuffer)
+        {
+            // capacity is less than required for the record
+            // copy the old into the newly allocated
+            if (buffer.capacity() < elementLength)
+            {
+                this.currentOffset += buffer.position();
+
+                ByteBuffer old = buffer;
+
+                // ensure enough capacity for one more record header
+                buffer = ensureCapacity(buffer, elementLength, useMemoryMappedBuffer);
+                buffer.put(old);
+                fill(buffer, channel);
+                buffer.position(0);
+            } else
+
+            // remaining is less than record length
+            // compact the remaining data and read again,
+            // allowing enough room for one more record header
+            if (buffer.remaining() < elementLength)
+            {
+                this.currentOffset += buffer.position();
+                buffer.compact();
+                fill(buffer, channel);
+                buffer.position(0);
+            }
+        }
+
+        // shape record is all little endian
+        // buffer.order(ByteOrder.LITTLE_ENDIAN);
+        // read the type, handlers don't need it
+        ElementType recordType = ElementType.forID(type);
+
+        logger.debug("nextElement at " + this.toBufferOffset(record.end) + ":type=" + type);
+
+        // this usually happens if the handler logic is bunk,
+        // but bad files could exist as well...
+
+        /*
+         * if (recordType != ElementType.NULL && recordType != fileElementType)
+         * {
+         *   throw new IllegalStateException("ShapeType changed illegally from " + fileElementType + " to " + recordType);
+         * }
+         */
+
+        // peek at bounds, then reset for handler
+        // many handler's may ignore bounds reading, but we don't want to
+        // second guess them...
+        buffer.mark();
+
+        if (recordType.isMultiPoint())
+        {
+            int lowCoorX = buffer.getInt();
+
+            lowCoorX    = Utility.ConvertFromDGN(lowCoorX);
+            record.minX = Utility.ConverUnitToCoord(lowCoorX);
+
+            int lowCoorY = buffer.getInt();
+
+            lowCoorY    = Utility.ConvertFromDGN(lowCoorY);
+            record.minY = Utility.ConverUnitToCoord(lowCoorY);
+
+            int lowCoorZ = buffer.getInt();
+
+            lowCoorZ    = Utility.ConvertFromDGN(lowCoorZ);
+            record.minZ = Utility.ConverUnitToCoord(lowCoorZ);
+
+            int highCoorX = buffer.getInt();
+
+            highCoorX   = Utility.ConvertFromDGN(highCoorX);
+            record.maxX = Utility.ConverUnitToCoord(highCoorX);
+
+            int highCoorY = buffer.getInt();
+
+            highCoorY   = Utility.ConvertFromDGN(highCoorY);
+            record.maxY = Utility.ConverUnitToCoord(highCoorY);
+
+            int highCoorZ = buffer.getInt();
+
+            highCoorZ   = Utility.ConvertFromDGN(highCoorZ);
+            record.maxZ = Utility.ConverUnitToCoord(highCoorZ);
+        }
+
+        buffer.reset();
+        record.offset = record.end;
+
+        // update all the record info.
+        record.length    = elementLength;
+        record.signature = signature;
+        record.number    = recordNumber;
+
+        // remember, we read one int already...
+        record.end = this.toFileOffset(buffer.position()) + elementLength - 4;
+        // record.end = this.toFileOffset(buffer.position()) + elementLength;
+
+        // mark this position for the reader
+        record.start = buffer.position();
+
+        // clear any cached record
+        record.handler = recordType.getElementHandler();
+        record.element = null;
+
+        return record;
+    }
+
+    public void goTo(int offset) throws IOException, UnsupportedOperationException
+    {
+        if (randomAccessEnabled)
+        {
+            if (this.useMemoryMappedBuffer)
+            {
+                buffer.position(offset);
+            } else
+            {
+                /*
+                 *     Check to see if requested offset is already loaded; ensure
+                 *     that record header is in the buffer
+                 */
+                if ((this.currentOffset <= offset) && (this.currentOffset + buffer.limit() >= offset + 4))
+                {
+                    buffer.position(this.toBufferOffset(offset));
+                } else
+                {
+                    FileChannel fc = (FileChannel) this.channel;
+
+                    fc.position(offset);
+                    this.currentOffset = offset;
+                    buffer.position(0);
+                    fill(buffer, fc);
+                    buffer.position(0);
+                }
+            }
+
+            int oldRecordOffset = record.end;
+
+            record.end = offset;
+
+            try
+            {
+                hasNext();
+            } catch (IOException ioe)
+            {
+                record.end = oldRecordOffset;
+
+                throw ioe;
+            }
+        } else
+        {
+            throw new UnsupportedOperationException("Random Access not enabled");
+        }
+    }
+
+    public Record elementAt(int offset) throws IOException, UnsupportedOperationException, Dgn7fileException
+    {
+        if (randomAccessEnabled)
+        {
+            this.goTo(offset);
+
+            return nextElement();
+        }
+
+        throw new UnsupportedOperationException("Random Access not enabled");
+    }
+
+    public boolean hasNext() throws IOException
+    {
+        // mark current position
+        int position = buffer.position();
+
+        // ensure the proper position, regardless of read or handler behavior
+        try
+        {
+            buffer.position(this.toBufferOffset(record.end));
+        } catch (IllegalArgumentException e)
+        {
+            logger.warn("position=" + this.toBufferOffset(record.end), e);
+
+            return false;
+        }
+
+        // no more data left
+        if (buffer.remaining() < 4)
+        {
+            return false;
+        }
+
+        // looks good
+        boolean hasNext = true;
+        short   type    = buffer.getShort();
+
+        if (type == -1)
+        {
+            hasNext = false;
+        }
+
+        // reset things to as they were
+        buffer.position(position);
+
+        return hasNext;
+    }
+
+    private void init(boolean strict) throws IOException, Dgn7fileException
+    {
+        header = readHeader(channel, strict);
+
+        // fileElementType = header.getElementType();
+        // handler = fileElementType.getElementHandler();
+
+        // recordHeader = ByteBuffer.allocateDirect(4);
+        // recordHeader.order(ByteOrder.BIG_ENDIAN);
+        // if (handler == null)
+        // {
+        // throw new IOException("Unsuported shape type:" + fileElementType);
+        // }
+        if ((channel instanceof FileChannel) && useMemoryMappedBuffer)
+        {
+            FileChannel fc = (FileChannel) channel;
+
+            buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+
+            // buffer.position(100);
+            buffer.position(header.size());
+            this.currentOffset = 0;
+        } else
+        {
+            // force useMemoryMappedBuffer to false
+            this.useMemoryMappedBuffer = false;
+
+            // start with 8K buffer
+            buffer = ByteBuffer.allocateDirect(8 * 1024);
+            fill(buffer, channel);
+            buffer.flip();
+            this.currentOffset = header.size();
+        }
+
+        headerTransfer = ByteBuffer.allocate(4);
+        headerTransfer.order(ByteOrder.LITTLE_ENDIAN);
+
+        // make sure the record end is set now...
+        record.end = toFileOffset(buffer.position());
+    }
+
+    private int toBufferOffset(int offset)
+    {
+        return (int) (offset - currentOffset);
+    }
+
+    private int toFileOffset(int offset)
+    {
+        return (int) (currentOffset + offset);
+    }
+
+    public int getCount(int count) throws Dgn7fileException
+    {
+        try
+        {
+            if (channel == null)
+            {
+                return -1;
+            }
+
+            count = 0;
+
+            for (int tmp = readElement(); tmp != -1; tmp = readElement())
+            {
+                count += tmp;
+            }
+        } catch (IOException ioe)
+        {
+            count = -1;
+
+            // What now? This seems arbitrarily appropriate !
+            throw new Dgn7fileException("Problem reading dgnfile record", ioe);
+        }
+
+        return count;
+    }
+
+    public int getCount() throws Dgn7fileException
+    {
+        return getCount(0);
+    }
+
+    private int readElement() throws IOException
+    {
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        // burn the record number
+        buffer.getInt();
+
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        int recordlength = buffer.getInt() * 2;
+
+        // Going to read the first 4 bytes of the record so
+        // subtract that from the record length
+        recordlength -= 4;
+
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        // read record type (used to determine if record is a null record)
+        int type = buffer.getInt();
+
+        // go to end of record
+        while (buffer.limit() < buffer.position() + recordlength)
+        {
+            recordlength -= buffer.limit() - buffer.position();
+            buffer.clear();
+
+            if (channel.read(buffer) < 1)
+            {
+                return -1;
+            }
+        }
+
+        buffer.position(buffer.position() + recordlength);
+
+        // return 0 if record is null. Null records should be counted.
+        if (type == 0)
+        {
+            // this is a null feature
+            return 0;
+        }
+
+        return 1;
+    }
+
+    private boolean fillBuffer() throws IOException
+    {
+        int result = 1;
+
+        if (buffer.limit() <= buffer.position() + 4)
+        {
+            result = fill(buffer, channel);
+        }
+
+        return result > 0;
+    }
+
+    public static void main(String[] args)
+    {
+        JFileChooser jfc = new JFileChooser("D:/TEMP");
+        File         f   = null;
+        int          r   = jfc.showOpenDialog(new JFrame());
+
+        if (r == JFileChooser.APPROVE_OPTION)
+        {
+            try
+            {
+                f = jfc.getSelectedFile();
+
+                FileChannel    channel = new FileInputStream(f).getChannel();
+                Dgn7fileReader reader  = new Dgn7fileReader(channel, new Lock());
+
+                System.out.println(reader.getHeader().toString());
+
+                GeometryFactory factory = new GeometryFactory();
+                int             count, size;
+
+                count = 0;
+                size  = 0;
+
+                try
+                {
+                    Element lastComplex = null;
+
+                    while (reader.hasNext())
+                    {
+                        size++;
+
+                        Dgn7fileReader.Record record = reader.nextElement();
+
+                        if (record.element() != null)
+                        {
+                            Element     element = (Element) record.element();
+                            ElementType type    = element.getElementType();
+
+                            if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                            {
+                                if (lastComplex != null)
+                                {
+                                    // @todo add process in here
+                                    count++;
+                                    lastComplex = null;
+                                }
+
+                                // @todo add process in here
+                                count++;
+                            } else if (element.isComponentElement())
+                            {
+                                if (lastComplex != null)
+                                {
+                                    ((ComplexElement) lastComplex).add(element);
+                                }
+                            } else if (type.isComplexElement())
+                            {
+                                if (lastComplex == null)
+                                {
+                                    lastComplex = element;
+                                } else
+                                {
+                                    // @todo add process in here
+                                    count++;
+                                    lastComplex = element;
+                                }
+                            }
+                        }
+                    }
+                } catch (IOException e)
+                {
+                    logger.warn("Stop read dgn file", e);
+                } catch (Dgn7fileException e)
+                {
+                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                } finally
+                {
+                    reader.close();
+                }
+
+                System.out.println("count=" + count + " size=" + size);
+                // reader.close();
+            } catch (IOException ioe)
+            {
+                System.out.println(ioe);
+                ioe.printStackTrace();
+            } catch (Dgn7fileException e)
+            {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
+        }
+
+        System.exit(0);
+    }
+
+    public final class Record
+    {
+        int   length;
+        int   number = 0;
+        int   offset;           // Relative to the whole file
+        int   start     = 0;    // Relative to the current loaded buffer
+        short signature = 0;
+
+        /**
+         * The minimum X value.
+         */
+        public double minX;
+
+        /**
+         * The minimum Y value.
+         */
+        public double minY;
+
+        /**
+         * The minimum Z value.
+         */
+        public double minZ;
+
+        /**
+         * The maximum X value.
+         */
+        public double maxX;
+
+        /**
+         * The maximum Y value.
+         */
+        public double maxY;
+
+        /**
+         * The maximum Z value.
+         */
+        public double maxZ;
+
+        // ElementType type;
+        int             end     = 0;    // Relative to the whole file
+        Object          element = null;
+        IElementHandler handler;
+
+        public Object element()
+        {
+            if (element == null)
+            {
+                buffer.position(start);
+                buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+                if (handler == null)
+                {
+                    return null;
+                }
+
+                element = handler.read(buffer, signature, length);
+            }
+
+            return element;
+        }
+
+        public int offset()
+        {
+            return offset;
+        }
+
+        /**
+         * A summary of the record.
+         */
+        public String toString()
+        {
+            return "Record " + number + " length " + length + " bounds " + minX + "," + minY + " " + maxX + "," + maxY;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
new file mode 100644
index 0000000..c8282b4
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
@@ -0,0 +1,272 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * Record
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:14:50
+ */
+public class Element
+{
+    public static final int CONSTRUCTION_CLASS      = 0;
+    public static final int CONSTRUCTION_RULE_CLASS = 0;
+    public static final int DIMENSION_CLASS         = 0;
+    public static final int LINEAR_PATTERNED_CLASS  = 0;
+    public static final int MAX_ELEMENT_SIZE        = 0;
+    public static final int MAX_VERTICES            = 100;
+    public static final int PATTERN_AREA            = 0;
+    public static final int PATTERN_COMPONENT_CLASS = 0;
+    public static final int PATTERN_CROSSHATCH      = 0;
+    public static final int PATTERN_HATCH           = 0;
+    public static final int PRIMARY_CLASS           = 0;
+    public static final int PRIMARY_RULE_CLASS      = 0;
+
+    protected short[]   raw;
+    protected byte      attrOffset = 0;
+
+    public Element(short[] raw)
+    {
+        this.raw = raw;
+    }
+
+    public int getLineStyle()
+    {
+        return 0;
+    }
+
+    public Envelope getRange()
+    {
+        int lowCoorX = (int) ((raw[3] << 16) & 0xffff0000) + (raw[2] & 0x0000ffff);
+
+        lowCoorX = Utility.ConvertFromDGN(lowCoorX);
+
+        int lowCoorY = (int) ((raw[5] << 16) & 0xffff0000) + (raw[4] & 0x0000ffff);
+
+        lowCoorY = Utility.ConvertFromDGN(lowCoorY);
+
+        int highCoorX = (int) ((raw[9] << 16) & 0xffff0000) + (raw[8] & 0x0000ffff);
+
+        highCoorX = Utility.ConvertFromDGN(highCoorX);
+
+        int highCoorY = (int) ((raw[11] << 16) & 0xffff0000) + (raw[10] & 0x0000ffff);
+
+        highCoorY = Utility.ConvertFromDGN(highCoorY);
+
+        return new Envelope(Utility.ConverUnitToCoord(lowCoorX), Utility.ConverUnitToCoord(highCoorX),
+                            Utility.ConverUnitToCoord(lowCoorY), Utility.ConverUnitToCoord(highCoorY));
+    }
+
+    public boolean isComponentElement()
+    {
+        if ((short) ((raw[0] >>> 7) & 0x0001) == 1)
+        {
+            return true;
+        } else
+        {
+            return false;
+        }
+    }
+
+    public boolean removeUserAttributeData(int iLinkageId)
+    {
+        return true;
+    }
+
+    public boolean removeUserAttributeData(int iLinkageId, int iLinkageIndex)
+    {
+        return true;
+    }
+
+    public boolean isDeleted()
+    {
+        if ((short) ((raw[0] >>> 15) & 0x0001) == 1)
+        {
+            return true;
+        } else
+        {
+            return false;
+        }
+    }
+
+    public int getColorIndex()
+    {
+        return ((raw[17] >>> 8) & 0x00ff);
+    }
+
+    public int getType()
+    {
+        return ((raw[0] >>> 8) & 0x007f);
+    }
+
+    public ElementType getElementType()
+    {
+        return ElementType.forID(getType());
+    }
+
+    public int getLevelIndex()
+    {
+        return (raw[0] & 0x003f);
+    }
+
+    public int getWeight()
+    {
+        return ((raw[17] >>> 3) & 0x001f);
+    }
+
+    public void addUserAttributeData(byte[] pDataBlock, Class dataClass, int iLinkageId) throws Element.Exception
+    {
+    }
+
+    public void addUserAttributeData(byte[] pDataBlock, int iLinkageId, Object oDataDef) throws Element.Exception
+    {
+    }
+
+    public boolean hasUserAttributeData()
+    {
+        if (raw[15] <= 0)
+        {
+            return false;
+        }
+
+        short index = (short) (raw[15] + 16);
+
+        if (index == -1)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    public List<UserAttributeData> getUserAttributeData()
+    {
+        short[] data;
+        short   length, nextAttribute;
+
+        if (raw[15] <= 0)
+        {
+            return new ArrayList<UserAttributeData>();
+        }
+
+        short index = (short) (raw[15] + 16 + attrOffset);
+
+        if (index == -1)
+        {
+            return null;
+        }
+
+        ArrayList<UserAttributeData> aLinkageSet = new ArrayList<UserAttributeData>();
+
+        while (index < raw.length)
+        {
+            length = (short) (raw[index] & (short) 0x00ff);
+
+            if (length == 0)
+            {
+                break;
+            }
+
+            nextAttribute = (short) (index + length + 1);
+            data          = new short[length];
+            System.arraycopy(raw, index + 1, data, 0, length);
+
+            if (data[0] == (short) 0x0020)
+            {
+                aLinkageSet.add(new FrammeAttributeData(data));
+            } else
+            {
+                aLinkageSet.add(new UserAttributeData(data));
+            }
+
+            index = nextAttribute;
+        }
+
+        return aLinkageSet;
+    }
+
+    public void getUserAttributeData(byte[] pDataBlock, Class dataClass, int iLinkageId, int iLinkageIndex)
+    {
+    }
+
+    public void getUserAttributeData(byte[] pDataBlock, int iLinkageId, Object oDataDef)
+    {
+    }
+
+    public static class Exception extends java.lang.Exception
+    {
+        public Exception()
+        {
+        }
+
+        // Constructs an Record.Exception with no detail message.
+        public Exception(String oStrMessage)
+        {
+            super(oStrMessage);
+        }
+    }
+
+
+    public static class ElementHandler implements IElementHandler
+    {
+        ElementType elementType;
+
+        public ElementHandler(ElementType elementType)
+        {
+            this.elementType = elementType;
+        }
+
+        public ElementType getElementType()
+        {
+            return elementType;
+        }
+
+        public Object read(ByteBuffer buffer, short signature, int length)
+        {
+            byte[] dst = new byte[length - 4];
+            try
+            {
+                buffer.get(dst, 0, dst.length);
+            } catch (BufferUnderflowException exception)
+            {
+                throw exception;   
+            }
+            ByteBuffer tmpBuffer = ByteBuffer.wrap(dst);
+            tmpBuffer.order(ByteOrder.LITTLE_ENDIAN);
+            ShortBuffer sbuffer = tmpBuffer.asShortBuffer();
+
+            short[] rawMem = new short[(length / 2)];
+            sbuffer.get(rawMem, 2, rawMem.length - 2);
+            rawMem[0] = signature;
+            rawMem[1] = (short) ((length / 2) - 2);
+            Element elm = createElement(rawMem);
+
+            return elm;
+        }
+
+        public void write(ByteBuffer buffer, Object element)
+        {
+        }
+
+        public int getLength(Object element)
+        {
+            return ((Element) element).raw.length;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new Element(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java
new file mode 100644
index 0000000..dda70b2
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java
@@ -0,0 +1,534 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+/*----------------------------------------------------------------------+
+|                                                                       |
+|   Type        Description                                             |
+|    1          Cell Library Header                                     |
+|    2          Cell (complex)                                          |
+|    3          Line                                                    |
+|    4          Line String                                             |
+|    5          Group Data                                              |
+|    6          Shape                                                   |
+|    7          Text Node (complex)                                     |
+|    8          Digitizer Setup Data                                    |
+|    9          Design File Header if level 8                           |
+|    10         Level Symbology                                         |
+|    11         Curve                                                   |
+|    12         Complex String (complex)                                |
+|    13         Conic                                                   |
+|    14         Complex Shape (complex)                                 |
+|    15         Ellipse                                                 |
+|    16         Arc                                                     |
+|    17         Text                                                    |
+|    18         Surface (complex)                                       |
+|    19         Solid (complex)                                         |
+|    20         not used                                                |
+|    21         B-Spline Pole                                           |
+|    22         Point String                                            |
+|    23         Circular Truncated Cone                                 |
+|    24         B-Spline Surface (complex)                              |
+|    25         B-Spline Surface boundary                               |
+|    26         B-Spline Knot Record                                   |
+|    27         B-Spline Curve (complex)                                |
+|    28         B-Spline Weight Factor                                  |
+|    33         Dimension Record                                       |
+|    34         Shared Cell Definition Record                          |
+|    35         Shared Cell Record                                     |
+|    36         Multiline Record                                       |
+|    37         Attribute Record                                       |
+|    38         DgnStore Component                                      |
+|    39         DgnStore Header                                         |
+|    66         MicroStation Application                                |
+|    87         Raster Header                                           |
+|    88         Raster Component                                        |
+|    90         Raster Reference Attachment                             |
+|    91         Raster Reference Component                              |
+|    92         Raster Hierarchy Record                                |
+|    93         Raster Hierarchy Component                              |
+|    94         Raster Frame Record                                    |
+|    95         Table Entry Record                                     |
+|    96         Table Header Record                                    |
+|    97         View Group Record                                      |
+|    98         View Record                                            |
+|    99         Level Mask Record                                      |
+|    100        Reference Attach Record                                |
+|    101        Matrix Header                                           |
+|    102        Matrix Int Data                                         |
+|    103        Matrix Double Data                                      |
+|    105        Mesh Header                                             |
+|    106        Extended Record (graphic) (complex)                    |
+|    107        Extended Record (non-graphic) (complex)                |
+|    108        Reference Override Record                              |
+|    110        Named Group Header                                      |
+|    111        Named Group Component                                   |
+|                                                                       |
++----------------------------------------------------------------------*/
+
+/**
+ * ElementType
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:26:49
+ */
+public final class ElementType
+{
+    /**
+     * Represents a Null shape (id = 0).
+     */
+    public static final ElementType NULL = new ElementType(0, "Null");
+
+    /**
+     * Represents a Line shape (id = 3).
+     */
+    public static final ElementType LINE = new ElementType(3, "Line");
+
+    /**
+     * Represents a LineString shape (id = 4).
+     */
+    public static final ElementType LINESTRING = new ElementType(4, "LineString");
+
+    /**
+     * Represents a Shape shape (id = 6).
+     */
+    public static final ElementType SHAPE = new ElementType(6, "Shape");
+
+    /**
+     * Represents an TextNode shape (id = 7).
+     */
+    public static final ElementType TEXTNODE = new ElementType(7, "TextNode");
+
+    /**
+     * Represents an IGDSDIGITIZER shape (id = 8).
+     */
+    public static final ElementType IGDSDIGITIZER = new ElementType(8, "IGDSDigitizer");
+
+    /**
+     * Represents an TCB shape (id = 9).
+     */
+    public static final ElementType TCB = new ElementType(9, "Tcb");
+
+    /**
+     * Represents a LevelSymbology shape (id = 5).
+     */
+    public static final ElementType LEVELSYMBOLOGY = new ElementType(10, "LevelSymbology");
+
+    /**
+     * Represents a ComplexChain shape (id = 15).
+     */
+    public static final ElementType COMPLEXCHAIN = new ElementType(12, "ComplexChain");
+
+    /**
+     * Represents a ComplexShape shape (id = 25).
+     */
+    public static final ElementType COMPLEXSHAPE = new ElementType(14, "ComplexShape");
+
+    /**
+     * Represents a Ellipse shape (id = 8).
+     */
+    public static final ElementType ELLIPSE = new ElementType(15, "Ellipse");
+
+    /**
+     * Represents a Arc shape (id = 28).
+     */
+    public static final ElementType ARC = new ElementType(16, "Arc");
+
+    /**
+     * Represents a Arc shape (id = 28).
+     */
+    public static final ElementType TEXT = new ElementType(17, "Text");
+
+    /**
+     * Represents a Arc shape (id = 28).
+     */
+    public static final ElementType POINTSTRING = new ElementType(22, "PointString");
+
+    /**
+     * Represents an Undefined shape (id = -1).
+     */
+    public static final ElementType UNDEFINED = new ElementType(-1, "Undefined");
+
+    /**
+     * The integer id of this ElementType.
+     */
+    public final int id;
+
+    /**
+     * The human-readable name for this ElementType.<br>
+     * Could easily use ResourceBundle for internationialization.
+     */
+    public final String name;
+
+    /**
+     * Creates a new instance of ElementType. Hidden on purpose.
+     *
+     * @param id   The id.
+     * @param name The name.
+     */
+    protected ElementType(int id, String name)
+    {
+        this.id   = id;
+        this.name = name;
+    }
+
+    /**
+     * Get the name of this ElementType.
+     *
+     * @return The name.
+     */
+    public String toString()
+    {
+        return name;
+    }
+
+    /**
+     * Is this a multipoint shape? Hint- all shapes are multipoint except NULL,
+     * UNDEFINED, and the POINTs.
+     *
+     * @return true if multipoint, false otherwise.
+     */
+    public boolean isMultiPoint()
+    {
+        boolean mp = true;
+
+        if (this == UNDEFINED)
+        {
+            mp = false;
+        } else if (this == NULL)
+        {
+            mp = false;
+        } else if (this == IGDSDIGITIZER)
+        {
+            mp = false;
+        } else if (this == TCB)
+        {
+            mp = false;
+        } else if (this == LEVELSYMBOLOGY)
+        {
+            mp = false;
+        }
+
+        return mp;
+    }
+
+    public boolean isComplexElement()
+    {
+        if (id == 2)
+        {
+            return true;
+        }
+
+        if (id == 7)
+        {
+            return true;
+        }
+
+        if (id == 12)
+        {
+            return true;
+        }
+
+        if (id == 14)
+        {
+            return true;
+        }
+
+        if (id == 18)
+        {
+            return true;
+        }
+
+        if (id == 19)
+        {
+            return true;
+        }
+
+        if (id == 106)
+        {
+            return true;
+        }
+
+        if (id == 107)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean isPointType()
+    {
+        if (id == 7)
+        {
+            return true;
+        }
+
+        if (id == 17)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean isLineType()
+    {
+        if (id == 3)
+        {
+            return true;
+        }
+
+        if (id == 4)
+        {
+            return true;
+        }
+
+        if (id == 11)
+        {
+            return true;
+        }
+
+        if (id == 12)
+        {
+            return true;
+        }
+
+        if (id == 16)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean isPolygonType()
+    {
+        if (id == 6)
+        {
+            return true;
+        }
+
+        if (id == 14)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean isMultiPointType()
+    {
+        if (id == 3)
+        {
+            return true;
+        }
+
+        if (id == 4)
+        {
+            return true;
+        }
+
+        if (id == 6)
+        {
+            return true;
+        }
+
+        if (id == 11)
+        {
+            return true;
+        }
+
+        if (id == 12)
+        {
+            return true;
+        }
+
+        if (id == 13)
+        {
+            return true;
+        }
+
+        if (id == 14)
+        {
+            return true;
+        }
+
+        if (id == 15)
+        {
+            return true;
+        }
+
+        if (id == 16)
+        {
+            return true;
+        }
+
+        if (id == 22)
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine the ElementType for the id.
+     *
+     * @param id The id to search for.
+     * @return The ElementType for the id.
+     */
+    public static ElementType forID(int id)
+    {
+        ElementType t;
+
+        switch (id)
+        {
+        case 0 :
+            t = NULL;
+
+            break;
+
+        case 3 :
+            t = LINE;
+
+            break;
+
+        case 4 :
+            t = LINESTRING;
+
+            break;
+
+        case 6 :
+            t = SHAPE;
+
+            break;
+
+        case 7 :
+            t = TEXTNODE;
+
+            break;
+
+        case 8 :
+            t = IGDSDIGITIZER;
+
+            break;
+
+        case 9 :
+            t = TCB;
+
+            break;
+
+        case 10 :
+            t = LEVELSYMBOLOGY;
+
+            break;
+
+        case 12 :
+            t = COMPLEXCHAIN;
+
+            break;
+
+        case 14 :
+            t = COMPLEXSHAPE;
+
+            break;
+
+        case 15 :
+            t = ELLIPSE;
+
+            break;
+
+        case 16 :
+            t = ARC;
+
+            break;
+
+        case 17 :
+            t = TEXT;
+
+            break;
+
+        default :
+            t = UNDEFINED;
+
+            break;
+        }
+
+        return t;
+    }
+
+    public IElementHandler getElementHandler() throws Dgn7fileException
+    {
+        IElementHandler handler;
+
+        switch (id)
+        {
+        case 3 :
+            handler = LineElement.ElementHandler.getInstance();
+
+            break;
+
+        case 4 :
+            handler = LineStringElement.ElementHandler.getInstance();
+
+            break;
+
+        case 6 :
+            handler = ShapeElement.ElementHandler.getInstance();
+
+            break;
+
+        case 7 :
+            handler = TextNodeElement.ElementHandler.getInstance();
+
+            break;
+
+        case 8 :
+            handler = new Element.ElementHandler(this);
+
+            break;
+
+        case 9 :
+            handler = new Element.ElementHandler(this);
+
+            break;
+
+        case 10 :
+            handler = new Element.ElementHandler(this);
+
+            break;
+
+        case 12 :
+            handler = ComplexChainElement.ElementHandler.getInstance();
+
+            break;
+
+        case 14 :
+            handler = ComplexShapeElement.ElementHandler.getInstance();
+
+            break;
+
+        case 15 :
+            handler = new Element.ElementHandler(this);
+
+            break;
+
+        case 16 :
+            handler = ArcElement.ElementHandler.getInstance();
+
+            break;
+
+        case 17 :
+            handler = TextElement.ElementHandler.getInstance();
+
+            break;
+
+        default :
+            handler = null;
+        }
+
+        return handler;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java
new file mode 100644
index 0000000..3707168
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java
@@ -0,0 +1,71 @@
+package com.ximple.io.dgn7;
+
+/**
+ * FrammeAttributeData
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 06:36:55
+ */
+public class FrammeAttributeData extends UserAttributeData
+{
+    public FrammeAttributeData(short id)
+    {
+        super(id, 7);
+    }
+
+    public FrammeAttributeData(short[] src)
+    {
+        super(src);
+    }
+
+    public short getFsc()
+    {
+        int fsc = _src[3] & 0x0000ffff;
+
+        return (short) fsc;
+    }
+
+    public int getUfid()
+    {
+        int ufid = (int) (_src[2] << 16 & 0xffff0000);
+
+        ufid += _src[1] & 0x0000ffff;
+
+        return ufid;
+    }
+
+    public byte getComponentID()
+    {
+        int cmpid = (int) (_src[5] & 0x000000ff);
+
+        return (byte) cmpid;
+    }
+
+    public byte getRuleNo()
+    {
+        int no = (int) ((_src[5] >> 8) & 0x000000ff);
+
+        return (byte) no;
+    }
+
+    public short getStatus()
+    {
+        int status = (int) (_src[4] & 0x0000ffff);
+
+        return (short) status;
+    }
+
+    public short getOccID()
+    {
+        int occid = (int) (_src[6] & 0x0000ffff);
+
+        return (short) occid;
+    }
+
+    public String toString()
+    {
+        return "FrammeData{" + getFsc() + "," + getUfid() + "," + getComponentID() + "," + getRuleNo() + "," + getStatus() + ","
+               + getOccID() + "}";
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java
new file mode 100644
index 0000000..2ac6fdc
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java
@@ -0,0 +1,18 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * GeometryConverter
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:38:57
+ */
+public interface GeometryConverter
+{
+    public Geometry toGeometry(GeometryFactory factory);
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
new file mode 100644
index 0000000..c98b9f3
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
@@ -0,0 +1,23 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteBuffer;
+
+/**
+ * IElementHandler
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:50:26
+ */
+public interface IElementHandler
+{
+    public ElementType getElementType();
+
+    public Object read(ByteBuffer buffer, short signature, int length);
+
+    public void write(ByteBuffer buffer, Object element);
+
+    public int getLength(Object element);
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java
new file mode 100644
index 0000000..8b0a2bc
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java
@@ -0,0 +1,129 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * LineElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:34:59
+ */
+public class LineElement extends Element implements GeometryConverter
+{
+    public LineElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public Coordinate getCentroid(double dTolerance)
+    {
+        return null;
+    }
+
+    public Coordinate getEndPoint()
+    {
+        int endX = (int) ((raw[22] << 16) & 0xffff0000);
+
+        endX = endX + (raw[23] & 0x0000ffff);
+
+        double x    = Utility.ConverUnitToCoord(endX);
+        int    endY = (int) ((raw[24] << 16) & 0xffff0000);
+
+        endY = endY + (raw[25] & 0x0000ffff);
+
+        double y = Utility.ConverUnitToCoord(endY);
+
+        return new Coordinate(x, y);
+    }
+
+    public Coordinate getNormal()
+    {
+        return null;
+    }
+
+    public Coordinate getOrigin()
+    {
+        return null;
+    }
+
+    public Coordinate getStartPoint()
+    {
+        int startX = (int) ((raw[18] << 16) & 0xffff0000);
+
+        startX = startX + (raw[19] & 0x0000ffff);
+
+        double x      = Utility.ConverUnitToCoord(startX);
+        int    startY = (int) ((raw[20] << 16) & 0xffff0000);
+
+        startY = startY + (raw[21] & 0x0000ffff);
+
+        double y = Utility.ConverUnitToCoord(startY);
+
+        return new Coordinate(x, y);
+    }
+
+    public Coordinate getVertex(int index)
+    {
+        return (index == 0)
+               ? getStartPoint()
+               : getEndPoint();
+    }
+
+    public double getLength()
+    {
+        Coordinate p1 = getStartPoint();
+        Coordinate p2 = getEndPoint();
+
+        return Utility.getLength(p1.x, p1.y, p2.x, p2.y);
+    }
+
+    public Coordinate pointAtDistance(double dDistance, double dTolerance)
+    {
+        return null;
+    }
+
+    public Coordinate[] getVertices()
+    {
+        Coordinate[] result = new Coordinate[2];
+
+        result[0] = getStartPoint();
+        result[1] = getEndPoint();
+
+        return result;
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        return factory.createLineString(getVertices());
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.LINE);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new LineElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java
new file mode 100644
index 0000000..bc163af
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java
@@ -0,0 +1,166 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * LineStringElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 02:48:58
+ */
+public class LineStringElement extends Element implements GeometryConverter
+{
+    public LineStringElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public Coordinate getCentroid(double dTolerance)
+    {
+        return null;
+    }
+
+    public Coordinate getEndPoint()
+    {
+        return new Coordinate(getX(getVerticeSize() - 1), getY(getVerticeSize() - 1));
+    }
+
+    public Coordinate getNormal()
+    {
+        return null;
+    }
+
+    public Coordinate getOrigin()
+    {
+        return null;
+    }
+
+    public Coordinate getStartPoint()
+    {
+        return new Coordinate(getX(0), getY(0));
+    }
+
+    public Coordinate getVertex(int index)
+    {
+        return (index == 0)
+               ? getStartPoint()
+               : getEndPoint();
+    }
+
+    public int getVerticeSize()
+    {
+        return raw[18] & 0x0000ffff;
+    }
+
+    public double getLength()
+    {
+        double       result = 0.0;
+        Coordinate[] vset   = getVertices();
+
+        for (int i = 1; i < getVerticeSize(); i++)
+        {
+            Coordinate p1 = vset[i - 1];
+            Coordinate p2 = vset[i];
+
+            result += Utility.getLength(p1.x, p1.y, p2.x, p2.y);
+        }
+
+        return result;
+    }
+
+    public Coordinate pointAtDistance(double dDistance, double dTolerance)
+    {
+        return null;
+    }
+
+    public Coordinate[] getVertices()
+    {
+        Coordinate[] result = new Coordinate[getVerticeSize()];
+
+        for (int i = 0; i < getVerticeSize(); i++)
+        {
+            result[i] = new Coordinate(getX(i), getY(i));
+        }
+
+        return result;
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        return factory.createLineString(getVertices());
+    }
+
+    protected double getX(int index)
+    {
+        if ((index < 0) || (index > getVerticeSize()))
+        {
+            return -1;
+        }
+
+        int x = (int) ((raw[19 + (4 * index)] << 16) & 0xffff0000);
+
+        x += (int) (raw[20 + (4 * index)] & 0x0000ffff);
+
+        return Utility.ConverUnitToCoord(x);
+    }
+
+    protected void setX(int index, double dx)
+    {
+        int newVal = Utility.ConverCoordToUnit(dx);
+
+        raw[19 + (4 * index)] = (short) (newVal >> 16 & 0x0000ffff);
+        raw[20 + (4 * index)] = (short) (newVal & 0x0000ffff);
+    }
+
+    protected double getY(int index)
+    {
+        if ((index < 0) || (index > getVerticeSize()))
+        {
+            return -1;
+        }
+
+        int y = (int) ((raw[21 + (4 * index)] << 16) & 0xffff0000);
+
+        y = y + (int) (raw[22 + (4 * index)] & 0x0000ffff);
+
+        return Utility.ConverUnitToCoord(y);
+    }
+
+    protected void setY(int index, double dy)
+    {
+        int newVal = Utility.ConverCoordToUnit(dy);
+
+        raw[21 + (4 * index)] = (short) ((newVal >> 16) & 0x0000ffff);
+        raw[22 + (4 * index)] = (short) (newVal & 0x0000ffff);
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.LINESTRING);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new LineStringElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java
new file mode 100644
index 0000000..e51dbf2
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java
@@ -0,0 +1,263 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+/**
+ * Lock
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 10:27:24
+ */
+public class Lock
+{
+    Logger logger = LogManager.getLogger("com.ximple.io.dgn7");
+
+    /**
+     * indicates a write is occurring
+     */
+    int writeLocks = 0;
+
+    /**
+     * if not null a writer is waiting for the lock or is writing.
+     */
+    Thread writer;
+
+    /**
+     * Thread->Owner map. If empty no read locks exist.
+     */
+    Map owners = new HashMap();
+
+    /**
+     * If the lock can be read locked the lock will be read and default
+     * visibility for tests
+     *
+     * @return
+     * @throws java.io.IOException
+     */
+    synchronized boolean canRead() throws IOException
+    {
+        if ((writer != null) && (writer != Thread.currentThread()))
+        {
+            return false;
+        }
+
+        if (writer == null)
+        {
+            return true;
+        }
+
+        if (owners.size() > 1)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * If the lock can be read locked the lock will be read and default
+     * visibility for tests
+     *
+     * @return
+     * @throws IOException
+     */
+    synchronized boolean canWrite() throws IOException
+    {
+        if (owners.size() > 1)
+        {
+            return false;
+        }
+
+        if ((canRead()) && ((writer == Thread.currentThread()) || (writer == null)))
+        {
+            if (owners.isEmpty())
+            {
+                return true;
+            }
+
+            if (owners.containsKey(Thread.currentThread()))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Called by shapefileReader before a read is started and before an IOStream
+     * is openned.
+     *
+     * @throws IOException
+     */
+    public synchronized void lockRead() throws IOException
+    {
+        if (!canRead())
+        {
+            while ((writeLocks > 0) || (writer != null))
+            {
+                try
+                {
+                    wait();
+                } catch (InterruptedException e)
+                {
+                    throw(IOException) new IOException().initCause(e);
+                }
+            }
+        }
+
+        assertTrue("A write lock exists that is owned by another thread", canRead());
+
+        Thread current = Thread.currentThread();
+        Owner  owner   = (Owner) owners.get(current);
+
+        if (owner != null)
+        {
+            owner.timesLocked++;
+        } else
+        {
+            owner = new Owner(current);
+            owners.put(current, owner);
+        }
+
+        logger.debug("Start Read Lock:" + owner);
+    }
+
+    private void assertTrue(String message, boolean b)
+    {
+        if (!b)
+        {
+            throw new AssertionError(message);
+        }
+    }
+
+    /**
+     * Called by ShapefileReader after a read is complete and after the IOStream
+     * is closed.
+     */
+    public synchronized void unlockRead()
+    {
+        assertTrue("Current thread does not have a readLock", owners.containsKey(Thread.currentThread()));
+
+        Owner owner = (Owner) owners.get(Thread.currentThread());
+
+        assertTrue("Current thread has " + owner.timesLocked + "negative number of locks", owner.timesLocked > 0);
+        owner.timesLocked--;
+
+        if (owner.timesLocked == 0)
+        {
+            owners.remove(Thread.currentThread());
+        }
+
+        notifyAll();
+        logger.debug("unlock Read:" + owner);
+    }
+
+    /**
+     * Called by ShapefileDataStore before a write is started and before an
+     * IOStream is openned.
+     *
+     * @throws IOException
+     */
+    public synchronized void lockWrite() throws IOException
+    {
+        Thread currentThread = Thread.currentThread();
+
+        if (writer == null)
+        {
+            writer = currentThread;
+        }
+
+        while (!canWrite())
+        {
+            try
+            {
+                wait();
+            } catch (InterruptedException e)
+            {
+                throw(IOException) new IOException().initCause(e);
+            }
+
+            if (writer == null)
+            {
+                writer = currentThread;
+            }
+        }
+
+        if (writer == null)
+        {
+            writer = currentThread;
+        }
+
+        assertTrue("The current thread is not the writer", writer == currentThread);
+        assertTrue("There are read locks not belonging to the current thread.", canRead());
+        writeLocks++;
+        logger.debug(currentThread.getName() + " is getting write lock:" + writeLocks);
+    }
+
+    /**
+     * default visibility for tests
+     */
+    synchronized int getReadLocks(Thread thread)
+    {
+        Owner owner = (Owner) owners.get(thread);
+
+        if (owner == null)
+        {
+            return -1;
+        }
+
+        return owner.timesLocked;
+    }
+
+    public synchronized void unlockWrite()
+    {
+        if (writeLocks > 0)
+        {
+            assertTrue("current thread does not own the write lock", writer == Thread.currentThread());
+            assertTrue("writeLock has already been unlocked", writeLocks > 0);
+            writeLocks--;
+
+            if (writeLocks == 0)
+            {
+                writer = null;
+            }
+        }
+
+        logger.debug("unlock write:" + Thread.currentThread().getName());
+        notifyAll();
+    }
+
+    /**
+     * default visibility for tests
+     */
+    synchronized boolean ownWriteLock(Thread thread)
+    {
+        return (writer == thread) && (writeLocks > 0);
+    }
+
+    private class Owner
+    {
+        final Thread owner;
+        int          timesLocked;
+
+        Owner(Thread owner)
+        {
+            this.owner  = owner;
+            timesLocked = 1;
+        }
+
+        public String toString()
+        {
+            return owner.getName() + " has " + timesLocked + " locks";
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
new file mode 100644
index 0000000..4f31770
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
@@ -0,0 +1,114 @@
+package com.ximple.io.dgn7;
+
+/*
+ *    GeoTools - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
+ *
+ *    This library is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU Lesser General Public
+ *    License as published by the Free Software Foundation; either
+ *    version 2.1 of the License, or (at your option) any later version.
+ *
+ *    This library is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *    Lesser General Public License for more details.
+ */
+
+// J2SE dependencies
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Utility class for managing memory mapped buffers.
+ *
+ * @since 2.0
+ * @source $URL$
+ * @version $Id$
+ * @author Andres Aimes
+ */
+public class NIOUtilities {
+    /**
+     * {@code true} if a warning has already been logged.
+     */
+    private static boolean warned = false;
+
+    /**
+     * Do not allows instantiation of this class.
+     *
+     * @todo This constructor will become private when {@code NIOBufferUtils}
+     *       will have been removed.
+     */
+    protected NIOUtilities() {
+    }
+
+    /**
+     * Really closes a {@code MappedByteBuffer} without the need to wait for garbage
+     * collection. Any problems with closing a buffer on Windows (the problem child in this
+     * case) will be logged as {@code SEVERE} to the logger of the package name. To
+     * force logging of errors, set the System property "org.geotools.io.debugBuffer" to "true".
+     *
+     * @param  buffer The buffer to close.
+     * @return true if the operation was successful, false otherwise.
+     *
+     * @see java.nio.MappedByteBuffer
+     */
+    public static boolean clean(final ByteBuffer buffer) {
+        if (buffer == null || ! buffer.isDirect() ) {
+            return false;
+        }
+        Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                Boolean success = Boolean.FALSE;
+                try {
+                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", (Class[])null);
+                    getCleanerMethod.setAccessible(true);
+                    Object cleaner = getCleanerMethod.invoke(buffer,  (Object[])null);
+                    Method clean = cleaner.getClass().getMethod("clean", (Class[])null);
+                    clean.invoke(cleaner, (Object[])null);
+                    success = Boolean.TRUE;
+                } catch (Exception e) {
+                    // This really is a show stopper on windows
+                    if (isLoggable()) {
+                        log(e, buffer);
+                    }
+                }
+                return success;
+            }
+        });
+
+        return b.booleanValue();
+    }
+
+    /**
+     * Check if a warning message should be logged.
+     */
+    private static synchronized boolean isLoggable() {
+        try {
+            return !warned && (
+                    System.getProperty("org.geotools.io.debugBuffer", "false").equalsIgnoreCase("true") ||
+                    System.getProperty("os.name").indexOf("Windows") >= 0 );
+        } catch (SecurityException exception) {
+            // The utilities may be running in an Applet, in which case we
+            // can't read properties. Assumes we are not in debugging mode.
+            return false;
+        }
+    }
+
+    /**
+     * Log a warning message.
+     */
+    private static synchronized void log(final Exception e, final ByteBuffer buffer) {
+        warned = true;
+        String message = "Error attempting to close a mapped byte buffer : " + buffer.getClass().getName()
+                       + "\n JVM : " + System.getProperty("java.version")
+                       + ' '         + System.getProperty("java.vendor");
+        Logger.getLogger("org.geotools.io").log(Level.SEVERE, message, e);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java
new file mode 100644
index 0000000..729413c
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java
@@ -0,0 +1,56 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LinearRing;
+
+/**
+ * ShapeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 03:08:43
+ */
+public class ShapeElement extends LineStringElement implements GeometryConverter
+{
+    public ShapeElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        LinearRing ring = factory.createLinearRing(this.getVertices());
+
+        return ring;
+
+        // return factory.createPolygon(ring, null);
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.SHAPE);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new ShapeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java
new file mode 100644
index 0000000..db92597
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java
@@ -0,0 +1,45 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+/**
+ * StreamLogging
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 10:31:08
+ */
+public class StreamLogging
+{
+    private static final Logger LOGGER = LogManager.getLogger("com.ximple.io.dgn7");
+    private String              name;
+    private int                 open = 0;
+
+    /**
+     * The name that will appear in the debug message
+     *
+     * @param name
+     */
+    public StreamLogging(String name)
+    {
+        this.name = name;
+    }
+
+    /**
+     * Call when reader or writer is opened
+     */
+    public synchronized void open()
+    {
+        open++;
+        LOGGER.debug(name + " has been opened. Number open: " + open);
+    }
+
+    public synchronized void close()
+    {
+        open--;
+        LOGGER.debug(name + " has been closed. Number open: " + open);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java
new file mode 100644
index 0000000..fe4de4a
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java
@@ -0,0 +1,90 @@
+package com.ximple.io.dgn7;
+
+/**
+ * TcbElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 05:03:46
+ */
+public class TcbElement extends Element
+{
+    public TcbElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public boolean is2D()
+    {
+        int dimension = (int) (raw[607] & 0x00000004);
+
+        if (dimension == 0)
+        {
+            return true;
+        } else
+        {
+            return false;
+        }
+    }
+
+    public String getMasterUnitName()
+    {
+        byte[] master = new byte[1];
+
+        master[0] = (byte) (raw[560] & 0x00ff);
+        java.nio.charset.Charset.forName("US-ASCII");
+
+        // ASCIIEncoding encode = new ASCIIEncoding();
+        StringBuffer sb = new StringBuffer();
+
+        sb.append((char) master[0]);
+
+        // return encode.GetString(master);
+        return sb.toString();
+    }
+
+    public String getSubUnitName()
+    {
+        byte[] sub = new byte[2];
+
+        sub[0] = (byte) (raw[561] & 0x00ff);
+        sub[1] = (byte) (raw[561] >> 8 & 0x00ff);
+
+        StringBuffer sb = new StringBuffer();
+
+        sb.append((char) sub[0]);
+        sb.append((char) sub[0]);
+
+        return sb.toString();
+    }
+
+    public int getGraphicGroup()
+    {
+        return (int) (raw[594] & 0x0000ffff);
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.TCB);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new TcbElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
new file mode 100644
index 0000000..3fc5fca
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
@@ -0,0 +1,296 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * TextElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:45:29
+ */
+public class TextElement extends Element implements GeometryConverter
+{
+    public static final int ED_CENTERJUSTIFICATION = 0;
+
+//  Enter data field center justification
+    public static final int ED_LEFTJUSTIFICATION = 0;
+
+//  Enter data field left justification
+    public static final int ED_RIGHTJUSTIFICATION = 0;
+
+//  Enter data field right justification
+    public static final int TXTJUST_CB = 0;
+
+//  Center/bottom text justification.
+    public static final int TXTJUST_CC = 0;
+
+//  Center/center text justification.
+    public static final int TXTJUST_CT = 0;
+
+//  Center/top text justification.
+    public static final int TXTJUST_LB = 0;
+
+//  Left/bottom text justification.
+    public static final int TXTJUST_LC = 0;
+
+//  Left/center text justification.
+    public static final int TXTJUST_LT = 0;
+
+//  Left/top text justification.
+    public static final int TXTJUST_RB = 0;
+
+//  Right/bottom text justification.
+    public static final int TXTJUST_RC = 0;
+
+//  Right/center text justification.
+    public static final int TXTJUST_RT = 0;
+
+    public TextElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public Coordinate getOrigin()
+    {
+        int x = (int) (raw[25] << 16 & 0xffff0000);
+
+        x += raw[26] & 0x0000ffff;
+
+        double dx = Utility.ConverUnitToCoord(x);
+        int    y  = (int) (raw[27] << 16 & 0xffff0000);
+
+        y += raw[28] & 0x0000ffff;
+
+        double dy = Utility.ConverUnitToCoord(y);
+
+        return new Coordinate(dx, dy);
+    }
+
+    public Coordinate getUserOrigin()
+    {
+        Coordinate origin = getOrigin();
+        double     x      = origin.x;
+        double     weight = getUserSetWeight();
+        double     height = getUserSetHeight();
+        double     angle  = Utility.ConverRotationToRadian(getRotationAngle());
+
+        x += weight * Math.cos(angle) - height * Math.sin(angle);
+
+        double y = origin.y;
+
+        y += weight * Math.cos(angle) - height * Math.sin(angle);
+
+        return new Coordinate(x, y);
+    }
+
+    private double getUserSetWeight()
+    {
+        int      just   = getJustification();
+        Envelope range  = getRange();
+        double   weight = (range.getWidth());
+
+        switch (just)
+        {
+        case 0 :
+        case 1 :
+        case 2 :
+            weight = 0;
+
+            break;
+
+        case 6 :
+        case 7 :
+        case 8 :
+            weight = weight / 2;
+
+            break;
+
+        case 12 :
+        case 13 :
+        case 14 :
+            break;
+        }
+
+        return weight;
+    }
+
+    private double getUserSetHeight()
+    {
+        int    just   = getJustification();
+        double height = getTextHeight();
+
+        switch (just)
+        {
+        case 2 :
+        case 8 :
+        case 14 :    // bottom
+            height = 0;
+
+            break;
+
+        case 1 :
+        case 7 :
+        case 13 :    // center
+            height = height / 2;
+
+            break;
+
+        case 0 :
+        case 6 :
+        case 12 :    // height
+            break;
+        }
+
+        return height;
+    }
+
+    public int getFontIndex()
+    {
+        return (int) raw[18] & 0x00000000ff;
+    }
+
+    public boolean hasSlant()
+    {
+        return true;
+    }
+
+    public boolean hasUnderline()
+    {
+        return true;
+    }
+
+    public boolean isFixedWidthSpacing()
+    {
+        return true;
+    }
+
+    public boolean isPlanar()
+    {
+        return true;
+    }
+
+    public boolean isVertical()
+    {
+        return true;
+    }
+
+    public double getTextHeight()
+    {
+        int height = (int) ((raw[21] << 16) & 0xffff0000);
+
+        height += raw[22] & 0x0000ffff;
+
+        return Utility.ConverIntToDouble(height);
+    }
+
+    public double getTextWidth()
+    {
+        int length = (int) (raw[19] << 16 & 0xffff0000);
+
+        length += raw[20] & 0x0000ffff;
+
+        return Utility.ConverIntToDouble(length);
+    }
+
+    public int getJustification()
+    {
+        return (int) ((raw[18] >>> 8) & 0x00000000ff);
+    }
+
+    public double getRotationAngle()
+    {
+        int totation = (int) ((raw[23] << 16) & 0xffff0000);
+
+        totation += raw[24] & 0x0000ffff;
+
+        return Utility.ConverIntToRotation(totation);
+    }
+
+    public boolean isChinese()
+    {
+        int isChinese = raw[30] & 0x0000ffff;
+
+        if (isChinese == 0xfdff)
+        {
+            return true;
+        } else
+        {
+            return false;
+        }
+    }
+
+    public int getTextLength()
+    {
+        int num = raw[29];
+
+        if (isChinese())
+        {
+            num = (num / 2) - 1;
+        }
+
+        return num;
+    }
+
+    public String getText()
+    {
+        StringBuffer val = new StringBuffer();
+        char[]       temp;
+        int          num = getTextLength();
+
+        if (!isChinese())
+        {
+            temp = new char[num];
+
+            for (int i = 0; i < temp.length; i++)
+            {
+                if ((i % 2) == 0)
+                {
+                    temp[i] = (char) (raw[30 + (int) (i / 2)] & (short) 0x00ff);
+                } else
+                {
+                    temp[i] = (char) ((raw[30 + (int) (i / 2)] >> 8) & (short) 0x00ff);
+                }
+
+                val.append(temp[i]);
+            }
+        }
+
+        return val.toString();
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        return factory.createPoint(getUserOrigin());
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.TEXT);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new TextElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
new file mode 100644
index 0000000..7991b77
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
@@ -0,0 +1,291 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * TextNodeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 04:02:58
+ */
+public class TextNodeElement extends Element implements ComplexElement, GeometryConverter
+{
+    ArrayList list = new ArrayList();
+
+    public TextNodeElement(short[] raw)
+    {
+        super(raw);
+    }
+
+    public int size()
+    {
+        return list.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return list.isEmpty();
+    }
+
+    public boolean contains(Object o)
+    {
+        return list.contains(o);
+    }
+
+    public Iterator iterator()
+    {
+        return list.iterator();
+    }
+
+    public Object[] toArray()
+    {
+        return list.toArray();
+    }
+
+    public boolean add(Object o)
+    {
+        return list.add(o);
+    }
+
+    public boolean remove(Object o)
+    {
+        return list.remove(o);
+    }
+
+    public boolean addAll(Collection c)
+    {
+        return list.addAll(c);
+    }
+
+    public boolean addAll(int index, Collection c)
+    {
+        return list.addAll(index, c);
+    }
+
+    public void clear()
+    {
+        list.clear();
+    }
+
+    public Object get(int index)
+    {
+        return list.get(index);
+    }
+
+    public Object set(int index, Object element)
+    {
+        return list.set(index, element);
+    }
+
+    public void add(int index, Object element)
+    {
+        list.add(index, element);
+    }
+
+    public Object remove(int index)
+    {
+        return list.remove(index);
+    }
+
+    public int indexOf(Object o)
+    {
+        return list.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o)
+    {
+        return list.lastIndexOf(o);
+    }
+
+    public ListIterator listIterator()
+    {
+        return list.listIterator();
+    }
+
+    public ListIterator listIterator(int index)
+    {
+        return list.listIterator(index);
+    }
+
+    public List subList(int fromIndex, int toIndex)
+    {
+        return list.subList(fromIndex, toIndex);
+    }
+
+    public boolean retainAll(Collection c)
+    {
+        return list.retainAll(c);
+    }
+
+    public boolean removeAll(Collection c)
+    {
+        return list.removeAll(c);
+    }
+
+    public boolean containsAll(Collection c)
+    {
+        return list.containsAll(c);
+    }
+
+    public Object[] toArray(Object[] a)
+    {
+        return list.toArray(a);
+    }
+
+    public String[] getTextArray()
+    {
+        ArrayList list = new ArrayList();
+
+        for (ListIterator it = listIterator(); it.hasNext(); )
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof TextElement)
+            {
+                list.add(((TextElement) element).getText());
+            }
+        }
+
+        return (String[]) list.toArray(new String[list.size()]);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        /*
+         * CoordinateList coords = new CoordinateList();
+         * for (ListIterator it = listIterator(); it.hasNext(); )
+         * {
+         *   Element element = (Element) it.next();
+         *   if (element instanceof TextElement)
+         *   {
+         *       coords.add(((TextElement) element).getUserOrigin());
+         *   }
+         * }
+         */
+        return factory.createPoint(getOrigin());
+
+        // return factory.createMultiPoint(coords.toCoordinateArray());
+    }
+
+    public int getNumString()
+    {
+        return (int) (raw[19] & 0x0000ffff);
+    }
+
+    public int getNodeNumber()
+    {
+        return (int) (raw[20] & 0x0000ffff);
+    }
+
+    public int getMaxLength()
+    {
+        return (int) (raw[21] & 0x00ff);
+    }
+
+    public int getMaxUsed()
+    {
+        return (int) ((raw[21] >> 8) & 0x00ff);
+    }
+
+    public int getJustification()
+    {
+        return (int) ((raw[22] >> 8) & 0x00ff);
+    }
+
+    public int getFontIndex()
+    {
+        return (int) (raw[22] & 0x00ff);
+    }
+
+    public double getLineSpacing()
+    {
+        int lineSpace;
+
+        lineSpace = (int) ((raw[23] << 16) & 0xffff0000);
+        lineSpace += (raw[24] & 0x0000ffff);
+
+        return lineSpace;
+    }
+
+    public double getTextNodeLength()
+    {
+        int lengthMult = -1;
+
+        lengthMult = (int) ((raw[25] << 16) & 0xffff0000);
+        lengthMult += (raw[26] & 0x0000ffff);
+
+        return Utility.ConverIntToDouble(lengthMult);
+    }
+
+    public double getTextNodeHeight()
+    {
+        int heightMult = -1;
+
+        heightMult = (int) ((raw[27] << 16) & 0xffff0000);
+        heightMult += (raw[28] & 0x0000ffff);
+
+        return Utility.ConverIntToDouble(heightMult);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (int) (raw[29] << 16 & 0xffff0000);
+
+        rotation += raw[30];
+
+        return Utility.ConverIntToRotation(rotation);
+    }
+
+    public Coordinate getOrigin()
+    {
+        int x = (int) ((raw[31] << 16) & 0xffff0000);
+
+        x += raw[32] & 0x0000ffff;
+
+        // return Utility.ConvertFromDGN(x);
+        double dx = Utility.ConverUnitToCoord(x);
+        int    y  = (int) ((raw[33] << 16) & 0xffff0000);
+
+        y += (raw[34] & 0x0000ffff);
+
+        double dy = Utility.ConverUnitToCoord(y);
+
+        return new Coordinate(dx, dy);
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.TEXTNODE);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(short[] raw)
+        {
+            return new TextNodeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java
new file mode 100644
index 0000000..5f0a9fe
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java
@@ -0,0 +1,34 @@
+package com.ximple.io.dgn7;
+
+/**
+ * UserAttributeData
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 02:29:29
+ */
+public class UserAttributeData
+{
+    protected short[] _src;
+
+    public UserAttributeData(short id, int attributeCount)
+    {
+        _src    = new short[attributeCount];
+        _src[0] = id;
+    }
+
+    public UserAttributeData(short[] src)
+    {
+        _src = src;
+    }
+
+    public short getID()
+    {
+        return _src[0];
+    }
+
+    public void setID(short value)
+    {
+        _src[0] = value;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java
new file mode 100644
index 0000000..82a4c67
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java
@@ -0,0 +1,238 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * Utility
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 01:33:00
+ */
+public final class Utility
+{
+    public static double ConverIntToDouble(int src)
+    {
+        double newVal = (double) ((long) ((src * 6) / 1000.0 + 0.5)) / 1000.0;    // ?[0.5?O?�X?F?|��????J
+
+        return newVal;
+    }
+
+    public static int ConverDoubleToInt(double src)
+    {
+        int newVal = (int) (src / 6 * 1000000.0);
+
+        return newVal;
+    }
+
+    public static int ConvertFromDGN(int aValue)
+    {
+        int newVal = 0;
+
+        newVal = (((aValue ^ 0x00008000) << 16) & 0xffff0000);
+        newVal += (aValue >>> 16) & 0x0000ffff;
+
+        return newVal;
+    }
+
+    public static int ConverToDGN(int aValue)
+    {
+        int newVal = 0;
+
+        newVal = (aValue << 16 & 0xffff0000);
+        newVal += (((aValue ^ 0x80000000) >>> 16) & 0x0000ffff);
+
+        return newVal;
+    }
+
+    public static double ConverIntToRotation(int aValue)
+    {
+        double newVal = aValue / 360000.0;
+
+        if (newVal > 0)
+        {
+            newVal = (int) (newVal + 0.5);
+        } else
+        {
+            newVal = (int) (newVal - 0.5);
+        }
+
+        return newVal;
+    }
+
+    public static int ConverRotatioToInt(double aValue)
+    {
+        int newVal = (int) (aValue * 360000.0);
+
+        return newVal;
+    }
+
+    public static double ConverRotationToRadian(double aValue)
+    {
+        double newVal = aValue * Math.PI / 180;
+
+        return newVal;
+    }
+
+    public static double ConverUnitToCoord(int aValue)
+    {
+        double newVal = 0;
+
+        newVal = aValue / 1000.0;
+        newVal += 2147483.648;    // 2147483.648 = 2 ^ 31
+
+        return newVal;
+    }
+
+    public static int ConverCoordToUnit(double aValue)
+    {
+        double newVal = aValue;
+
+        newVal -= 2147483.648;
+        newVal = newVal * 1000.0;
+
+        return (int) newVal;
+    }
+
+    public static Envelope ConverUnitToCoord(Envelope range)
+    {
+        if (range == null)
+        {
+            return null;
+        }
+
+        Envelope newRange = new Envelope(ConverUnitToCoord((int) range.getMinX()), ConverUnitToCoord((int) range.getMaxX()),
+                                         ConverUnitToCoord((int) range.getMinY()), ConverUnitToCoord((int) range.getMaxY()));
+
+        return newRange;
+    }
+
+    public static Envelope ConverCoordToUnit(Envelope range)
+    {
+        if (range == null)
+        {
+            return null;
+        }
+
+        Envelope newRange = new Envelope(ConverCoordToUnit(range.getMinX()), ConverCoordToUnit(range.getMaxX()),
+                                         ConverCoordToUnit(range.getMinY()), ConverCoordToUnit(range.getMaxY()));
+
+        return newRange;
+    }
+
+    public static double DGNToIEEEDouble(short[] src)
+    {
+        int[] tmp = new int[2];
+        long  des = 0;
+        int   sign;
+        int   exponent;
+        int   rndbits;
+
+        if (src == null)
+        {
+            throw new RuntimeException("Source short array is null");
+        }
+
+        tmp[0]   = (int) ((src[0] << 16) & 0xffff0000) | (src[1] & 0x0000ffff);    // �X?????
+        tmp[1]   = (int) ((src[2] << 16) & 0xffff0000) | (src[3] & 0x0000ffff);    // ��C????
+        sign     = (int) (tmp[0] & 0x80000000);
+        exponent = (tmp[0] >>> 23) & 0x000000ff;
+
+        if (exponent != 0)
+        {
+            exponent = exponent - 129 + 1023;
+        }
+
+        rndbits = tmp[1] & 0x00000007;
+        tmp[1]  = tmp[1] >>> 3;
+        tmp[1]  = (tmp[1] & 0x1fffffff) | (tmp[0] << 29);
+
+        if (rndbits != 0)
+        {
+            tmp[1] = tmp[1] | 0x00000001;
+        }
+
+        tmp[0] = (tmp[0] >>> 3) & 0x000fffff;
+        tmp[0] = tmp[0] | (exponent << 20) | sign;
+        des    = (((long) tmp[0] << 32));
+        des    = des | (long) tmp[1] & 0x00000000ffffffff;
+
+        return Double.longBitsToDouble(des);
+    }
+
+    public static short[] IEEEDoubleToDGN(double src)
+    {
+        long newVal = Double.doubleToLongBits(src);
+
+        // uint[]   tmp = new int[ 2 ];
+        // ushort[] des = new short[ 4 ];
+        int[]   tmp = new int[2];
+        short[] des = new short[4];
+        int     sign;
+        int     exponent;
+
+        tmp[0] = (int) ((newVal >>> 32) & 0x0ffffffff);
+        tmp[1] = (int) (newVal & 0x0ffffffff);
+
+        // sign = ( int ) ( ( uint ) tmp[ 0 ] & 0x80000000 );
+        sign     = (int) tmp[0] & 0x80000000;
+        exponent = (tmp[0] >>> 20) & 0x07ff;
+
+        if (exponent != 0)
+        {
+            exponent = exponent - 1023 + 129;
+        }
+
+        if (exponent > 255)
+        {
+            if (sign != 0)
+            {
+                des[0] = -1;
+            } else
+            {
+                des[0] = 0x7fff;
+            }
+
+            des[1] = -1;
+            des[2] = -1;
+            des[3] = -1;
+
+            return des;
+        } else if ((exponent < 0) || ((exponent == 0) && (sign == 0)))
+        {
+            des[0] = 0x0;
+            des[1] = 0x0;
+            des[2] = 0x0;
+            des[3] = 0x0;
+
+            return des;
+        } else
+        {
+            tmp[0] = (tmp[0] << 3) | (tmp[1] >> 29);
+            tmp[0] = tmp[0] & 0x007fffff;
+            tmp[0] = tmp[0] | (exponent << 23) | sign;
+
+            // changed by phil 07/05/2004
+            // tmp[ 1 ] = tmp[ 1 ] >> 3;
+            tmp[1] = tmp[1] << 3;
+        }
+
+        des[0] = (short) ((tmp[0] >>> 16) & 0x0000ffff);
+        des[1] = (short) (tmp[0] & 0x0000ffff);
+        des[2] = (short) ((tmp[1] >>> 16) & 0x0000ffff);
+        des[3] = (short) (tmp[1] & 0x0000ffff);
+
+        return des;
+    }
+
+    public static double getLength(double x1, double y1, double x2, double y2)
+    {
+        double dx     = x1 - x2;
+        double dy     = y1 - y2;
+        double length = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
+
+        return length;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java
new file mode 100644
index 0000000..de4c574
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/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.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/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java
new file mode 100644
index 0000000..0ab0b93
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java
@@ -0,0 +1,125 @@
+package com.ximple.io.dgn7;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.ximple.util.PrintfFormat;
+import oracle.jdbc.OracleConnection;
+
+/**
+ * Dgn7OracleReaderTest
+ * User: Ulysses
+ * Date: 2007/10/24
+ * Time: �W�� 10:49:54
+ */
+public class Dgn7OracleReaderTest
+{
+    @BeforeTest
+    public void setUp()
+    {
+
+    }
+
+    @Test
+    public void testOracleReader() throws SQLException, IOException
+    {
+        OracleConnection connection = OracleTarget.getInstance().getOracleConnection();
+        // String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" ORDER BY ROWID";
+        String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" WHERE TAG_SFSC=106 ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchSrcStmtFmt);
+        String srcschema = "SPATIALDB";
+        String srctable = "IGSET_1";
+        String fetchSrcStmt = spf.sprintf(new Object[]{srcschema, srctable});
+
+        Dgn7OracleReader reader = new Dgn7OracleReader(fetchSrcStmt, "IGDSELM", connection);
+        int count = 0;
+        while (reader.hasNext())
+        {
+            Element element = reader.next();
+
+            if (element instanceof ComplexChainElement)
+            {
+                ComplexChainElement complexChain = (ComplexChainElement) element;
+                FrammeAttributeData frammeLinkage = null;
+
+                List<UserAttributeData> attrs = complexChain.getUserAttributeData();
+                for (int k = 0; k < attrs.size(); k++)
+                {
+                    UserAttributeData userAttr = attrs.get(k);
+                    if (userAttr instanceof FrammeAttributeData)
+                    {
+                        frammeLinkage = (FrammeAttributeData) userAttr;
+                        break;
+                    }
+                }
+
+                System.out.print("complexChain:");
+                if (frammeLinkage != null)
+                    System.out.print(":FSC-" + frammeLinkage.getFsc() +
+                                       ":UFID-" + frammeLinkage.getUfid() +
+                                       ":COMP-" + frammeLinkage.getComponentID());
+                else
+                    System.out.print("Linkage is null");
+
+                for (int i = 0; i < complexChain.size(); i++)
+                {
+                    Element elm = (Element) complexChain.get(i);
+                    if (elm instanceof LineStringElement)
+                    {
+                        LineStringElement lineStringElement = (LineStringElement) elm;
+                        int size = lineStringElement.getVerticeSize();
+                        System.out.print("size=" + size + ":");
+                        Coordinate[] coords = lineStringElement.getVertices();
+                        for (int j = 0; j < coords.length; j++)
+                            System.out.print("[" + j + "]" + coords[j].toString());
+                    }
+                }
+
+                System.out.println();
+            } else if (element instanceof TextNodeElement)
+            {
+                TextNodeElement textNode = (TextNodeElement) element;
+
+                FrammeAttributeData frammeLinkage = null;
+
+                List<UserAttributeData> attrs = textNode.getUserAttributeData();
+                for (int k = 0; k < attrs.size(); k++)
+                {
+                    UserAttributeData userAttr = attrs.get(k);
+                    if (userAttr instanceof FrammeAttributeData)
+                    {
+                        frammeLinkage = (FrammeAttributeData) userAttr;
+                        break;
+                    }
+                }
+
+                Coordinate coord = textNode.getOrigin();
+                System.out.print("TextNode:origin=" + coord.toString());
+                if (frammeLinkage != null)
+                    System.out.print(":FSC-" + frammeLinkage.getFsc() +
+                                       ":UFID-" + frammeLinkage.getUfid() +
+                                       ":COMP-" + frammeLinkage.getComponentID());
+                else
+                    System.out.print("Linkage is null");
+                for (int i = 0; i < textNode.size(); i++)
+                {
+                    Element elm = (Element) textNode.get(i);
+                    if (elm instanceof TextElement)
+                    {
+                        TextElement textElm = (TextElement) elm;
+                        System.out.print("---");
+                        String text = textElm.getText();
+                        System.out.print("'" + text + "'");
+                    }
+                }
+                System.out.println();
+            }
+        }
+    }
+
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java
new file mode 100644
index 0000000..cc1ed35
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java
@@ -0,0 +1,180 @@
+package com.ximple.io.dgn7;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.geotools.TestData;
+
+/**
+ * Dgn7TextElementReaderTest
+ * User: Ulysses
+ * Date: 2008/1/10
+ * Time: �W�� 12:19:14
+ */
+public class Dgn7TextElementReaderTest
+{
+    private final static Logger logger = Logger.getLogger(Dgn7fileReaderTest.class);
+
+    private final static String testFilePathCreated = "demo.dgn";
+    private final static String testFilePathExist = "HV88491-1.dgn";
+    private final static String testFilePathPostComplete = "HV88494_0.dgn";
+
+    private FileInputStream _fs;
+
+    @BeforeTest
+    public void setUp() throws FileNotFoundException
+    {
+    }
+
+    @Test
+    public void testRead() throws Dgn7fileException, IOException
+    {
+        File dataFile = TestData.file(this, testFilePathCreated);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathCreated);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+
+        dataFile = TestData.file(this, testFilePathExist);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathExist);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+
+        dataFile = TestData.file(this, testFilePathPostComplete);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathPostComplete);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+    }
+
+    public void dumpElements(FileChannel fc) throws Dgn7fileException, IOException
+    {
+        Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    if (lastComplex != null)
+                    {
+                        // @todo add process in here
+                        lastComplex = null;
+                    }
+
+                    // @todo add process in here
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        // @todo add process in here
+                        lastComplex = element;
+                    }
+                }
+
+                if (element.getElementType().isComplexElement())
+                {
+                    if (element instanceof ComplexChainElement)
+                    {
+                        ComplexChainElement complexChain = (ComplexChainElement) element;
+                        int size = complexChain.size();
+                        for (Object aComplexChain : complexChain)
+                        {
+                            Element subElement = (Element) aComplexChain;
+                            subElement.getType();
+                        }
+                    }
+
+                    if (element instanceof ComplexShapeElement)
+                    {
+                        ComplexShapeElement complexShape = (ComplexShapeElement) element;
+                    }
+
+                    if (element instanceof TextNodeElement)
+                    {
+                        TextNodeElement textNode = (TextNodeElement) element;
+                        int size = textNode.size();
+                        for (int i = 0; i < size; i++)
+                        {
+                            Element subElement = (Element) textNode.get(i);
+                            subElement.getElementType();
+                        }
+                    }
+
+                } else
+                {
+                    boolean hasLinkage = false;
+                    if (element instanceof TextElement)
+                    {
+                        TextElement textElm = (TextElement) element;
+                        List<UserAttributeData> usrData = textElm.getUserAttributeData();
+                        Iterator<UserAttributeData> it = usrData.iterator();
+                        while (it.hasNext())
+                        {
+                            UserAttributeData attr = it.next();
+                            if (attr instanceof FrammeAttributeData)
+                            {
+                                hasLinkage = true;
+                                System.out.println("------------------------------------------");
+                                System.out.println("FSC=" + ((FrammeAttributeData) attr).getFsc() + ":" +
+                                        ((FrammeAttributeData) attr).getUfid());
+                            }
+                        }
+
+                        if (hasLinkage)
+                        {
+                            System.out.println("Text.Font=" + textElm.getFontIndex());
+                            System.out.println("Text.Just=" + textElm.getJustification());
+                            System.out.println("usrData.len=" + usrData.size());
+                            System.out.println("text=" + textElm.getText());
+                            System.out.println("Origin=" + textElm.getOrigin());
+                            System.out.println("UserOrigin=" + textElm.getUserOrigin());
+                        }
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.info("ElementRecord Count=" + count);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java
new file mode 100644
index 0000000..314427c
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java
@@ -0,0 +1,119 @@
+package com.ximple.io.dgn7;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import org.apache.log4j.Logger;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.geotools.TestData;
+
+/**
+ * Dgn7fileReaderTest
+ * User: Ulysses
+ * Date: 2007/10/24
+ * Time: �W�� 01:43:41
+ * To change this template use File | Settings | File Templates.
+ */
+public class Dgn7fileReaderTest
+{
+    private final static Logger logger = Logger.getLogger(Dgn7fileReaderTest.class);
+
+    // private final static String testFilePath = "test-data\\testHV.dgn";
+    private final static String testFilePath = "testHV.dgn";
+    private FileInputStream _fs;
+
+    @BeforeTest
+    public void setUp() throws IOException
+    {
+        File dataFile = TestData.file(this, testFilePath);
+        if (!dataFile.exists())
+        {
+            return;
+        }
+
+        _fs = new FileInputStream(dataFile);
+    }
+
+    @Test
+    public void testRead() throws Dgn7fileException, IOException
+    {
+        if (_fs == null) return;
+        FileChannel fc = _fs.getChannel();
+        Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    if (lastComplex != null)
+                    {
+                        // @todo add process in here
+                        lastComplex = null;
+                    }
+
+                    // @todo add process in here
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        // @todo add process in here
+                        lastComplex = element;
+                    }
+                }
+
+                if (element.getElementType().isComplexElement())
+                {
+                    if (element instanceof ComplexChainElement)
+                    {
+                        ComplexChainElement complexChain = (ComplexChainElement) element;
+                        int size = complexChain.size();
+                        for (Object aComplexChain : complexChain)
+                        {
+                            Element subElement = (Element) aComplexChain;
+                            subElement.getType();
+                        }
+                    }
+
+                    if (element instanceof ComplexShapeElement)
+                    {
+                        ComplexShapeElement complexShape = (ComplexShapeElement) element;
+                    }
+
+                    if (element instanceof TextNodeElement)
+                    {
+                        TextNodeElement textNode = (TextNodeElement) element;
+                        int size = textNode.size();
+                        for (int i = 0; i < size; i++)
+                        {
+                            Element subElement = (Element) textNode.get(i);
+                            subElement.getElementType();
+                        }
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.info("ElementRecord Count=" + count);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java
new file mode 100644
index 0000000..1279340
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java
@@ -0,0 +1,162 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.util.Assert;
+import oracle.jdbc.OracleConnection;
+
+/**
+ * OracleTarget
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: ?U?? 03:12:43
+ * To change this template use File | Settings | File Templates.
+ */
+public class OracleTarget
+{
+    private static final Logger logger          = Logger.getLogger(OracleTarget.class);
+    private static OracleTarget _instance       = null;
+    private static final String ORACLE_URL      = "jdbc:oracle:thin:@";
+    private static final String _propUsrKey     = "user";
+    private static final String _propPassKey    = "password";
+    private static String       _oracleHost     = "192.168.11.200";
+    private static String       _oracleInstance = "NNTPC";
+    private static String       _oraclePort     = "1521";
+
+    static
+    {
+        try
+        {
+            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
+        } catch (SQLException e)
+        {
+            Assert.shouldNeverReachHere(e.getMessage());
+        }
+    }
+
+    private OracleConnection oracleConnection = null;
+    private Properties       properties;
+
+    private OracleTarget()
+    {
+        properties = new Properties();
+        properties.put(_propUsrKey, "SPATIALDB");
+        properties.put(_propPassKey, "SPATIALDB000");
+    }
+
+    public static String getOracleHost()
+    {
+        return _oracleHost;
+    }
+
+    public static void setOracleHost(String oracleHost)
+    {
+        OracleTarget._oracleHost = oracleHost;
+    }
+
+    public static String getOracleInstance()
+    {
+        return _oracleInstance;
+    }
+
+    public static void setOracleInstance(String oracleInstance)
+    {
+        OracleTarget._oracleInstance = oracleInstance;
+    }
+
+    public static String getOraclePort()
+    {
+        return _oraclePort;
+    }
+
+    public static void setOraclePort(String oraclePort)
+    {
+        OracleTarget._oraclePort = oraclePort;
+    }
+
+    public static String getCurrentURL()
+    {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(ORACLE_URL);
+        builder.append(_oracleHost);
+        builder.append(":");
+        builder.append(_oraclePort);
+        builder.append(":");
+        builder.append(_oracleInstance);
+
+        return builder.toString();
+    }
+
+    public String getLoginPass()
+    {
+        return (String) properties.get(_propPassKey);
+    }
+
+    public void setLoginPass(String loginPass)
+    {
+        properties.put(_propPassKey, loginPass);
+    }
+
+    public String getLoginUsr()
+    {
+        return (String) properties.get(_propUsrKey);
+    }
+
+    public void setLoginUsr(String loginUsr)
+    {
+        properties.put(_propUsrKey, loginUsr);
+    }
+
+    public static OracleTarget getInstance()
+    {
+        if (_instance == null)
+        {
+            _instance = new OracleTarget();
+        }
+
+        return _instance;
+    }
+
+    public OracleConnection getOracleConnection()
+    {
+        try
+        {
+            if (oracleConnection == null)
+            {
+                oracleConnection = (OracleConnection) DriverManager.getConnection(getCurrentURL(), 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);
+        }
+    }
+}
+
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn
new file mode 100644
index 0000000..d749f68
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn
new file mode 100644
index 0000000..acbb8a2
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn
new file mode 100644
index 0000000..f29f86f
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn
new file mode 100644
index 0000000..4899d19
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn
new file mode 100644
index 0000000..05fb839
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-jobcarrier/pom.xml b/xdgnjobs/ximple-jobcarrier/pom.xml
new file mode 100644
index 0000000..c1c3505
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/pom.xml
@@ -0,0 +1,137 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.0.2</version>
+  </parent>
+
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-jobcarrier</artifactId>
+  <version>0.0.2</version>
+  <packaging>jar</packaging>
+  <name>ximple-jobcarrier</name>
+  <url>http://maven.apache.org</url>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/</url>
+  </scm>
+
+  <description>
+    Ximple Job Carrier for Quartz
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>quartz</artifactId>
+      <groupId>opensymphony</groupId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.transaction</groupId>
+      <artifactId>jta</artifactId>
+      <version>1.0.1B</version>
+    </dependency>
+
+    <!-- Ximple Library -->
+    <dependency>
+      <artifactId>ximple-dgnio</artifactId>
+      <groupId>com.ximple.eofms</groupId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+    <dependency>
+      <artifactId>ximple-spatialjob</artifactId>
+      <groupId>com.ximple.eofms</groupId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- ======================================================= -->
+      <!--     JAR packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <mainClass>com.ximple.eofms.XQuartzJobCarrier</mainClass>
+              <addClasspath>true</addClasspath>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+      <!-- ======================================================= -->
+      <!--     exec jar.                                           -->
+      <!-- ======================================================= -->
+      <plugin>
+        <!--
+           Use maven from the command line:
+             mvn exec:java -Dexec.mainClass="com.ximple.eofms.XQuartzJobCarrier"
+        -->
+        <artifactId>exec-maven-plugin</artifactId>
+        <groupId>org.codehaus.mojo</groupId>
+        <!--
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+        -->
+        <configuration>
+          <mainClass>com.ximple.eofms.XQuartzJobCarrier</mainClass>
+        </configuration>
+        <!--
+        <dependencies>
+          <dependency>
+             <groupId>com.ximple.eofms</groupId>
+             <artifactId>ximple-jobcarrier</artifactId>
+             <version>0.0.1</version>
+             <type>jar</type>
+           </dependency>
+        </dependencies>
+        -->
+      </plugin>
+    </plugins>
+    <resources>
+    </resources>
+  </build>
+
+</project>
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java b/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java
new file mode 100644
index 0000000..f2eef84
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java
@@ -0,0 +1,95 @@
+package com.ximple.eofms;
+
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerUtils;
+import org.quartz.impl.StdSchedulerFactory;
+
+import com.ximple.eofms.jobs.OracleConvertDgn2ShpJob;
+
+/**
+ * Hello world!
+ */
+public class XQuartzJobCarrier
+{
+    static Log logger = LogFactory.getLog(XQuartzJobCarrier.class);
+
+
+    public static void main(String[] args)
+    {
+        XQuartzJobCarrier instance = new XQuartzJobCarrier();
+        instance.startScheduler();
+    }
+
+    public void startScheduler()
+    {
+        Scheduler scheduler = null;
+        boolean shutdown = false;
+
+        try
+        {
+            // Get a Scheduler instance from the Factory
+            scheduler = StdSchedulerFactory.getDefaultScheduler();
+
+            // Start the scheduler
+            scheduler.start();
+            logger.info("Scheduler started at " + new Date());
+
+        } catch (SchedulerException ex)
+        {
+            // deal with any exceptions
+            logger.error(ex);
+            shutdown = true;
+        } catch (Throwable throwable)
+        {
+            logger.error(throwable.getMessage(), throwable);
+            shutdown = true;
+        }
+        if (shutdown)
+        {
+            try
+            {
+                scheduler.shutdown();
+            } catch (SchedulerException e)
+            {
+                logger.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    /*
+     * return an instance of the Scheduler from the factory
+     */
+    public Scheduler createScheduler() throws SchedulerException
+    {
+        return StdSchedulerFactory.getDefaultScheduler();
+    }
+
+    // Create and Schedule a ScanDirectoryJob with the Scheduler
+    private void scheduleJob(Scheduler scheduler) throws SchedulerException
+    {
+
+        // Create a JobDetail for the Job
+        JobDetail jobDetail = new JobDetail("ScanDirectory", Scheduler.DEFAULT_GROUP,
+                OracleConvertDgn2ShpJob.class);
+
+        // Configure the directory to scan
+        jobDetail.getJobDataMap().put("SCAN_DIR", "c:\\quartz-book\\input");
+
+        // Create a trigger that fires every 10 seconds, forever
+        Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);
+        trigger.setName("scanTrigger");
+        // Start the trigger firing from now
+        trigger.setStartTime(new Date());
+
+        // Associate the trigger with the job in the scheduler
+        scheduler.scheduleJob(jobDetail, trigger);
+    }
+
+}
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
new file mode 100644
index 0000000..b844eb1
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
@@ -0,0 +1,15 @@
+# Create stdout appender
+log4j.rootLogger=error, stdout
+
+# Configure the stdout appender to go to the Console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+
+# Configure stdout appender to use the PatternLayout
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# Pattern output the caller's filename and line #
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+
+# Print messages of level INFO or above for examples
+log4j.logger.org.cavaness.quartzbook=INFO
+log4j.logger.com.ximple.eofms=DEBUG
\ No newline at end of file
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties
new file mode 100644
index 0000000..be35e19
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties
@@ -0,0 +1,28 @@
+#===============================================================
+#Configure Main Scheduler Properties
+#===============================================================
+org.quartz.scheduler.instanceName = QuartzScheduler
+org.quartz.scheduler.instanceId = AUTO
+
+#===============================================================
+#Configure ThreadPool
+#===============================================================
+org.quartz.threadPool.threadCount =  5
+org.quartz.threadPool.threadPriority = 5
+org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
+
+#===============================================================
+#Configure JobStore
+#===============================================================
+org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
+
+#===============================================================
+#Configure Plugins
+#===============================================================
+org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
+
+org.quartz.plugin.jobInitializer.fileName = quartz_jobs.xml
+
+org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
+org.quartz.plugin.jobInitializer.failOnFileNotFound = true
+org.quartz.plugin.jobInitializer.validating=false
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
new file mode 100644
index 0000000..9d80084
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
@@ -0,0 +1,77 @@
+<?xml version='1.0' encoding='utf-8'?>
+
+<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData
+        http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"
+        version="1.5">
+
+  <job>
+    <job-detail>
+      <name>ConvertDgn2ShpIntoDirectory</name>
+      <group>DEFAULT</group>
+      <description>
+        A job that convert dgn to shapefiles
+      </description>
+      <job-class>
+        com.ximple.eofms.jobs.OracleConvertDgn2ShpJob
+      </job-class>
+      <volatility>false</volatility>
+      <durability>false</durability>
+      <recover>false</recover>
+      <job-data-map allows-transient-data="true">
+        <entry>
+          <key>SHPDATA_DIR</key>
+          <value>c:\temp\data</value>
+        </entry>
+        <entry>
+          <key>ORAHOST</key>
+          <value>192.168.11.200</value>
+        </entry>
+        <entry>
+          <key>ORAINST</key>
+          <value>nntpc</value>
+        </entry>
+        <entry>
+          <key>ORAPORT</key>
+          <value>1521</value>
+        </entry>
+        <entry>
+          <key>ORAUSER</key>
+          <value>spatialdb</value>
+        </entry>
+        <entry>
+          <key>ORAPASS</key>
+          <value>spatialdb000</value>
+        </entry>
+        <entry>
+          <key>ORGSCHEMA</key>
+          <value>SPATIALDB</value>
+        </entry>
+        <entry>
+          <key>TESTMODE</key>
+          <value>FALSE</value>
+        </entry>
+        <entry>
+          <key>TESTCOUNT</key>
+          <value>2</value>
+        </entry>
+      </job-data-map>
+    </job-detail>
+
+    <trigger>
+      <simple>
+        <name>convertTrigger</name>
+        <group>DEFAULT</group>
+        <job-name>ConvertDgn2ShpIntoDirectory</job-name>
+        <job-group>DEFAULT</job-group>
+        <start-time>2008-03-01T18:10:00</start-time>
+        <!-- repeat indefinitely every 10 seconds -->
+        <repeat-count>0</repeat-count>
+        <repeat-interval>1000</repeat-interval>
+        <!-- <repeat-interval>72000000</repeat-interval> -->
+      </simple>
+    </trigger>
+
+  </job>
+</quartz>
diff --git a/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java b/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java
new file mode 100644
index 0000000..d907cc1
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java
@@ -0,0 +1,19 @@
+package com.ximple.eofms;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Unit test for simple App.
+ */
+public class XQuartzJobCarrierTest
+{
+    /**
+     * Rigourous Test :-)
+     */
+    @Test
+    public void testApp()
+    {
+        Assert.assertTrue(true);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/pom.xml b/xdgnjobs/ximple-spatialjob/pom.xml
new file mode 100644
index 0000000..f3b3e16
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.0.2</version>
+  </parent>
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-spatialjob</artifactId>
+  <version>0.0.2</version>
+  <packaging>jar</packaging>
+  <name>ximple-spatialjob</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <description>
+    Ximple Spatial Data Job for Quartz
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>quartz</artifactId>
+      <groupId>opensymphony</groupId>
+    </dependency>
+
+    <!-- Ximple Library -->
+    <dependency>
+      <groupId>com.ximple.eofms</groupId>
+      <artifactId>ximple-dgnio</artifactId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+    </plugins>
+    <resources>
+    </resources>
+  </build>
+</project>
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java
new file mode 100644
index 0000000..b39643c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java
@@ -0,0 +1,75 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ximple.io.dgn7.Element;
+
+public abstract class AbstractDispatchableFilter implements ElementDispatchableFilter
+{
+    private String name;
+    private String description;
+    private LinkedList<ElementTypeCriterion> typeIdCriterions;
+    private LinkedList<ElementLevelCriterion> levelCriterions;
+
+    protected Log logger = LogFactory.getLog(AbstractFLinkageDispatchableFilter.class);
+
+    public AbstractDispatchableFilter()
+    {
+        typeIdCriterions = new LinkedList<ElementTypeCriterion>();
+        levelCriterions = new LinkedList<ElementLevelCriterion>();
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public void addCriterion(ElementTypeCriterion criterion)
+    {
+        typeIdCriterions.add(criterion);
+    }
+
+    public void addLevelCriterion(ElementLevelCriterion criterion)
+    {
+        levelCriterions.add(criterion);
+    }
+
+    protected int compareType(Element element)
+    {
+        for (ElementTypeCriterion criterion : typeIdCriterions)
+        {
+            if (criterion.compareTo(element) == 0)
+                return 0;
+        }
+        return -1;
+    }
+
+    protected int compareLevel(Element element)
+    {
+        for (ElementLevelCriterion criterion : levelCriterions)
+        {
+            if (criterion.compareTo(element) == 0)
+                return 0;
+        }
+        return -1;
+    }
+
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java
new file mode 100644
index 0000000..48be949
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java
@@ -0,0 +1,26 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public abstract class AbstractFLinkageDispatchableFilter extends AbstractDispatchableFilter
+{
+    public static FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java
new file mode 100644
index 0000000..7f8c9f7
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java
@@ -0,0 +1,15 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+
+public interface CreateFeatureTypeStrategy
+{
+    public FeatureType createFeatureElement(String featureName) throws SchemaException;
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException;
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
new file mode 100644
index 0000000..0bf7f05
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
@@ -0,0 +1,101 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.io.dgn7.*;
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+
+public class CreateLineStringStrategy implements CreateFeatureTypeStrategy
+{
+    GeometryFactory geometryFactory = new GeometryFactory();
+    FeatureTypeBuilder typeBuilder = null;
+    TWD97GeometryConverterDecorator convertDecordator = new TWD97GeometryConverterDecorator();
+
+    public CreateLineStringStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (typeBuilder == null)
+        {
+            typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+        }
+        return typeBuilder.getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecordator.setConverter(lineStringElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecordator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineStringElement.getLevelIndex(),
+                    lineStringElement.getColorIndex(),
+                    lineStringElement.getWeight(),
+                    lineStringElement.getLineStyle(),
+            });
+            return feature;
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecordator.setConverter(complexChain);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecordator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    complexChain.getLevelIndex(),
+                    complexChain.getColorIndex(),
+                    complexChain.getWeight(),
+                    complexChain.getLineStyle(),
+            });
+            return feature;
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java
new file mode 100644
index 0000000..9a231ef
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java
@@ -0,0 +1,132 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.UserAttributeData;
+import com.ximple.io.dgn7.TextNodeElement;
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+
+public class CreateTextStrategy implements CreateFeatureTypeStrategy
+{
+    GeometryFactory geometryFactory = new GeometryFactory();
+    FeatureTypeBuilder typeBuilder = null;
+    TWD97GeometryConverterDecorator convertDecordator = new TWD97GeometryConverterDecorator();
+
+    public CreateTextStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (typeBuilder == null)
+        {
+            typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("FONT", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("JUST", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("HEIGHT", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("WIDTH", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("ANGLE", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CONTEXT", String.class));
+        }
+        return typeBuilder.getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof TextElement)
+        {
+            TextElement txtElement = (TextElement) element;
+            convertDecordator.setConverter(txtElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecordator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    txtElement.getLevelIndex(),
+                    txtElement.getColorIndex(),
+                    txtElement.getWeight(),
+                    txtElement.getLineStyle(),
+                    txtElement.getFontIndex(),
+                    txtElement.getJustification(),
+                    txtElement.getTextHeight(),
+                    txtElement.getTextWidth(),
+                    txtElement.getRotationAngle(),
+                    txtElement.getText()
+            });
+            return feature;
+        } else if (element instanceof TextNodeElement)
+        {
+            TextNodeElement nodeElement = (TextNodeElement) element;
+            convertDecordator.setConverter(nodeElement);
+            String[] texts = nodeElement.getTextArray();
+            StringBuffer sb = new StringBuffer();
+            for (String text : texts)
+            {
+                if (sb.length() != 0)
+                sb.append("\n");
+                sb.append(text);
+            }
+
+            Feature feature = featureType.create(new Object[]{
+                    convertDecordator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    nodeElement.getLevelIndex(),
+                    nodeElement.getColorIndex(),
+                    nodeElement.getWeight(),
+                    nodeElement.getLineStyle(),
+                    nodeElement.getFontIndex(),
+                    nodeElement.getJustification(),
+                    nodeElement.getTextNodeHeight(),
+                    nodeElement.getTextNodeLength(),
+                    nodeElement.getRotationAngle(),
+                    sb.toString()
+            });
+            return feature;
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java
new file mode 100644
index 0000000..9ed97d1
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java
@@ -0,0 +1,12 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+
+import com.ximple.io.dgn7.Element;
+
+public interface ElementDispatchableFilter
+{
+    public boolean isDispatchable(Element element);
+
+    public Feature execute(Element element);
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java
new file mode 100644
index 0000000..48df964
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java
@@ -0,0 +1,39 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import org.geotools.feature.Feature;
+
+import com.ximple.io.dgn7.Element;
+
+public class ElementDispatcher
+{
+    private LinkedList<ElementDispatchableFilter> rules;
+
+    public ElementDispatcher()
+    {
+        rules = new LinkedList<ElementDispatchableFilter>();
+    }
+
+    public LinkedList<ElementDispatchableFilter> getRules()
+    {
+        return rules;
+    }
+
+    public void addRule(ElementDispatchableFilter rule)
+    {
+        rules.add(rule);
+    }
+
+    public Feature execute(Element element)
+    {
+        for (ElementDispatchableFilter rule : rules)
+        {
+            if (rule.isDispatchable(element))
+            {
+                return rule.execute(element);
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java
new file mode 100644
index 0000000..187bcc5
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java
@@ -0,0 +1,48 @@
+package com.ximple.eofms.filter;
+
+import com.ximple.io.dgn7.Element;
+
+import java.util.ArrayList;
+
+public class ElementLevelCriterion implements Comparable
+{
+    private int elementLevel;
+    private ArrayList<Integer> elementLevelArray  ;
+
+    public ElementLevelCriterion()
+    {
+            elementLevelArray = new ArrayList<Integer>();
+    }
+
+    public int compareTo(Object o)
+    {
+        if (o instanceof Element)
+        {
+            Element elm = (Element) o;
+
+            for(Integer elevel : getElementLevelArray())
+            {
+             if (elm.getElementType().id == elevel.intValue())
+                return 0;
+            }
+
+        }
+        return -1;
+    }
+
+    public int getElementLevel()
+    {
+        return elementLevel;
+    }
+
+     public ArrayList<Integer> getElementLevelArray()
+    {
+        return elementLevelArray;
+    }
+
+    public void setElementLevel(int iLevel)
+    {
+        this.elementLevel =  iLevel ;
+        this.elementLevelArray.add(iLevel);
+    }
+}
\ No newline at end of file
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java
new file mode 100644
index 0000000..4c51245
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java
@@ -0,0 +1,48 @@
+package com.ximple.eofms.filter;
+
+import com.ximple.io.dgn7.Element;
+
+import java.util.ArrayList;
+
+public class ElementTypeCriterion implements Comparable
+{
+    private int elementType;
+    private ArrayList<Integer> elementTypeArray  ;
+
+    public ElementTypeCriterion()
+    {
+            elementTypeArray = new ArrayList<Integer>();
+    }
+
+    public int compareTo(Object o)
+    {
+        if (o instanceof Element)
+        {
+            Element elm = (Element) o;
+
+            for(Integer etype : getElementTypeArray())
+            {
+             if (elm.getElementType().id == etype.intValue())
+                return 0;
+            }
+
+        }
+        return -1;
+    }
+
+    public int getElementType()
+    {
+        return elementType;
+    }
+
+     public ArrayList<Integer> getElementTypeArray()
+    {
+        return elementTypeArray;
+    }
+
+    public void setElementType(int itype)
+    {
+        this.elementType =  itype ;
+        this.elementTypeArray.add(itype);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java
new file mode 100644
index 0000000..e5e80b6
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java
@@ -0,0 +1,89 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+import java.util.ArrayList;
+
+public class TypeCompIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private int cid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeCompIdDispatchableFilter()
+    {
+    }
+
+    public TypeCompIdDispatchableFilter(String fname,
+                                        CreateFeatureTypeStrategy createStrategy,
+                                        int tid, int compid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.cid = compid;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public int getCid()
+    {
+        return cid;
+    }
+
+    public void setCid(int cid)
+    {
+        this.cid = cid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+
+
+    //�P�_�O�_�ũM����
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (cid == featureLinkage.getComponentID()) &&
+                (compareType(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            FeatureType ftype = createStrategy.createFeatureElement(getName());
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java
new file mode 100644
index 0000000..ecc6534
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java
@@ -0,0 +1,96 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class TypeCompLevelIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private int cid;
+    private int lid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeCompLevelIdDispatchableFilter()
+    {
+    }
+
+    public TypeCompLevelIdDispatchableFilter(String fname,
+                                             CreateFeatureTypeStrategy createStrategy,
+                                             int tid, int compid, int level)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.cid = compid;
+        this.lid = level;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public int getCid()
+    {
+        return cid;
+    }
+
+    public void setCid(int cid)
+    {
+        this.cid = cid;
+    }
+
+    public int getLid()
+    {
+        return lid;
+    }
+
+    public void setLid(int lid)
+    {
+        this.lid = lid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (cid == featureLinkage.getComponentID()) &&
+                (lid == element.getLevelIndex()) && (compareLevel(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            FeatureType ftype = createStrategy.createFeatureElement(getName());
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java
new file mode 100644
index 0000000..c45b547
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java
@@ -0,0 +1,72 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+import java.util.ArrayList;
+
+public class TypeIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeIdDispatchableFilter()
+    {
+    }
+
+    public TypeIdDispatchableFilter(String fname, CreateFeatureTypeStrategy createStrategy,
+                                    int tid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (compareType(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            FeatureType ftype = createStrategy.createFeatureElement(getName());
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
new file mode 100644
index 0000000..00057d2
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
@@ -0,0 +1,150 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+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 CONFSHPFILTER = "SHPFILTER_CONF";
+    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";
+    private static final String TESTMODE = "TESTMODE";
+    private static final String TESTCOUNT = "TESTCOUNT";
+
+    protected String _dataPath;
+    protected String _filterPath;
+    protected String _oracleHost;
+    protected String _oracleInstance;
+    protected String _oraclePort;
+    protected String _username;
+    protected String _password;
+    protected String _orgSchema;
+    protected boolean _testMode = false;
+    protected int _testCount = -1;
+
+    public abstract void execute(JobExecutionContext context) throws JobExecutionException;
+
+    public Log getLogger() { return null; }
+
+    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);
+        _filterPath = dataMap.getString(CONFSHPFILTER);
+        _oracleHost = dataMap.getString(ORAHOST);
+        _oracleInstance = dataMap.getString(ORAINST);
+        _oraclePort = dataMap.getString(ORAPORT);
+        _username = dataMap.getString(ORAUSER);
+        _password = dataMap.getString(ORAPASS);
+        _orgSchema = dataMap.getString(SPATAILSCHEMA);
+        _testMode = dataMap.getBooleanFromString(TESTMODE);
+        _testCount = dataMap.getIntFromString(TESTCOUNT);
+
+        // Validate the required input
+        if (_dataPath == null)
+        {
+            Log logger = getLogger();
+            if (logger != null)
+            {
+                logger.warn("Cannot found data directory in configarion.");
+            }
+            throw new JobExecutionException("Directory not configured");
+        }
+
+        // Make sure the directory exists
+        File dir = new File(_dataPath);
+        if (!dir.exists())
+        {
+            Log logger = getLogger();
+            if (logger != null)
+            {
+                logger.warn("Cannot found data directory in file system.[" + _dataPath + "]");
+            }
+            throw new JobExecutionException("Invalid Dir " + _dataPath);
+        }
+
+        if (_oracleHost == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Host.");
+        }
+        if (_oracleInstance == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Instance.");
+        }
+        if (_username == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Username.");
+        }
+        if (_password == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Password.");
+        }
+        if (_orgSchema == null)
+        {
+            throw new JobExecutionException("Unknown Spatial Database Schema.");
+        }
+    }
+
+    protected abstract AbstractOracleJobContext prepareJobContext(String filterPath);
+
+    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;
+
+        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();
+
+            assert buffer != null;
+            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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java
new file mode 100644
index 0000000..4be207f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java
@@ -0,0 +1,294 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import oracle.jdbc.OracleConnection;
+
+public abstract class AbstractOracleJobContext
+{
+    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;";
+
+    /**
+     * copy connectivity to connectivity_webcheck sql
+     */
+    protected static final String TRUNCATE_CONNECTIVITY_WEBCHECK = "TRUNCATE TABLE  BASEDB.CONNECTIVITY_WEBCHECK";
+    protected static final String CREATE_CONNECTIVITY_WEBCHECK = "CREATE TABLE BASEDB.CONNECTIVITY_WEBCHECK\n" +
+            "( FSC      NUMBER(5) NOT NULL," +
+            "  UFID     NUMBER(10) NOT NULL," +
+            "  N1       NUMBER(10)," +
+            "  N2       NUMBER(10)," +
+            "  FDR1     NUMBER(5)," +
+            "  FDR2     NUMBER(5)," +
+            "  DIR      NUMBER(3)," +
+            "  OHUG     NUMBER(3)," +
+            "  OSTATUS  NUMBER(3)," +
+            "  PHASE    NUMBER(3)," +
+            "  X        NUMBER(10,3) NOT NULL," +
+            "  Y        NUMBER(10,3) NOT NULL" +
+            ")\n" +
+            "TABLESPACE BASE_DATA PCTUSED 40 PCTFREE 10 INITRANS 1 MAXTRANS 255\n" +
+            "STORAGE    (INITIAL 19120K MINEXTENTS 1 MAXEXTENTS 2147483645 \n" +
+            "PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT )\n" +
+            "LOGGING NOCOMPRESS NOCACHE NOPARALLEL MONITORING;";
+
+     protected static final String COPY_CONNECTIVITY_TO_WEBCHECK = "INSERT /*+ APPEND */ INTO BASEDB.CONNECTIVITY_WEBCHECK\n" +
+             "(FSC, UFID, N1, N2, FDR1, FDR2, DIR, OHUG,OSTATUS, PHASE, X, Y)\n" +
+             "SELECT FSC, UFID, N1, N2, FDR1, FDR2, DIR, OHUG, OSTATUS, PHASE, X, Y FROM BASEDB.CONNECTIVITY ";
+
+
+    /**
+     *
+     */
+    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 + "\"";
+    protected static final long TIMEOUT = Long.MAX_VALUE;
+    /**
+     * Encoding of URL path.
+     */
+    protected static final String ENCODING = "UTF-8";
+    private String _oracleHost;
+    private String _oracleInstance;
+    private String _oraclePort;
+    protected String _dataPath;
+    private OracleConnection oracleConnection = null;
+    protected Properties properties;
+
+    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();
+    }
+
+    public void setLogin(String userName, String password)
+    {
+        properties.put(PROPUsrKey, userName);
+        properties.put(PROPPassKey, password);
+    }
+
+    public void setShapeData(String dataPath)
+    {
+        _dataPath = dataPath;
+    }
+
+    public OracleConnection getOracleConnection()
+    {
+        try
+        {
+            if (oracleConnection == null)
+            {
+                oracleConnection = (OracleConnection) DriverManager.getConnection(
+                        getCurrentURL(_oracleHost, _oraclePort, _oracleInstance),
+                        properties);
+            }
+
+            return oracleConnection;
+        } catch (SQLException e)
+        {
+            OracleConvertJobContext.logger.warn(e.getMessage(), e);
+        }
+
+        oracleConnection = null;
+
+        return null;
+    }
+
+    public void closeConnection()
+    {
+        try
+        {
+            if (oracleConnection != null)
+            {
+                oracleConnection.close();
+                oracleConnection = null;
+            }
+        } catch (SQLException e)
+        {
+            OracleConvertJobContext.logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public void setConnectionInfo(String oracleHost, String oraclePort, String oracleInstance)
+    {
+        _oracleHost = oracleHost;
+        _oracleInstance = oracleInstance;
+        _oraclePort = oraclePort;
+    }
+
+    public abstract void startTransaction();
+
+    public abstract void commitTransaction();
+
+    public abstract void rollbackTransaction();
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
new file mode 100644
index 0000000..73ad22e
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
@@ -0,0 +1,405 @@
+package com.ximple.eofms.jobs;
+
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Date;
+import java.io.IOException;
+
+import org.apache.commons.collections.OrderedMap;
+import org.apache.commons.collections.OrderedMapIterator;
+import org.apache.commons.collections.map.LinkedMap;
+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 com.vividsolutions.jts.geom.GeometryFactory;
+
+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.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Dgn7fileException;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.ElementType;
+import com.ximple.io.dgn7.IElementHandler;
+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;
+    private static final int COMMITSIZE = 20;
+
+    class Pair
+    {
+        Object first;
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    GeometryFactory _geomFactory = new GeometryFactory();
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleConvertJobContext(filterPath);
+    }
+
+    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 = (OracleConvertJobContext) prepareJobContext(_filterPath);
+        jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+        jobContext.setLogin(_username, _password);
+        jobContext.setShapeData(_dataPath);
+        jobContext.setExecutionContext(context);
+     
+        try
+        {
+            copyConnectivity(jobContext);
+            exetcuteConvert(jobContext, _orgSchema, _dataPath);
+            //exetcuteConvert(jobContext, "CMMS_SPATIALDB", _dataPath);
+
+            //close all open filewriter instance
+            jobContext.closeFeatureWrite();
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException("Database error.", e);
+        }   catch (IOException ex) {
+            ex.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+
+    }
+
+    //Connectivity�ƻs�@�Ӫ����A�b�d�߹q�y��V�ɥΨӤ��OMS��Ʈw���q���s����(Connectivity)
+    private void copyConnectivity(OracleConvertJobContext jobContext)  throws SQLException
+    {
+       OracleConnection connection =  jobContext.getOracleConnection() ;
+       Statement stmt = connection.createStatement();
+       stmt.execute(OracleConvertJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+       stmt.execute(OracleConvertJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+    }
+
+    private void exetcuteConvert(OracleConvertJobContext jobContext,
+                                 String querySchema, String dataPath) throws SQLException
+    {
+        int order = 0;
+        OrderedMap map = getBlobStorageList(jobContext.getOracleConnection(), querySchema, "SD$SPACENODES"
+                , null);
+
+        logger.info("begin convert job:[" + map.size() + "]:testmode=" + _testMode);
+
+        int total = map.size(); //spacenodes count
+        int step = total / 100;
+        int current = 0;
+        
+        //jobContext.startTransaction();
+        jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", 0);
+        for (OrderedMapIterator it = map.orderedMapIterator(); it.hasNext();)
+        {
+            it.next();
+
+            Pair pair = (Pair) it.getValue();
+            String tableSrc = (String) pair.first;
+
+            logger.info("begin convert:[" + order + "]-" + tableSrc);
+            queryIgsetElement(jobContext, querySchema, tableSrc);
+
+            order++;
+
+            if (_testMode)
+            {
+                if ((_testCount < 0) || (order >= _testCount))
+                    break;
+            }
+
+            if ((order % COMMITSIZE) == 0)
+            {
+                // OracleConnection connection = jobContext.getOracleConnection();
+                // connection.commitTransaction();
+                jobContext.commitTransaction();
+                //jobContext.startTransaction();
+                System.gc();
+            }
+
+            int now = order % step;
+            if (now != current)
+            {
+                current = now;
+                jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", current);
+
+            }
+        }
+        jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", 100);
+
+        jobContext.commitTransaction();
+
+        logger.info("end convert job:[" + order + "]");
+        System.gc();
+    }
+
+    protected OrderedMap getBlobStorageList(OracleConnection connection, String schemaSrc, String tableSrc,
+                                            OrderedMap orderedMap) throws SQLException
+    {
+        if (orderedMap == null)
+            orderedMap = new LinkedMap(99);
+        String fetchStmtFmt = "SELECT SNID, SPACETABLE FROM \"%s\".\"%s\"";
+        PrintfFormat spf = new PrintfFormat(fetchStmtFmt);
+        String fetchStmt = spf.sprintf(new Object[]{schemaSrc, tableSrc});
+        Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmt.setFetchSize(FETCHSIZE);
+
+        ResultSet rs = stmt.executeQuery(fetchStmt);
+
+        while (rs.next())
+        {
+            int size = rs.getMetaData().getColumnCount();
+            Object[] values = new Object[size];
+
+            for (int i = 0; i < size; i++)
+            {
+                values[i] = rs.getObject(i + 1);
+            }
+
+            Integer key = ((BigDecimal) values[0]).intValue();
+            String name = (String) values[1];
+
+            Pair pair = (Pair) orderedMap.get(key);
+            if (pair == null)
+                orderedMap.put(key, new Pair(name, null));
+            else
+                pair.first = name;
+        }
+
+        rs.close();
+        stmt.close();
+
+        return orderedMap;
+    }
+
+    protected OrderedMap getRawFormatStorageList(OracleConnection connection, String schemaSrc, String tableSrc,
+                                                 OrderedMap orderedMap) throws SQLException
+    {
+        if (orderedMap == null)
+            orderedMap = new LinkedMap(99);
+        String fetchStmtFmt = "SELECT RNID, SPACETABLE FROM \"%s\".\"%s\"";
+        PrintfFormat spf = new PrintfFormat(fetchStmtFmt);
+        String fetchStmt = spf.sprintf(new Object[]{schemaSrc, tableSrc});
+        Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmt.setFetchSize(FETCHSIZE);
+
+        ResultSet rs = stmt.executeQuery(fetchStmt);
+
+        while (rs.next())
+        {
+            int size = rs.getMetaData().getColumnCount();
+            Object[] values = new Object[size];
+
+            for (int i = 0; i < size; i++)
+            {
+                values[i] = rs.getObject(i + 1);
+            }
+
+            Integer key = ((BigDecimal) values[0]).intValue();
+            String name = (String) values[1];
+
+            Pair pair = (Pair) orderedMap.get(key);
+            if (pair == null)
+                orderedMap.put(key, new Pair(null, name));
+            else
+                pair.second = name;
+        }
+
+        rs.close();
+        stmt.close();
+
+        return orderedMap;
+    }
+
+    protected void queryIgsetElement(OracleConvertJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        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);
+            }
+
+            try
+            {
+                Element element = fetchBinaryElement(raw);
+                jobContext.putFeatureCollection(element);
+            } catch (Dgn7fileException e)
+            {
+                logger.warn("Dgn7Exception", e);
+            }
+        }
+
+        rsSrc.close();
+        stmtSrc.close();
+    }
+
+    protected void queryRawElement(OracleConvertJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        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);
+
+
+            try
+            {
+                Element element = fetchBinaryElement(rawDest);
+                jobContext.putFeatureCollection(element);
+            } catch (Dgn7fileException e)
+            {
+                logger.warn("Dgn7Exception:" + e.getMessage(), e);
+            }
+        }
+
+        rsDest.close();
+        stmtDest.close();
+    }
+
+    // Binary to Element
+    private Element fetchBinaryElement(byte[] raws) throws Dgn7fileException
+    {
+        ByteBuffer buffer = ByteBuffer.wrap(raws);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        short signature = buffer.getShort();
+
+        // byte type = (byte) (buffer.get() & 0x7f);
+        byte type = (byte) ((signature >>> 8) & 0x007f);
+
+        // silly Bentley say contentLength is in 2-byte words
+        // and ByteByffer uses raws.
+        // track the record location
+        int elementLength = (buffer.getShort() * 2) + 4;
+        ElementType recordType = ElementType.forID(type);
+        IElementHandler handler;
+
+        handler = recordType.getElementHandler();
+
+        Element dgnElement = (Element) handler.read(buffer, signature, elementLength);
+        if (recordType.isComplexElement() && (elementLength < raws.length))
+        {
+            int offset = elementLength;
+            while (offset < (raws.length - 4))
+            {
+                buffer.position(offset);
+                signature = buffer.getShort();
+                type = (byte) ((signature >>> 8) & 0x007f);
+                elementLength = (buffer.getShort() * 2) + 4;
+                if (raws.length < (offset + elementLength))
+                {
+                    System.out.println("Length not match:" + offset + ":" + buffer.position() + ":" + buffer.limit());
+                    break;
+                }
+                recordType = ElementType.forID(type);
+                handler = recordType.getElementHandler();
+                if (handler != null)
+                {
+                    Element subElement = (Element) handler.read(buffer, signature, elementLength);
+                    ((ComplexElement) dgnElement).add(subElement);
+                    offset += elementLength;
+                } else
+                {
+                    byte[] remain = new byte[buffer.remaining()];
+                    System.arraycopy(raws, offset, remain, 0, buffer.remaining());
+                    for (int i = 0; i < remain.length; i++)
+                    {
+                        if (remain[i] != 0)
+                        {
+                            logger.info("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                            System.out.println("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        return dgnElement;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
new file mode 100644
index 0000000..749cb89
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
@@ -0,0 +1,248 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SimpleFeature;
+import org.xml.sax.SAXException;
+import org.quartz.JobExecutionContext;
+
+import com.vividsolutions.jts.util.Assert;
+
+import com.ximple.eofms.filter.AbstractFLinkageDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+
+public class OracleConvertJobContext extends AbstractOracleJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    static
+    {
+        try
+        {
+            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
+        } catch (SQLException e)
+        {
+            Assert.shouldNeverReachHere(e.getMessage());
+        }
+    }
+
+    private String _filterConfig;
+
+    private ElementDispatcher elementDispatcher;
+
+    private HashMap featuresContext = new HashMap();
+    private HashMap featuresWriterContext = new HashMap();
+    private PessimisticMapWrapper txFeaturesContext;
+
+    private JobExecutionContext executionContext;
+
+    public OracleConvertJobContext(String filterConfig)
+    {
+        properties = new Properties();
+        _filterConfig = filterConfig;
+        elementDispatcher = createElementDispatcher();
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+    }
+
+    private ElementDispatcher createElementDispatcher()
+    {
+        try
+        {
+            URL rulesURL = ElementDispatcher.class.getResource("ElementDispatcherRules.xml");
+            assert rulesURL != null;
+            Digester digester = DigesterLoader.createDigester(rulesURL);
+            URL filterURL = null;
+            if (_filterConfig != null)
+            {
+                File config = new File(_filterConfig);
+                if (config.exists())
+                {
+                    filterURL = config.toURI().toURL();
+                }
+            }
+            if (filterURL == null)
+            {
+                // config = new File("conf/DefaultConvertShpFilter.xml");
+                filterURL = this.getClass().getResource("/conf/DefaultConvertShpFilter.xml");
+               //filterURL = this.getClass().getResource("/conf/ConvertShpFilterForLevel.xml");
+            }
+            assert filterURL != null;
+            return (ElementDispatcher) digester.parse(filterURL);
+        } catch (UnsupportedEncodingException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (MalformedURLException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (SAXException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public void putFeatureCollection(Element element)
+    {
+        //�P�_�O�_�ũM����
+        Feature feature = elementDispatcher.execute(element);
+        if (feature == null)
+        {
+            FrammeAttributeData linkage =
+                    AbstractFLinkageDispatchableFilter.getFeatureLinkage(element);
+            logger.debug("Unknown Element:" + element.getElementType().toString() +
+                    ":type=" + element.getType() + ":lv=" + element.getLevelIndex() + ":id=" +
+                    (linkage == null ? "NULL" : (linkage.getFsc() + "|" + linkage.getComponentID())));
+
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        ArrayList arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+    }
+
+    public void startTransaction()
+    {
+       //txFeaturesContext.startTransaction();
+    }
+
+    public void commitTransaction()
+    {
+       if (!txFeaturesContext.isEmpty())
+       {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+           //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+       //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        // todo:
+        Iterator it = featuresContext.keySet().iterator();  
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(_dataPath + "\\" + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer = null;
+                if(featuresWriterContext.containsKey( featureType.getTypeName()))
+                {
+                    writer = (FeatureWriter) featuresWriterContext.get(featureType.getTypeName()) ;
+                }
+                else
+                {
+                  ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL());
+                  shapefileDataStore.createSchema(featureType);
+                  writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                  if(this.featuresWriterContext == null)
+                  {
+                     this.featuresWriterContext =  new HashMap();
+                  }
+                  featuresWriterContext.put(featureType.getTypeName() , writer);
+                }
+
+                ArrayList features = (ArrayList) featuresContext.get(featureType);
+                Iterator itFeature = features.iterator();
+                while (itFeature.hasNext())
+                {
+                    Feature feature = (Feature) itFeature.next();
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+
+    }
+
+    public JobExecutionContext getExecutionContext()
+    {
+        return executionContext;
+    }
+
+    public void setExecutionContext(JobExecutionContext context)
+    {
+        executionContext = context;
+    }
+
+    public void closeFeatureWrite() throws IOException {
+       Iterator iter =   this.featuresWriterContext.values().iterator();
+
+        while(iter.hasNext())
+        {
+           FeatureWriter writer = (FeatureWriter) iter.next();
+           writer.close();
+        }
+
+        this.featuresWriterContext = null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
new file mode 100644
index 0000000..f77744b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
@@ -0,0 +1,58 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+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.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);
+
+        AbstractOracleJobContext jobContext = prepareJobContext(_filterPath);
+        jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+        jobContext.setLogin(_username, _password);
+
+        try
+        {
+            exetcuteConvert(jobContext, _orgSchema, _dataPath);
+            exetcuteConvert(jobContext, "CMMS_SPATIALDB", _dataPath);
+        } catch (SQLException e)
+        {
+            throw new JobExecutionException("Database error.", e);
+        }
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleUpgradeJobContext();
+    }
+
+    private void exetcuteConvert(AbstractOracleJobContext jobContext,
+                                 String orgSchema, String dataPath) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        OracleDatabaseMetaData metaData = (OracleDatabaseMetaData) connection.getMetaData();
+        OracleStatement statement = (OracleStatement) connection.createStatement();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java
new file mode 100644
index 0000000..54cf900
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java
@@ -0,0 +1,16 @@
+package com.ximple.eofms.jobs;
+
+public class OracleUpgradeJobContext extends AbstractOracleJobContext
+{
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java
new file mode 100644
index 0000000..8968e34
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java
@@ -0,0 +1,75 @@
+package com.ximple.eofms.jobs;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.util.Assert;
+
+import com.ximple.io.dgn7.GeometryConverter;
+import com.ximple.eofms.util.TWDDatumConverter;
+
+public class TWD97GeometryConverterDecorator implements GeometryConverter
+{
+    private GeometryConverter converter;
+    private TWD97ConvertFilter coordinatesFilter = new TWD97ConvertFilter();
+
+    public TWD97GeometryConverterDecorator()
+    {
+    }
+
+    public GeometryConverter getConverter()
+    {
+        return converter;
+    }
+
+    public void setConverter(GeometryConverter converter)
+    {
+        this.converter = converter;
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        if (converter == null) Assert.shouldNeverReachHere();
+
+        coordinatesFilter.reset();
+
+        Geometry geom = converter.toGeometry(factory);
+        geom.apply(coordinatesFilter);
+        return geom;
+    }
+
+    class TWD97ConvertFilter implements CoordinateSequenceFilter
+    {
+        private boolean done = false;
+        private boolean geometryChanged = false;
+
+        public void filter(CoordinateSequence coordinateSequence, int i)
+        {
+            Coordinate pt = coordinateSequence.getCoordinateCopy(i);
+            Coordinate pt97 = TWDDatumConverter.toTWD97(pt);
+            pt.x = pt97.x;
+            pt.y = pt97.y;
+            pt.z = pt97.z;
+            done = (i >= coordinateSequence.size());
+            geometryChanged = true;
+        }
+
+        public boolean isDone()
+        {
+            return done;
+        }
+
+        public boolean isGeometryChanged()
+        {
+            return geometryChanged;
+        }
+
+        public void reset()
+        {
+            done = false;
+            geometryChanged = false;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
new file mode 100644
index 0000000..2dd4c53
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
@@ -0,0 +1,363 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.LongBuffer;
+
+/**
+ * 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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java
new file mode 100644
index 0000000..5e529ac
--- /dev/null
+++ b/xdgnjobs/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.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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java
new file mode 100644
index 0000000..f22eaec
--- /dev/null
+++ b/xdgnjobs/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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java
new file mode 100644
index 0000000..023ee69
--- /dev/null
+++ b/xdgnjobs/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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java
new file mode 100644
index 0000000..794c27b
--- /dev/null
+++ b/xdgnjobs/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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
new file mode 100644
index 0000000..b7a6ade
--- /dev/null
+++ b/xdgnjobs/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/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
new file mode 100644
index 0000000..e208caf
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
@@ -0,0 +1,490 @@
+package com.ximple.eofms.util;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * 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
+     */
+    public static Coordinate toTWD97(Coordinate pt)
+    {
+        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    = pt.x * DEG_RAD;
+        lat    = pt.y * DEG_RAD;
+        height = pt.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 null;
+        }
+
+        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 new Coordinate(newX, newY, newZ);
+                }
+            }
+        }
+
+        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;
+        return new Coordinate(newX, newY, newZ);
+    }
+
+    public static Coordinate toTWD67(Coordinate pt)
+    {
+        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    = pt.x * DEG_RAD;
+        lat    = pt.y * DEG_RAD;
+        height = pt.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 null;
+        }
+
+        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 new Coordinate(newX, newY, newZ);
+                }
+            }
+        }
+
+        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;
+        return new Coordinate(newX, newY, newZ);
+    }
+
+    public static 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));
+    }
+
+    public static 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;
+    }
+
+    private static 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));
+     * }
+     */
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml
new file mode 100644
index 0000000..6c3924e
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml
@@ -0,0 +1,86 @@
+<?xml version='1.0' encoding="big5"?>
+<!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "digester-rules.dtd">
+<digester-rules>
+  <pattern value="ElementDispatcherRules">
+    <object-create-rule classname="com.ximple.eofms.filter.ElementDispatcher"/>
+    <set-properties-rule/>
+    <pattern value="TypeFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="elmtype"/>
+      <bean-property-setter-rule pattern="tid"/>
+        <pattern value="LineCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+        </pattern>
+        <pattern value="TextCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+        </pattern>
+    </pattern>
+    <pattern value="TypeCompFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+       <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+       <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompLevelFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <!-- <bean-property-setter-rule pattern="lid"/> -->
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+       <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="elementLayerCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-next-rule methodname="addLayerCriterion" paramtype="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-properties-rule/>
+       <bean-property-setter-rule pattern="elementLayer"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+            <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+            <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+            <set-properties-rule/>
+      </pattern>
+    </pattern>
+  </pattern>
+</digester-rules>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml
new file mode 100644
index 0000000..d8d863a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+<!-- High Voltage Features -->
+				<TypeCompLevelFilter name="FSC-106.C-0">
+								<tid>106</tid>
+								<cid>0</cid>
+								<description>�D�����u</description>
+								<elementCriterion>
+												<elementType>12</elementType>
+								</elementCriterion>
+                                <elementLevelCriterion>
+												<elementLevel>1</elementLevel>
+								</elementLevelCriterion>
+                                <LineCreateStrategy/>
+				</TypeCompLevelFilter>
+
+</ElementDispatcherRules>
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml
new file mode 100644
index 0000000..e5a1b5a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml
@@ -0,0 +1,720 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+<!-- High Voltage Features -->
+				<TypeCompFilter name="FSC-106.C-0">
+								<tid>106</tid>
+								<cid>0</cid>
+								<description>�D�����u</description>
+								<elementCriterion>
+												<elementType>12</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-402.C-0">
+								<tid>402</tid>
+								<cid>0</cid>
+								<description>�ܹq��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-402.C-1">
+								<tid>402</tid>
+								<cid>1</cid>
+								<description>�ܹq�ҵ��O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-402.C-2">
+								<tid>402</tid>
+								<cid>2</cid>
+								<description>�ܹq�Ҥ�����O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-411.C-0">
+								<tid>411</tid>
+								<cid>0</cid>
+								<description>�t�q��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-411.C-1">
+								<tid>411</tid>
+								<cid>1</cid>
+								<description>�t�q�����O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-411.C-2">
+								<tid>411</tid>
+								<cid>2</cid>
+								<description>�t�q��-1/600</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-411.C-3">
+								<tid>411</tid>
+								<cid>3</cid>
+								<description>�t�q�����O-1/600</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-407.C-0">
+								<tid>407</tid>
+								<cid>0</cid>
+								<description>�q��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-407.C-1">
+								<tid>407</tid>
+								<cid>1</cid>
+								<description>�q����O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-407.C-2">
+								<tid>407</tid>
+								<cid>2</cid>
+								<description>�q��-1/600</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-407.C-3">
+								<tid>407</tid>
+								<cid>3</cid>
+								<description>�q��-1/600���O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-114.C-0">
+								<tid>114</tid>
+								<cid>0</cid>
+								<description>�}��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-114.C-1">
+								<tid>114</tid>
+								<cid>1</cid>
+								<description>�}���j�����O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-114.C-2">
+								<tid>114</tid>
+								<cid>2</cid>
+								<description>�}���p�P��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-114.C-3">
+								<tid>114</tid>
+								<cid>3</cid>
+								<description>�}���j�P��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-122.C-0">
+								<tid>122</tid>
+								<cid>0</cid>
+								<description>�����s��</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-122.C-1">
+								<tid>122</tid>
+								<cid>1</cid>
+								<description>�����s�����O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-107.C-0">
+								<tid>107</tid>
+								<cid>0</cid>
+								<description>������</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-107.C-2">
+								<tid>107</tid>
+								<cid>2</cid>
+								<description>�����Τᤤ����O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-117.C-0">
+								<tid>117</tid>
+								<cid>0</cid>
+								<description>Tie������</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-105.C-0">
+								<tid>105</tid>
+								<cid>0</cid>
+								<description>�������Y</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-105.C-1">
+								<tid>105</tid>
+								<cid>1</cid>
+								<description>�������Y���O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-101.C-0">
+								<tid>101</tid>
+								<cid>0</cid>
+								<description>�׬y��</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+												<elementType>12</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-120.C-0">
+								<tid>120</tid>
+								<cid>0</cid>
+								<description>�`�I</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-150.C-0">
+								<tid>150</tid>
+								<cid>0</cid>
+								<description>����}��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-130.C-0">
+								<tid>130</tid>
+								<cid>0</cid>
+								<description>�ɽu��e</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-131.C-0">
+								<tid>131</tid>
+								<cid>0</cid>
+								<description>�ɽu�ܧ�</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-109.C-0">
+								<tid>109</tid>
+								<cid>0</cid>
+								<description>���u�s��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-116.C-0">
+								<tid>116</tid>
+								<cid>0</cid>
+								<description>�����׺�</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-108.C-0">
+								<tid>108</tid>
+								<cid>0</cid>
+								<description>�_����</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-108.C-1">
+								<tid>108</tid>
+								<cid>1</cid>
+								<description>�_�������O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-119.C-0">
+								<tid>119</tid>
+								<cid>0</cid>
+								<description>�q��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-115.C-0">
+								<tid>115</tid>
+								<cid>0</cid>
+								<description>������</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-115.C-1">
+								<tid>115</tid>
+								<cid>1</cid>
+								<description>���������O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-102.C-0">
+								<tid>102</tid>
+								<cid>0</cid>
+								<description>�q�e��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-100.C-0">
+								<tid>100</tid>
+								<cid>0</cid>
+								<description>�׹p��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-140.C-0">
+								<tid>140</tid>
+								<cid>0</cid>
+								<description>�����u��</description>
+								<elementCriterion>
+												<elementType>12</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-151.C-0">
+								<tid>150</tid>
+								<cid>0</cid>
+								<description>�`���}��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter><!-- Low Voltage Features -->
+				<TypeCompFilter name="FSC-200.C-0">
+								<tid>200</tid>
+								<cid>0</cid>
+								<description>�C���`�I</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<!-- *****�C��****** -->
+				<TypeCompFilter name="FSC-201.C-0">
+								<tid>201</tid>
+								<cid>0</cid>
+								<description>����u</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-201.C-1">
+								<tid>201</tid>
+								<cid>1</cid>
+								<description>����u���O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-201.C-2">
+								<tid>201</tid>
+								<cid>2</cid>
+								<description>����u���׵��O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-202.C-0">
+								<tid>202</tid>
+								<cid>0</cid>
+								<description>�����I</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-202.C-1">
+								<tid>202</tid>
+								<cid>1</cid>
+								<description>�����I���P���O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-203.C-0">
+								<tid>203</tid>
+								<cid>0</cid>
+								<description>���O�d�������I</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-203.C-1">
+								<tid>203</tid>
+								<cid>1</cid>
+								<description>���O�d�������I���O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-204.C-0">
+								<tid>204</tid>
+								<cid>0</cid>
+								<description>�C�����u</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-205.C-0">
+								<tid>205</tid>
+								<cid>0</cid>
+								<description>�C���ɽu</description>
+								<elementCriterion>
+												<elementType>12</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-205.C-1">
+								<tid>205</tid>
+								<cid>1</cid>
+								<description>�C���ɽu���O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-205.C-2">
+								<tid>205</tid>
+								<cid>2</cid>
+								<description>�C���ɽu���׵��O</description>
+								<elementCriterion>
+												<elementType>7</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-206.C-0">
+								<tid>206</tid>
+								<cid>0</cid>
+								<description>�C�������c</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-207.C-0">
+								<tid>207</tid>
+								<cid>0</cid>
+								<description>�C��ĵ����</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-208.C-0">
+								<tid>208</tid>
+								<cid>0</cid>
+								<description>�C���׺�</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-209.C-0">
+								<tid>209</tid>
+								<cid>0</cid>
+								<description>�C����q���x</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-210.C-0">
+								<tid>210</tid>
+								<cid>0</cid>
+								<description>�C���a�U�ɽu</description>
+								<elmtype>12</elmtype>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-210.C-1">
+								<tid>210</tid>
+								<cid>1</cid>
+								<description>�C���a�U�ɽu���I�Ÿ�</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-210.C-2">
+								<tid>210</tid>
+								<cid>2</cid>
+								<description>�C���a�U�ɽu���׵��O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-210.C-3">
+								<tid>210</tid>
+								<cid>3</cid>
+								<description>�C���a�U�ɽu�Ÿ�</description>
+								<elmtype>4</elmtype>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-210.C-4">
+								<tid>210</tid>
+								<cid>4</cid>
+								<description>�C���a�U�ɽu���O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-211.C-0">
+								<tid>211</tid>
+								<cid>0</cid>
+								<description>�C���[�ű���u</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-211.C-1">
+								<tid>211</tid>
+								<cid>1</cid>
+								<description>�C���[�ű���u</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-211.C-2">
+								<tid>211</tid>
+								<cid>2</cid>
+								<description>�C���[�ű���u���׵��O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-212.C-0">
+								<tid>212</tid>
+								<cid>0</cid>
+								<description>�C���[�ųs������u</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-212.C-1">
+								<tid>212</tid>
+								<cid>1</cid>
+								<description>�C���[�ųs������u���O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-212.C-2">
+								<tid>212</tid>
+								<cid>2</cid>
+								<description>�C���[�ųs������u���׵��O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-213.C-0">
+								<tid>213</tid>
+								<cid>0</cid>
+								<description>�C���a�U�s������u</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-213.C-1">
+								<tid>213</tid>
+								<cid>1</cid>
+								<description>�C���a�U�s������u���O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-213.C-2">
+								<tid>213</tid>
+								<cid>2</cid>
+								<description>�C���a�U�s������u���׵��O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-215.C-0">
+								<tid>215</tid>
+								<cid>0</cid>
+								<description>�C���۰ʭt�������}��</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-216.C-0">
+								<tid>216</tid>
+								<cid>0</cid>
+								<description>�a�U�C���ʵ���</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+	 <!-- ���l -->			
+				<TypeCompFilter name="FSC-300.C-0">
+								<tid>300</tid>
+								<cid>0</cid>
+								<description>�q�T���l�u</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-300.C-1">
+								<tid>300</tid>
+								<cid>1</cid>
+								<description>���ֹq�l����(����)���O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-300.C-2">
+								<tid>300</tid>
+								<cid>2</cid>
+								<description>���ֹq�l��r�������O</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-300.C-3">
+								<tid>300</tid>
+								<cid>3</cid>
+								<description>���ֹq�l�޽u�Ÿ�</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>
+				<TypeCompFilter name="FSC-300.C-4">
+								<tid>300</tid>
+								<cid>4</cid>
+								<description>���ֹq�l���I�Ÿ�</description>
+								<elementCriterion>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+			<TypeCompFilter name="FSC-301.C-0">
+								<tid>301</tid>
+								<cid>0</cid>
+								<description>���O����u</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+								</elementCriterion>
+								<LineCreateStrategy/>
+				</TypeCompFilter>																																				
+				<TypeCompFilter name="FSC-999.C-0">
+								<tid>999</tid>
+								<cid>0</cid>
+								<description>���O</description>
+								<elementCriterion>
+												<elementType>4</elementType>
+												<elementType>17</elementType>
+								</elementCriterion>
+								<TextCreateStrategy/>
+				</TypeCompFilter>
+  <!-- Dummy
+  <TypeCompLevelFilter name="DemoFeature3">
+    <tid>999</tid>
+    <cid>2</cid>
+    <lid>34</lid>
+    <description>DemoFilter for DemoFeature</description>
+    <TextCreateStrategy-None/>
+  </TypeCompLevelFilter>
+  -->
+</ElementDispatcherRules>
diff --git a/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java b/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java
new file mode 100644
index 0000000..a921169
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java
@@ -0,0 +1,69 @@
+package com.ximple.eofms.filter;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.geotools.TestData;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
+
+public class ElementDispatcherTest
+{
+    private static final String TestRulesName = "testRules.xml";
+    private static final String TestConfigName = "testElementFilter.xml";
+
+    @Test
+    public void testLoadRules() throws IOException
+    {
+        File rules = TestData.file(this, TestRulesName);
+        Digester digester = DigesterLoader.createDigester(rules.toURI().toURL());
+        // File config = TestData.file(this, TestConfigName);
+        // ElementDispatcher ed = (ElementDispatcher) digester.parse(config);
+        Assert.assertNotNull(digester);
+    }
+
+    @Test
+    public void testLoadConfig() throws IOException, SAXException
+    {
+        File rules = TestData.file(this, TestRulesName);
+        Digester digester = DigesterLoader.createDigester(rules.toURI().toURL());
+        File config = TestData.file(this, TestConfigName);
+        ElementDispatcher ed = (ElementDispatcher) digester.parse(config);
+        Assert.assertNotNull(ed);
+       // Assert.assertEquals(ed.getRules().size(), 3);
+
+
+        ElementDispatchableFilter filter = ed.getRules().get(0);
+        /*
+        Assert.assertTrue(filter instanceof TypeIdDispatchableFilter);
+        TypeIdDispatchableFilter tFilter = (TypeIdDispatchableFilter) filter;
+        Assert.assertEquals(tFilter.getName(), "DemoFeature1");
+        Assert.assertEquals(tFilter.getTid(), 106);
+        Assert.assertEquals(tFilter.getElmtype(), 7);
+        Assert.assertNotNull(tFilter.getCreateStrategy());
+        Assert.assertTrue(tFilter.getCreateStrategy() instanceof CreateLineStringStrategy);
+        */
+
+        filter = ed.getRules().get(0);
+        Assert.assertTrue(filter instanceof TypeCompIdDispatchableFilter);
+        TypeCompIdDispatchableFilter tcFilter = (TypeCompIdDispatchableFilter) filter;
+        Assert.assertEquals(tcFilter.getName(), "DemoFeature2");
+        Assert.assertEquals(tcFilter.getTid(), 107);
+        Assert.assertEquals(tcFilter.getCid(), 11);
+        Assert.assertNotNull(tcFilter.getCreateStrategy());
+        Assert.assertTrue(tcFilter.getCreateStrategy() instanceof CreateTextStrategy);
+        /*
+        filter = ed.getRules().get(2);
+        Assert.assertTrue(filter instanceof TypeCompLevelIdDispatchableFilter);
+        TypeCompLevelIdDispatchableFilter tclFilter = (TypeCompLevelIdDispatchableFilter) filter;
+        Assert.assertEquals(tclFilter.getName(), "DemoFeature3");
+        Assert.assertEquals(tclFilter.getTid(), 108);
+        Assert.assertEquals(tclFilter.getCid(), 2);
+        Assert.assertEquals(tclFilter.getLid(), 34);
+        Assert.assertNull(tclFilter.getCreateStrategy());
+        */
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml
new file mode 100644
index 0000000..2be77e2
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+  <TypeFilter name="DemoFeature1">
+    <tid>106</tid>
+    <description>DemoFilter for DemoFeature</description>
+    <elementCriterion>
+      <elementtype>7</elementtype>
+      <elementtype>17</elementtype>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeFilter>
+  <TypeCompFilter name="DemoFeature2">
+    <tid>107</tid>
+    <cid>11</cid>
+    <description>DemoFilter for DemoFeature</description>
+    <elementCriterion>
+      <elementtype>7</elementtype>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompLevelFilter name="DemoFeature3">
+    <tid>108</tid>
+    <cid>2</cid>
+    <lid>34</lid>
+    <elementCriterion>
+      <elementtype>7</elementtype>
+      <elementtype>17</elementtype>
+    </elementCriterion>
+    <description>DemoFilter for DemoFeature</description>
+    <TextCreateStrategy-None/>
+  </TypeCompLevelFilter>
+</ElementDispatcherRules>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml
new file mode 100644
index 0000000..e9da319
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml
@@ -0,0 +1,74 @@
+<?xml version='1.0' encoding="big5"?>
+<!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "digester-rules.dtd">
+<digester-rules>
+  <pattern value="ElementDispatcherRules">
+    <object-create-rule classname="com.ximple.eofms.filter.ElementDispatcher"/>
+    <set-properties-rule/>
+    <pattern value="TypeFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="elmtype"/>
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompLevelFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="elmtype"/>
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <bean-property-setter-rule pattern="lid"/>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+  </pattern>
+</digester-rules>
\ No newline at end of file

--
Gitblit v0.0.0-SNAPSHOT