From 9299ee8ad709aaf4131af10afeeb2e43b374fba3 Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Mon, 10 Mar 2008 01:53:54 +0800
Subject: [PATCH] update for EOFM-8

---
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java        |  114 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java          |  166 +
 .gitattributes                                                         |   27 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java   |   31 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java |  230 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java             |  271 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java   |   18 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java      |   58 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java   |  166 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java                |  263 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java             |  238 +
 ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java           | 4774 +++++++++++++++++++++++++++++
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java        |   56 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java     |  291 +
 ximple-dgnio/pom.xml                                                   |  443 ++
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java       |   45 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java   |   34 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java |  193 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java      |  718 ++++
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java |   71 
 ximple-dgnio/.project                                                  |   23 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java      |   16 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java         |  534 +++
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java         |  129 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java    |  246 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java          |   90 
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java         |  296 +
 ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java     |   23 
 28 files changed, 9,564 insertions(+), 0 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 8e948c5..7715a78 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,28 @@
 * text=auto !eol
+ximple-dgnio/.project svneol=native#text/xml
+ximple-dgnio/pom.xml svneol=native#text/xml
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java svneol=native#text/plain
+ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java svneol=native#text/plain
diff --git a/ximple-dgnio/.project b/ximple-dgnio/.project
new file mode 100644
index 0000000..4198653
--- /dev/null
+++ b/ximple-dgnio/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ximple-dgnio</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/ximple-dgnio/pom.xml b/ximple-dgnio/pom.xml
new file mode 100644
index 0000000..7cacdd1
--- /dev/null
+++ b/ximple-dgnio/pom.xml
@@ -0,0 +1,443 @@
+<?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>
+  <groupId>com.ximple</groupId>
+  <artifactId>ximple-dgnio</artifactId>
+  <packaging>pom</packaging>
+  <version>1.0.0-SNAPSHOT</version>
+  <name>ximple-dgnio-1.0.x</name>
+
+  <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>
+
+  <!-- =========================================================== -->
+  <!--     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>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.vividsolutions</groupId>
+      <artifactId>jts</artifactId>
+      <version>1.9</version>
+    </dependency>
+    <dependency>
+      <groupId>com.testng</groupId>
+      <artifactId>testng</artifactId>
+      <classifier>jdk15</classifier>
+      <version>5.7</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+      <version>3.2</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.poi</groupId>
+      <artifactId>poi-contrib</artifactId>
+      <version>3.0.1-FINAL</version>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.15</version>
+      <!-- Same as the dependency in commons-logging -->
+    </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.2</version>
+          <!-- Current version is 2.3, but it cause the following exception:
+
+                    Running org.geotools.gce.imagepyramid.ImagePyramidReaderTest
+                    Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode.
+                    Occurs in: com.sun.media.jai.mlib.MediaLibAccessor
+                    java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image
+                        at com.sun.media.jai.mlib.MediaLibAccessor.setUseMlib(MediaLibAccessor.java:245)
+                        at com.sun.media.jai.mlib.MlibAffineRIF.create(MlibAffineRIF.java:71)
+                    -->
+        </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} -Dorg.geotools.test.extensive=${extensive.tests}
+            -Dorg.geotools.test.interactive=${interactive.tests} -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>
+
+          <!-- The two following options have the opposite value of what we would
+               like. They are that way because they don't seem to work as expected
+               with Surefire 2.3. TODO: Try again when Surefire 2.4 will be available. -->
+
+          <!-- 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>
+  </build>
+
+  <!-- =========================================================== -->
+  <!--     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>
+</project>
\ No newline at end of file
diff --git a/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java
new file mode 100644
index 0000000..0e0a507
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
new file mode 100644
index 0000000..3407d2a
--- /dev/null
+++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
@@ -0,0 +1,230 @@
+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
+{
+    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)
+            {
+                return ((LineStringElement) element).toGeometry(factory);
+            } else if (element instanceof LineElement)
+            {
+                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)
+            {
+                list.add(((LineStringElement) element).toGeometry(factory));
+            } else if (element instanceof LineElement)
+            {
+                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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java
new file mode 100644
index 0000000..08fa5ae
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
new file mode 100644
index 0000000..6bbda8b
--- /dev/null
+++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
@@ -0,0 +1,193 @@
+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)
+            {
+                list.add(((LineStringElement) element).toGeometry(factory));
+            } else if (element instanceof LineElement)
+            {
+                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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java
new file mode 100644
index 0000000..f5936c7
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java
new file mode 100644
index 0000000..0d9f1b8
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java
new file mode 100644
index 0000000..0d50d54
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java
new file mode 100644
index 0000000..475932c
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
new file mode 100644
index 0000000..8bdb62c
--- /dev/null
+++ b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
@@ -0,0 +1,271 @@
+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;
+    short[]                 raw;
+    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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java
new file mode 100644
index 0000000..dda70b2
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java
new file mode 100644
index 0000000..3707168
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java
new file mode 100644
index 0000000..2ac6fdc
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
new file mode 100644
index 0000000..c98b9f3
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java
new file mode 100644
index 0000000..8b0a2bc
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java
new file mode 100644
index 0000000..bc163af
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java
new file mode 100644
index 0000000..e51dbf2
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
new file mode 100644
index 0000000..4f31770
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java
new file mode 100644
index 0000000..729413c
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java
new file mode 100644
index 0000000..db92597
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java
new file mode 100644
index 0000000..fe4de4a
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
new file mode 100644
index 0000000..3fc5fca
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
new file mode 100644
index 0000000..7991b77
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java
new file mode 100644
index 0000000..5f0a9fe
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java b/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Utility.java
new file mode 100644
index 0000000..82a4c67
--- /dev/null
+++ b/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/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java b/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java
new file mode 100644
index 0000000..de4c574
--- /dev/null
+++ b/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;
+                }
+            }
+        }
+    }
+}

--
Gitblit v0.0.0-SNAPSHOT