From 92fe8e29a4df7ad12b2f13661f1c326b6b78644a Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Wed, 28 May 2008 16:29:51 +0800
Subject: [PATCH] tag 0.3a

---
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java            |  305 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java                  |   39 
 xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java              |   69 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn                |    0 
 .gitattributes                                                                                           |   93 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java                |  112 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/GeneralDgnConvertJobContext.java          |  554 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java                                      |  274 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java                                 |  323 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java                    |  508 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java                                 |  114 
 xdgnjobs/ximple-spatialjob/pom.xml                                                                       |   73 
 xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml    | 1406 ++
 xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java                         |   95 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java         |   97 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java                    |   12 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java                                 |   62 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java              |   16 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java                         |  363 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java        |   96 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java                             |  104 
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml                          |   17 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/FeatureDgnConvertJobContext.java          |  280 
 xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java                     |   19 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java                                 |  162 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn                         |    0 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java                 |  142 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java                                  |  385 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/EllipseElement.java                               |  211 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java                            |   24 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java              |  347 
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml                           | 1465 ++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java              |  917 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java                                  |  340 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java                    |  363 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java |   26 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java      |   69 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java           |  147 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java             |  326 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java                                   |  231 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java          |   12 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java                                         |  263 
 xdgnjobs/ximple-jobcarrier/pom.xml                                                                       |  141 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java                                    | 4774 +++++++++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java                              |  289 
 xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties                                           |   28 
 xdgnjobs/ximple-dgnio/pom.xml                                                                            |   70 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java                              |   23 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn                      |    0 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java                            |   34 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java                               |  551 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java                          |   71 
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml         |  161 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java                    |  180 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java         |   75 
 xdgnjobs/pom.xml                                                                                         |  750 +
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java                           |  119 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java                            |   18 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java                      |  758 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java                                |   45 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java              |   48 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java                             |  243 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java                               |  722 +
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java                               |   58 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java                             |   30 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java                               |   16 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java                                |   24 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java                                  |  130 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java             |  164 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java        |  297 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java                         |  125 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java             |   60 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java                  |  314 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn                           |    0 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java                                   |   94 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties                                          |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java          |   15 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java                          |  247 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java           |   82 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java               |  126 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java  |  108 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn                      |    0 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java               |   48 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml                                            |   99 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/IndexDgnConvertJobContext.java            |  355 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java                         | 4774 +++++++++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java                                      |  259 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java                            |  171 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractDgnFileJobContext.java            |   82 
 xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml            |  161 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java                  |   97 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java                          |  217 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java       |  101 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java                          | 2923 +++++
 94 files changed, 30,766 insertions(+), 0 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 8e948c5..47b69e9 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,94 @@
 * text=auto !eol
+xdgnjobs/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-dgnio/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java -text
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/EllipseElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java svneol=native#text/plain
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn -text
+xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn -text
+xdgnjobs/ximple-jobcarrier/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties svneol=native#text/plain
+xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml svneol=native#text/xml
+xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractDgnFileJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/FeatureDgnConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/GeneralDgnConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/IndexDgnConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml svneol=native#text/xml
+xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml svneol=native#text/xml
diff --git a/xdgnjobs/pom.xml b/xdgnjobs/pom.xml
new file mode 100644
index 0000000..cd8219d
--- /dev/null
+++ b/xdgnjobs/pom.xml
@@ -0,0 +1,750 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- =======================================================================
+  Maven Project Configuration File
+  
+  The Ximple DgnIO Project
+  http://www.ximple.com.tw/
+  
+  Version: $Id$
+  ======================================================================= -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+  http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <properties>
+    <oracle.jdbc>true</oracle.jdbc>
+    <test.maxHeapSize>512M</test.maxHeapSize>
+    <src.output>${basedir}/target</src.output>
+    <java5>1.5</java5>
+    <xdgnio.version>0.3.0</xdgnio.version>
+    <gt.version>2.4.2</gt.version>
+    <failIfNoTests>false</failIfNoTests>
+  </properties>
+
+  <profiles>
+    <profile>
+      <id>java5</id>
+      <reporting>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <source>1.5</source>
+            </configuration>
+          </plugin>
+        </plugins>
+      </reporting>
+    </profile>
+    <profile>
+      <id>site.ximple.tw</id>
+      <distributionManagement>
+        <site>
+          <id>artifactorysite-ximple-tw</id>
+          <name>Artifactory Web site for Maven reports</name>
+          <url>scp://www.ximple.com.tw/home/www/artfactory/libs-releases@repo</url>
+        </site>
+      </distributionManagement>
+    </profile>
+  </profiles>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/</url>
+  </scm>
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-dgnjobs</artifactId>
+  <packaging>pom</packaging>
+  <version>0.3.0</version>
+  <name>ximple-dgnjobs</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <description>Ximple Spatial Data Job for Quartz</description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <!-- =========================================================== -->
+  <!--     Issue managements and mailing lists.                    -->
+  <!-- =========================================================== -->
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://www.ximple.com.tw/jira/browse/EOFMS</url>
+  </issueManagement>
+
+  <!-- =========================================================== -->
+  <!--    Continuous Integration                                   -->
+  <!-- =========================================================== -->
+  <ciManagement>
+    <system>continuum</system>
+  </ciManagement>
+
+  <mailingLists>
+    <mailingList>
+    </mailingList>
+  </mailingLists>
+
+  <developers>
+    <developer>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependency Management                                   -->
+  <!--     If a POM declares one of those dependencies, then it    -->
+  <!--     will use the version specified here. Otherwise, those   -->
+  <!--     dependencies are ignored.                               -->
+  <!-- =========================================================== -->
+  <dependencyManagement>
+    <dependencies>
+      <!-- GeoAPI and its dependencies -->
+      <dependency>
+        <groupId>org.opengis</groupId>
+        <artifactId>geoapi-nogenerics</artifactId>
+        <version>2.1.0</version>
+      </dependency>
+      <dependency>
+        <groupId>javax.units</groupId>
+        <artifactId>jsr108</artifactId>
+        <version>0.01</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.vividsolutions</groupId>
+        <artifactId>jts</artifactId>
+        <version>1.9</version>
+      </dependency>
+
+      <!-- Apache -->
+      <!--
+      <dependency>
+        <groupId>commons-beanutils</groupId>
+        <artifactId>commons-beanutils</artifactId>
+        <version>1.7</version>
+      </dependency>
+      -->
+      <dependency>
+        <groupId>commons-collections</groupId>
+        <artifactId>commons-collections</artifactId>
+        <version>3.2.1</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-digester</groupId>
+        <artifactId>commons-digester</artifactId>
+        <version>1.8</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-pool</groupId>
+        <artifactId>commons-pool</artifactId>
+        <version>1.4</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-logging</groupId>
+        <artifactId>commons-logging</artifactId>
+        <version>1.1.1</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-transaction</groupId>
+        <artifactId>commons-transaction</artifactId>
+        <version>1.2</version>
+      </dependency>
+      <dependency>
+        <groupId>log4j</groupId>
+        <artifactId>log4j</artifactId>
+        <version>1.2.15</version>
+        <!-- Same as the dependency in commons-logging -->
+      </dependency>
+      <dependency>
+        <groupId>org.apache.poi</groupId>
+        <artifactId>poi-contrib</artifactId>
+        <version>3.0.2-FINAL</version>
+      </dependency>
+
+      <!-- geotools -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-api</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-main</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-shapefile</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-sample-data</artifactId>
+        <version>${gt.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-data</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+
+      <!-- because main and sample-data depend on referencing we need a tie breaker -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-referencing</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>jdom</groupId>
+        <artifactId>jdom</artifactId>
+        <version>1.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>velocity</groupId>
+        <artifactId>velocity</artifactId>
+        <version>1.4</version>
+      </dependency>
+
+      <!-- We need this to make the referencing module useful -->
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-epsg-hsql</artifactId>
+        <version>${gt.version}</version>
+        <scope>test</scope>
+      </dependency>
+
+      <!-- ORACLE -->
+      <!-- Download and install into your own repo -->
+      <dependency>
+        <artifactId>ojdbc5</artifactId>
+        <groupId>com.oracle</groupId>
+        <version>11.1.0</version>
+      </dependency>
+
+      <!-- opensymphony -->
+      <dependency>
+        <groupId>opensymphony</groupId>
+        <artifactId>quartz</artifactId>
+        <version>1.6.0</version>
+      </dependency>
+
+      <!-- Ximple Library -->
+      <dependency>
+        <artifactId>ximple-dgnio</artifactId>
+        <groupId>com.ximple</groupId>
+        <version>${xdgnio.version}</version>
+      </dependency>
+
+      <!-- Tests or legacy -->
+      <dependency>
+        <groupId>org.testng</groupId>
+        <artifactId>testng</artifactId>
+        <version>5.7</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>geoapi-nogenerics</artifactId>
+      <groupId>org.opengis</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>jsr108</artifactId>
+      <groupId>javax.units</groupId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.vividsolutions</groupId>
+      <artifactId>jts</artifactId>
+    </dependency>
+
+    <!-- Apache -->
+    <dependency>
+      <artifactId>commons-collections</artifactId>
+      <groupId>commons-collections</groupId>
+    </dependency>
+    <dependency>
+      <groupId>commons-digester</groupId>
+      <artifactId>commons-digester</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-pool</groupId>
+      <artifactId>commons-pool</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-transaction</groupId>
+      <artifactId>commons-transaction</artifactId>
+    </dependency>
+    <dependency>
+      <artifactId>log4j</artifactId>
+      <groupId>log4j</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>gt2-api</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-main</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-shapefile</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-sample-data</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <artifactId>gt2-data</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+
+    <!-- because main and sample-data depend on referencing we need a tie breaker -->
+    <dependency>
+      <artifactId>gt2-referencing</artifactId>
+      <groupId>org.geotools</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>jdom</artifactId>
+      <groupId>jdom</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>velocity</artifactId>
+      <groupId>velocity</groupId>
+    </dependency>
+
+    <!-- We need this to make the referencing module useful -->
+    <dependency>
+      <artifactId>gt2-epsg-hsql</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- ORACLE -->
+    <dependency>
+      <artifactId>ojdbc5</artifactId>
+      <groupId>com.oracle</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>testng</artifactId>
+      <groupId>org.testng</groupId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <!-- =========================================================== -->
+  <!--     Build Configuration                                     -->
+  <!-- =========================================================== -->
+  <build>
+    <!-- ========================================================= -->
+    <!--   Maven plugins dependencies management.                  -->
+    <!--   It should not be needed since Maven select by default   -->
+    <!--   the latest plugins. Unfortunatly, experience shows that -->
+    <!--   new plugin releases sometime introduce new bugs that    -->
+    <!--   break our build. So it is saferto specify plugin        -->
+    <!--   versions that are known to work.  This list is in       -->
+    <!--   alphabetical order for easier comparaison with latest   -->
+    <!--   plugins at                                              -->
+    <!--   http://www.ibiblio.org/maven2/org/apache/maven/plugins/ -->
+    <!-- ========================================================= -->
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <version>2.1</version>
+          <configuration>
+            <descriptors>
+              <descriptor>build/maven/assembly/binaryDist.xml</descriptor>
+              <descriptor>build/maven/assembly/sourceDist.xml</descriptor>
+            </descriptors>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clean-plugin</artifactId>
+          <version>2.1.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clover-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>2.0.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-install-plugin</artifactId>
+          <version>2.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jar-plugin</artifactId>
+          <version>2.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-plugin-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-project-info-reports-plugin</artifactId>
+          <version>2.0.1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-resources-plugin</artifactId>
+          <version>2.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-site-plugin</artifactId>
+          <version>2.0-beta-5</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>2.4.2</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-report-plugin</artifactId>
+          <version>2.3</version>
+        </plugin>
+
+        <!-- http://www.ibiblio.org/maven2/org/codehaus/mojo/ -->
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>changelog-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>changes-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>jxr-maven-plugin</artifactId>
+          <version>2.0-beta-1</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>taglist-maven-plugin</artifactId>
+          <version>2.0</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>jalopy-maven-plugin</artifactId>
+          <version>1.0-SNAPSHOT</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-source-plugin</artifactId>
+          <configuration>
+            <outputDirectory>${src.output}</outputDirectory>
+            <attach>false</attach>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-eclipse-plugin</artifactId>
+          <version>2.4</version>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+    <!-- http://www.ibiblio.org/maven2/org/apache/maven/wagon/ -->
+    <extensions>
+      <extension>
+        <groupId>org.apache.maven.wagon</groupId>
+        <artifactId>wagon-webdav</artifactId>
+        <version>1.0-beta-2</version>
+      </extension>
+    </extensions>
+
+    <plugins>
+      <!-- ======================================================= -->
+      <!--     Source reformat                                     -->
+      <!--       (activated only on request, jalopy:format)        -->
+      <!--     See developer's guide for automated activation      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>jalopy-maven-plugin</artifactId>
+        <configuration>
+          <convention>gt2/jalopygeotools.xml</convention>
+          <failOnError>false</failOnError>
+        </configuration>
+        <dependencies>
+          <dependency>
+            <groupId>org.geotools.maven</groupId>
+            <artifactId>gt2-build-configs</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Compilation.                                        -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <!-- The -source argument for the Java compiler. -->
+          <target>1.5</target>
+          <!-- The -target argument for the Java compiler. -->
+          <debug>true</debug>
+          <!-- Whether to include debugging information.   -->
+          <encoding>ISO-8859-1</encoding>
+          <!-- The -encoding argument for the Java compiler. -->
+        </configuration>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Tests.                                              -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>**/*Test.java</include>
+          </includes>
+          <excludes>
+            <exclude>${online.skip.pattern}</exclude>
+            <exclude>${stress.skip.pattern}</exclude>
+          </excludes>
+          <argLine>-Xmx${test.maxHeapSize} -Djava.awt.headless=${java.awt.headless}
+          </argLine>
+          <!-- Ignores test failure only if we are generating a       -->
+          <!-- report for publication on the web site. See the        -->
+          <!-- profiles section at the begining of this pom.xml file. -->
+          <testFailureIgnore>
+            ${allow.test.failure.ignore}
+          </testFailureIgnore>
+
+          <!-- Option to print summary of test suites or just print the test cases that has errors. -->
+          <printSummary>true</printSummary>
+          <!-- Redirect the unit test standard output to a file. -->
+          <redirectTestOutputToFile>false</redirectTestOutputToFile>
+        </configuration>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     Code coverage                                       -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-clover-plugin</artifactId>
+        <configuration>
+          <jdk>1.5</jdk>
+          <licenseLocation>
+            http://svn.geotools.org/geotools/branches/2.4.x/build/maven/build-configs/src/main/resources/gt2/clover.license
+          </licenseLocation>
+          <flushPolicy>directed</flushPolicy>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>pre-site</phase>
+            <goals>
+              <goal>instrument</goal>
+              <!-- aggregation is disabled due to the bug:     -->
+              <!-- http://jira.codehaus.org/browse/MCLOVER-34  -->
+            </goals>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>org.geotools.maven</groupId>
+            <artifactId>gt2-build-configs</artifactId>
+            <version>${project.version}</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+
+
+      <!-- ======================================================= -->
+      <!--     JAR packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <!-- ======================================================= -->
+      <!--     Source packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <inherited>true</inherited>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <configuration>
+          <attach>true</attach>
+        </configuration>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <resources>
+      <!--
+      <resource>
+        <targetPath>conf</targetPath>
+        <filtering>false</filtering>
+        <directory>${basedir}/src/main/resources/conf</directory>
+        <includes>
+          <include>configuration.xml</include>
+        </includes>
+        <excludes>
+          <exclude>**/*.properties</exclude>
+        </excludes>
+      </resource>
+        -->
+    </resources>
+  </build>
+
+  <distributionManagement>
+    <repository>
+      <uniqueVersion>false</uniqueVersion>
+      <id>ximple</id>
+      <name>Ximple - Artifactory lib repo</name>
+      <url>dav:http://www.ximple.com.tw/artifactory/libs-releases</url>
+    </repository>
+    <snapshotRepository>
+      <uniqueVersion>false</uniqueVersion>
+      <id>ximple-snapshots</id>
+      <name>Ximple - Artifactory lib-snapshots repo</name>
+      <url>dav:http://www.ximple.com.tw/artifactory/libs-snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <!-- =========================================================== -->
+  <!--     Repositories (ibiblio, refractions...).                 -->
+  <!--     This is where Maven looks for dependencies.             -->
+  <!-- =========================================================== -->
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <name>Ximple Artifactory Maven Repository Switchboard</name>
+      <url>http://www.ximple.com.tw/artifactory/repo</url>
+    </repository>
+
+    <repository>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <id>snapshots</id>
+      <name>Ximple Artifactory Maven Repository Switchboard</name>
+      <url>http://www.ximple.com.tw/artifactory/repo</url>
+    </repository>
+  </repositories>
+
+
+  <!-- =========================================================== -->
+  <!--     Plugin repositories.                                    -->
+  <!--     This is where Maven looks for plugin dependencies.      -->
+  <!-- =========================================================== -->
+  <pluginRepositories>
+    <pluginRepository>
+      <id>ximple-snapshots</id>
+      <name>ximple-shapshots</name>
+      <url>http://www.ximple.com.tw/artifactory/vplugins-snapshots</url>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+    </pluginRepository>
+    <pluginRepository>
+      <id>ximple</id>
+      <name>Ximple Maven 2 Repository</name>
+      <url>http://www.ximple.com.tw/artifactory/vplugins-releases</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <releases>
+        <enabled>true</enabled>
+      </releases>
+    </pluginRepository>
+  </pluginRepositories>
+
+  <!-- =========================================================== -->
+  <!--     Modules for the build in approximate dependency order   -->
+  <!-- =========================================================== -->
+  <modules>
+    <module>ximple-dgnio</module>
+    <module>ximple-spatialjob</module>
+    <module>ximple-jobcarrier</module>
+  </modules>
+</project>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-dgnio/pom.xml b/xdgnjobs/ximple-dgnio/pom.xml
new file mode 100644
index 0000000..8140195
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.3.0</version>
+  </parent>
+  
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-dgnio</artifactId>
+  <version>0.3.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-dgnio-1.0.x</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url>
+  </scm>
+
+  <description>
+    Ximple Dgn IO Library
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+  </dependencies>
+
+  <!-- =========================================================== -->
+  <!--     Build Configuration                                     -->
+  <!-- =========================================================== -->
+  <build>
+    <plugins>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java
new file mode 100644
index 0000000..bd452df
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ArcElement.java
@@ -0,0 +1,231 @@
+package com.ximple.io.dgn7;
+
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * ArcElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/26 �U�� 06:41:45
+ */
+public class ArcElement extends Element implements GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(ArcElement.class);
+
+    public ArcElement(byte[] raw)
+    {
+        super(raw);
+    }
+
+    public double getStartAngle()
+    {
+        int angle = (raw[18] & 0x0000ffff) << 16 | (raw[19] & 0x0000ffff);
+        return DgnUtility.converIntToRotation(angle);
+    }
+
+    public void setStartAngle(double value)
+    {
+        int angle = DgnUtility.converRotatioToInt(value);
+
+        raw[18] = (short) (angle >>> 16 & 0x0000ffff);
+        raw[19] = (short) (angle & 0x0000ffff);
+    }
+
+    public double getSweepAngle()
+    {
+        int angle = (raw[20] & 0x0000ffff) << 16 | (raw[21] & 0x0000ffff);
+        if (angle < 0)
+            angle = -1 * (angle & 0x7fffffff);
+
+        return DgnUtility.converIntToRotation(angle);
+    }
+
+    public void setSweepAngle(double value)
+    {
+        int angle = DgnUtility.converRotatioToInt(value);
+        if (angle < 0)
+        {
+            angle &= 0x7fffffff;
+            angle |= 0x80000000;
+        }
+
+        raw[20] = (short) (angle >> 16 & 0x0000ffff);
+        raw[21] = (short) (angle & 0x0000ffff);
+    }
+
+    public double getPrimary()
+    {
+        rawBuffer.position(22 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] primary = new byte[8];
+        rawBuffer.get(primary);
+        rawBuffer.order(bo);
+        return DgnUtility.convertDGNToIEEEDouble(primary) / 1000.0;
+    }
+
+    public void setPrimary(double value)
+    {
+        double temp = value * 1000.0;
+        short[] primary = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(primary, 0, raw, 22, 4);
+    }
+
+    public double getSecondary()
+    {
+        rawBuffer.position(26 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] secondary = new byte[8];
+        rawBuffer.get(secondary);
+        rawBuffer.order(bo);
+        return DgnUtility.convertDGNToIEEEDouble(secondary) / 1000.0;
+    }
+
+    public void setSecondary(double value)
+    {
+        double temp = value * 1000.0;
+        short[] secondary = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(secondary, 0, raw, 26, 4);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (raw[30] << 16 & 0xffff0000);
+        rotation |= raw[31] & 0x0000ffff;
+
+        return DgnUtility.converIntToRotation(rotation);
+    }
+
+    public void setRotationAngle(double value)
+    {
+        int angle = DgnUtility.converRotatioToInt(value);
+
+        raw[30] = (short) (angle >> 16 & 0x0000ffff);
+        raw[31] = (short) (angle & 0x0000ffff);
+    }
+
+    public Coordinate getOrigin()
+    {
+        rawBuffer.position(32 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] rawValue = new byte[8];
+
+        rawBuffer.get(rawValue); // x
+        double dx = DgnUtility.converUnitToCoord(DgnUtility.convertDGNToIEEEDouble(rawValue));
+
+        rawBuffer.get(rawValue); // y
+        double dy = DgnUtility.converUnitToCoord(DgnUtility.convertDGNToIEEEDouble(rawValue));
+
+        rawBuffer.order(bo);
+
+        return new Coordinate(dx, dy);
+    }
+
+    public void setOrigin(Coordinate value)
+    {
+        double temp = DgnUtility.converCoordToUnit(value.x);
+        short[] x = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(x, 0, raw, 32, 4);
+        temp = DgnUtility.converCoordToUnit(value.y);
+
+        short[] y = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(y, 0, raw, 36, 4);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        double sweep = getSweepAngle();
+        double temp = Math.abs(sweep);
+        temp /= 4;
+        int pts = (temp < 3) ? 3 : (int) temp;
+        return factory.createLineString(convertToLineString(pts));
+    }
+
+    private Coordinate[] convertToLineString(int pts)
+    {
+        ArrayList<Coordinate> result = new ArrayList<Coordinate>();
+        double beta = DgnUtility.converRotationToRadian(-getRotationAngle());
+        double startAngle = getStartAngle();
+        double sweepAngle = getSweepAngle();
+        double endAngle = startAngle + sweepAngle;
+        double steps = sweepAngle / pts;
+        double current;
+        if (sweepAngle < 0)
+        {
+            for (current = startAngle; current > endAngle; current += steps)
+            {
+                Coordinate pt = computePointOnArcByAngle(beta, current);
+                result.add(pt);
+            }
+
+        } else
+        {
+            for (current = startAngle; current < endAngle; current += steps)
+            {
+                Coordinate pt = computePointOnArcByAngle(beta, current);
+                result.add(pt);
+            }
+        }
+
+        Coordinate pt = computePointOnArcByAngle(beta, endAngle);
+        result.add(pt);
+
+        return result.toArray(new Coordinate[result.size()]);
+    }
+
+    private Coordinate computePointOnArcByAngle(double beta, double current)
+    {
+        double sinbeta = Math.sin(beta);
+        double cosbeta = Math.cos(beta);
+        Coordinate pt = new Coordinate();
+        double alpha = DgnUtility.converRotationToRadian(current);
+        double sinalpha = Math.sin(alpha);
+        double cosalpha = Math.cos(alpha);
+        pt.x = getOrigin().x + (getPrimary() * cosalpha * cosbeta -
+                getSecondary() * sinalpha * sinbeta);
+        pt.y = getOrigin().y + (getPrimary() * cosalpha * sinbeta +
+                getSecondary() * sinalpha * cosbeta);
+        return pt;
+    }
+
+    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(byte[] raw)
+        {
+            return new ArcElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
new file mode 100644
index 0000000..001f83a
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
@@ -0,0 +1,247 @@
+package com.ximple.io.dgn7;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.CoordinateList;
+import com.vividsolutions.jts.geom.Geometry;
+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
+{
+    private static final Logger logger = Logger.getLogger(ComplexChainElement.class);
+
+    protected ArrayList<Element> list = new ArrayList<Element>();
+
+    public ComplexChainElement(byte[] 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((Element) 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) element);
+    }
+
+    public void add(int index, Object element)
+    {
+        list.add(index, (Element) 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)
+    {
+        if (size() == 1)
+        {
+            Element element = (Element) get(0);
+
+            if (element instanceof LineStringElement)
+            {
+                if (((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+                {
+                    return ((LineStringElement) element).toGeometry(factory);
+                }
+
+            } else if (element instanceof LineElement)
+            {
+                if (((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1)
+                {
+                    return ((LineElement) element).toGeometry(factory);
+                }
+
+            } else
+            {
+                if (element instanceof GeometryConverter)
+                {
+                    return ((GeometryConverter) element).toGeometry(factory);
+                }
+
+                return null;
+            }
+        }
+
+        CoordinateList pts = new CoordinateList();
+        for (ListIterator it = listIterator(); it.hasNext();)
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof LineStringElement)
+            {
+                if (((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+                {
+                    pts.add(((LineStringElement) element).toGeometry(factory).getCoordinates(), true);
+                }
+
+            } else if (element instanceof LineElement)
+            {
+
+                if (((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1)
+                {
+                    pts.add(((LineElement) element).toGeometry(factory).getCoordinates(), true);
+                }
+            } else if (element instanceof ArcElement)
+            {
+                pts.add(((ArcElement) element).toGeometry(factory).getCoordinates(), true);
+            }
+        }
+
+        return factory.createLineString(pts.toCoordinateArray());
+    }
+
+    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(byte[] raw)
+        {
+            return new ComplexChainElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java
new file mode 100644
index 0000000..08fa5ae
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java
@@ -0,0 +1,16 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.util.List;
+
+/**
+ * ComplexElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 04:17:37
+ */
+public interface ComplexElement extends List
+{
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
new file mode 100644
index 0000000..f2d6541
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
@@ -0,0 +1,217 @@
+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 org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.CoordinateList;
+
+/**
+ * ComplexShapeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 03:45:15
+ */
+public class ComplexShapeElement extends Element implements ComplexElement, GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(ComplexShapeElement.class);
+
+    ArrayList<Element> list = new ArrayList<Element>();
+
+    public ComplexShapeElement(byte[] 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((Element) 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) element);
+    }
+
+    public void add(int index, Object element)
+    {
+        list.add(index, (Element) 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<Geometry> list = new ArrayList<Geometry>();
+
+        for (ListIterator it = listIterator(); it.hasNext(); )
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof ShapeElement)
+            {
+                if( ((ShapeElement) element).getVerticeSize() == 0 || ((ShapeElement) element).getVerticeSize() > 1)
+                {
+                  list.add(((ShapeElement) element).toGeometry(factory));
+                }
+            }
+            else if (element instanceof LineStringElement)
+            {
+               if( ((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+               {
+                 list.add(((LineStringElement) element).toGeometry(factory));
+               }
+            } else if (element instanceof LineElement)
+            {
+               if( ((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1 )
+               {
+                 list.add(((LineElement) element).toGeometry(factory));
+               }
+            } else if (element instanceof ArcElement)
+            {
+            }
+        }
+
+
+        CoordinateList pts = new CoordinateList();
+        for (Geometry geom : list)
+        {
+            pts.add(geom.getCoordinates(), true);
+        }
+
+        return factory.createLinearRing(pts.toCoordinateArray());
+    }
+
+    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(byte[] raw)
+        {
+            return new ComplexShapeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java
new file mode 100644
index 0000000..0f2cd91
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java
@@ -0,0 +1,24 @@
+package com.ximple.io.dgn7;
+
+
+public class Dgn7Exception extends Exception
+{
+    public Dgn7Exception()
+    {
+    }
+
+    public Dgn7Exception(String s)
+    {
+        super(s);
+    }
+
+    public Dgn7Exception(String s, Throwable throwable)
+    {
+        super(s, throwable);
+    }
+
+    public Dgn7Exception(Throwable throwable)
+    {
+        super(throwable);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java
new file mode 100644
index 0000000..2db498d
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7OracleReader.java
@@ -0,0 +1,243 @@
+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 (Dgn7Exception e)
+            {
+                throw new RuntimeException("initialize oralce error.", e);
+            }
+        }
+        return _element != null;
+    }
+
+    public Element next()
+    {
+        Element result = _element;
+
+        try
+        {
+            fetchElement();
+        } catch (SQLException e)
+        {
+            throw new RuntimeException("Error:" + e.getMessage(), e);
+        } catch (Dgn7Exception 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, Dgn7Exception
+    {
+        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, Dgn7Exception
+    {
+        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 Dgn7Exception("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;
+
+        // BLOB        blob        = (BLOB) rs.getBlob(1);
+        int optimalSize = blob.getChunkSize();
+        byte[] chunk = new byte[optimalSize];
+        InputStream is = blob.getBinaryStream(0);
+        ByteBuffer buffer = null;    // ByteBuffer.allocate(optimalSize);
+        int len;
+
+        try
+        {
+            while ((len = (is.read(chunk))) != -1)
+            {
+                if (buffer != null)
+                {
+                    buffer.limit(buffer.limit() + len);
+                } else
+                {
+                    buffer = ByteBuffer.allocate(len);
+                }
+
+                buffer.put(chunk);
+            }
+
+            is.close();
+            assert buffer != null;
+            buffer.position(0);
+            raw = buffer.array();
+        } catch (IOException e)
+        {
+            e.printStackTrace();
+            throw e;
+        }
+
+        return raw;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java
new file mode 100644
index 0000000..54741e5
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java
@@ -0,0 +1,24 @@
+package com.ximple.io.dgn7;
+
+public class Dgn7fileException extends Dgn7Exception
+{
+
+    public Dgn7fileException()
+    {
+    }
+
+    public Dgn7fileException(String message)
+    {
+        super(message);
+    }
+
+    public Dgn7fileException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+    public Dgn7fileException(Throwable cause)
+    {
+        super(cause);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java
new file mode 100644
index 0000000..0d50d54
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileHeader.java
@@ -0,0 +1,58 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Dgn7fileHeader
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:21:00
+ */
+public class Dgn7fileHeader
+{
+    private short  elmtype;
+    private byte[] raw;
+
+    public Dgn7fileHeader()
+    {
+    }
+
+    public void read(ByteBuffer file, boolean strict) throws IOException
+    {
+        file.order(ByteOrder.LITTLE_ENDIAN);
+        elmtype = file.getShort();
+
+        short wtf    = file.getShort();
+        int   length = (wtf * 2);
+
+        if (file.remaining() != (length))
+        {
+            Assert.shouldNeverReachHere();
+        }
+
+        raw = new byte[length];
+        file.get(raw, 0, file.remaining());
+    }
+
+    public String toString()
+    {
+        return "Dgn7fileHeader{" + "raw=" + ((raw == null) ? "null" : raw.length) + '}';
+    }
+
+    public int size()
+    {
+        if (raw == null)
+        {
+            return 0;
+        }
+
+        return raw.length + 4;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java
new file mode 100644
index 0000000..668e289
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java
@@ -0,0 +1,722 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+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 javax.swing.JFileChooser;
+import javax.swing.JFrame;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+
+/**
+ * Dgn7fileReader
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:24:10
+ */
+public class Dgn7fileReader
+{
+    private static final Logger logger = LogManager.getLogger(Dgn7fileReader.class);
+
+    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    = DgnUtility.convertFromDGN(lowCoorX);
+            record.minX = DgnUtility.converUnitToCoord(lowCoorX);
+
+            int lowCoorY = buffer.getInt();
+
+            lowCoorY    = DgnUtility.convertFromDGN(lowCoorY);
+            record.minY = DgnUtility.converUnitToCoord(lowCoorY);
+
+            int lowCoorZ = buffer.getInt();
+
+            lowCoorZ    = DgnUtility.convertFromDGN(lowCoorZ);
+            record.minZ = DgnUtility.converUnitToCoord(lowCoorZ);
+
+            int highCoorX = buffer.getInt();
+
+            highCoorX   = DgnUtility.convertFromDGN(highCoorX);
+            record.maxX = DgnUtility.converUnitToCoord(highCoorX);
+
+            int highCoorY = buffer.getInt();
+
+            highCoorY   = DgnUtility.convertFromDGN(highCoorY);
+            record.maxY = DgnUtility.converUnitToCoord(highCoorY);
+
+            int highCoorZ = buffer.getInt();
+
+            highCoorZ   = DgnUtility.convertFromDGN(highCoorZ);
+            record.maxZ = DgnUtility.converUnitToCoord(highCoorZ);
+        }
+
+        buffer.reset();
+        record.offset = record.end;
+
+        // update all the record info.
+        record.length    = elementLength;
+        record.signature = signature;
+        record.number    = recordNumber;
+
+        // remember, we read one int already...
+        record.end = this.toFileOffset(buffer.position()) + elementLength - 4;
+        // record.end = this.toFileOffset(buffer.position()) + elementLength;
+
+        // mark this position for the reader
+        record.start = buffer.position();
+
+        // clear any cached record
+        record.handler = recordType.getElementHandler();
+        record.element = null;
+
+        return record;
+    }
+
+    public void goTo(int offset) throws IOException, UnsupportedOperationException
+    {
+        if (randomAccessEnabled)
+        {
+            if (this.useMemoryMappedBuffer)
+            {
+                buffer.position(offset);
+            } else
+            {
+                /*
+                 *     Check to see if requested offset is already loaded; ensure
+                 *     that record header is in the buffer
+                 */
+                if ((this.currentOffset <= offset) && (this.currentOffset + buffer.limit() >= offset + 4))
+                {
+                    buffer.position(this.toBufferOffset(offset));
+                } else
+                {
+                    FileChannel fc = (FileChannel) this.channel;
+
+                    fc.position(offset);
+                    this.currentOffset = offset;
+                    buffer.position(0);
+                    fill(buffer, fc);
+                    buffer.position(0);
+                }
+            }
+
+            int oldRecordOffset = record.end;
+
+            record.end = offset;
+
+            try
+            {
+                hasNext();
+            } catch (IOException ioe)
+            {
+                record.end = oldRecordOffset;
+
+                throw ioe;
+            }
+        } else
+        {
+            throw new UnsupportedOperationException("Random Access not enabled");
+        }
+    }
+
+    public Record elementAt(int offset) throws IOException, UnsupportedOperationException, Dgn7fileException
+    {
+        if (randomAccessEnabled)
+        {
+            this.goTo(offset);
+
+            return nextElement();
+        }
+
+        throw new UnsupportedOperationException("Random Access not enabled");
+    }
+
+    public boolean hasNext() throws IOException
+    {
+        // mark current position
+        int position = buffer.position();
+
+        // ensure the proper position, regardless of read or handler behavior
+        try
+        {
+            buffer.position(this.toBufferOffset(record.end));
+        } catch (IllegalArgumentException e)
+        {
+            logger.warn("position=" + this.toBufferOffset(record.end), e);
+
+            return false;
+        }
+
+        // no more data left
+        if (buffer.remaining() < 4)
+        {
+            return false;
+        }
+
+        // looks good
+        boolean hasNext = true;
+        short   type    = buffer.getShort();
+
+        if (type == -1)
+        {
+            hasNext = false;
+        }
+
+        // reset things to as they were
+        buffer.position(position);
+
+        return hasNext;
+    }
+
+    private void init(boolean strict) throws IOException, Dgn7fileException
+    {
+        header = readHeader(channel, strict);
+
+        // fileElementType = header.getElementType();
+        // handler = fileElementType.getElementHandler();
+
+        // recordHeader = ByteBuffer.allocateDirect(4);
+        // recordHeader.order(ByteOrder.BIG_ENDIAN);
+        // if (handler == null)
+        // {
+        // throw new IOException("Unsuported shape type:" + fileElementType);
+        // }
+        if ((channel instanceof FileChannel) && useMemoryMappedBuffer)
+        {
+            FileChannel fc = (FileChannel) channel;
+
+            buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+
+            // buffer.position(100);
+            buffer.position(header.size());
+            this.currentOffset = 0;
+        } else
+        {
+            // force useMemoryMappedBuffer to false
+            this.useMemoryMappedBuffer = false;
+
+            // start with 8K buffer
+            buffer = ByteBuffer.allocateDirect(8 * 1024);
+            fill(buffer, channel);
+            buffer.flip();
+            this.currentOffset = header.size();
+        }
+
+        headerTransfer = ByteBuffer.allocate(4);
+        headerTransfer.order(ByteOrder.LITTLE_ENDIAN);
+
+        // make sure the record end is set now...
+        record.end = toFileOffset(buffer.position());
+    }
+
+    private int toBufferOffset(int offset)
+    {
+        return (int) (offset - currentOffset);
+    }
+
+    private int toFileOffset(int offset)
+    {
+        return (int) (currentOffset + offset);
+    }
+
+    public int getCount(int count) throws Dgn7fileException
+    {
+        try
+        {
+            if (channel == null)
+            {
+                return -1;
+            }
+
+            count = 0;
+
+            for (int tmp = readElement(); tmp != -1; tmp = readElement())
+            {
+                count += tmp;
+            }
+        } catch (IOException ioe)
+        {
+            count = -1;
+
+            // What now? This seems arbitrarily appropriate !
+            throw new Dgn7fileException("Problem reading dgnfile record", ioe);
+        }
+
+        return count;
+    }
+
+    public int getCount() throws Dgn7fileException
+    {
+        return getCount(0);
+    }
+
+    private int readElement() throws IOException
+    {
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        // burn the record number
+        buffer.getInt();
+
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        int recordlength = buffer.getInt() * 2;
+
+        // Going to read the first 4 bytes of the record so
+        // subtract that from the record length
+        recordlength -= 4;
+
+        if (!fillBuffer())
+        {
+            return -1;
+        }
+
+        // read record type (used to determine if record is a null record)
+        int type = buffer.getInt();
+
+        // go to end of record
+        while (buffer.limit() < buffer.position() + recordlength)
+        {
+            recordlength -= buffer.limit() - buffer.position();
+            buffer.clear();
+
+            if (channel.read(buffer) < 1)
+            {
+                return -1;
+            }
+        }
+
+        buffer.position(buffer.position() + recordlength);
+
+        // return 0 if record is null. Null records should be counted.
+        if (type == 0)
+        {
+            // this is a null feature
+            return 0;
+        }
+
+        return 1;
+    }
+
+    private boolean fillBuffer() throws IOException
+    {
+        int result = 1;
+
+        if (buffer.limit() <= buffer.position() + 4)
+        {
+            result = fill(buffer, channel);
+        }
+
+        return result > 0;
+    }
+
+    public static void main(String[] args)
+    {
+        JFileChooser jfc = new JFileChooser("D:/TEMP");
+        File         f   = null;
+        int          r   = jfc.showOpenDialog(new JFrame());
+
+        if (r == JFileChooser.APPROVE_OPTION)
+        {
+            try
+            {
+                f = jfc.getSelectedFile();
+
+                FileChannel    channel = new FileInputStream(f).getChannel();
+                Dgn7fileReader reader  = new Dgn7fileReader(channel, new Lock());
+
+                System.out.println(reader.getHeader().toString());
+
+                GeometryFactory factory = new GeometryFactory();
+                int             count, size;
+
+                count = 0;
+                size  = 0;
+
+                try
+                {
+                    Element lastComplex = null;
+
+                    while (reader.hasNext())
+                    {
+                        size++;
+
+                        Dgn7fileReader.Record record = reader.nextElement();
+
+                        if (record.element() != null)
+                        {
+                            Element     element = (Element) record.element();
+                            ElementType type    = element.getElementType();
+
+                            if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                            {
+                                if (lastComplex != null)
+                                {
+                                    // @todo add process in here
+                                    count++;
+                                    lastComplex = null;
+                                }
+
+                                // @todo add process in here
+                                count++;
+                            } else if (element.isComponentElement())
+                            {
+                                if (lastComplex != null)
+                                {
+                                    ((ComplexElement) lastComplex).add(element);
+                                }
+                            } else if (type.isComplexElement())
+                            {
+                                if (lastComplex == null)
+                                {
+                                    lastComplex = element;
+                                } else
+                                {
+                                    // @todo add process in here
+                                    count++;
+                                    lastComplex = element;
+                                }
+                            }
+                        }
+                    }
+                } catch (IOException e)
+                {
+                    logger.warn("Stop read dgn file", e);
+                } catch (Dgn7fileException e)
+                {
+                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                } finally
+                {
+                    reader.close();
+                }
+
+                System.out.println("count=" + count + " size=" + size);
+                // reader.close();
+            } catch (IOException ioe)
+            {
+                System.out.println(ioe);
+                ioe.printStackTrace();
+            } catch (Dgn7fileException e)
+            {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
+        }
+
+        System.exit(0);
+    }
+
+    public final class Record
+    {
+        int   length;
+        int   number = 0;
+        int   offset;           // Relative to the whole file
+        int   start     = 0;    // Relative to the current loaded buffer
+        short signature = 0;
+
+        /**
+         * The minimum X value.
+         */
+        public double minX;
+
+        /**
+         * The minimum Y value.
+         */
+        public double minY;
+
+        /**
+         * The minimum Z value.
+         */
+        public double minZ;
+
+        /**
+         * The maximum X value.
+         */
+        public double maxX;
+
+        /**
+         * The maximum Y value.
+         */
+        public double maxY;
+
+        /**
+         * The maximum Z value.
+         */
+        public double maxZ;
+
+        // ElementType type;
+        int             end     = 0;    // Relative to the whole file
+        Object          element = null;
+        IElementHandler handler;
+
+        public Object element()
+        {
+            if (element == null)
+            {
+                buffer.position(start);
+                buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+                if (handler == null)
+                {
+                    return null;
+                }
+
+                element = handler.read(buffer, signature, length);
+            }
+
+            return element;
+        }
+
+        public int offset()
+        {
+            return offset;
+        }
+
+        /**
+         * A summary of the record.
+         */
+        public String toString()
+        {
+            return "Record " + number + " length " + length + " bounds " + minX + "," + minY + " " + maxX + "," + maxY;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
new file mode 100644
index 0000000..be83dde
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
@@ -0,0 +1,274 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * Record
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:14:50
+ */
+public class Element
+{
+    public static final int CONSTRUCTION_CLASS      = 0;
+    public static final int CONSTRUCTION_RULE_CLASS = 0;
+    public static final int DIMENSION_CLASS         = 0;
+    public static final int LINEAR_PATTERNED_CLASS  = 0;
+    public static final int MAX_ELEMENT_SIZE        = 0;
+    public static final int MAX_VERTICES            = 100;
+    public static final int PATTERN_AREA            = 0;
+    public static final int PATTERN_COMPONENT_CLASS = 0;
+    public static final int PATTERN_CROSSHATCH      = 0;
+    public static final int PATTERN_HATCH           = 0;
+    public static final int PRIMARY_CLASS           = 0;
+    public static final int PRIMARY_RULE_CLASS      = 0;
+
+    protected short[]       raw;
+    protected byte          attrOffset = 0;
+    protected ByteBuffer    rawBuffer;
+
+    public Element(byte[] raw)
+    {
+        // this.raw = raw;
+        this.raw = new short[raw.length / 2];
+        rawBuffer = ByteBuffer.wrap(raw);
+        rawBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        rawBuffer.asShortBuffer().get(this.raw);
+    }
+
+    public int getLineStyle()
+    {
+        return 0;
+    }
+
+    public Envelope getRange()
+    {
+        int lowCoorX = ((raw[3] << 16) & 0xffff0000) + (raw[2] & 0x0000ffff);
+        lowCoorX = DgnUtility.convertFromDGN(lowCoorX);
+
+        int lowCoorY = ((raw[5] << 16) & 0xffff0000) + (raw[4] & 0x0000ffff);
+        lowCoorY = DgnUtility.convertFromDGN(lowCoorY);
+
+        int highCoorX = ((raw[9] << 16) & 0xffff0000) + (raw[8] & 0x0000ffff);
+        highCoorX = DgnUtility.convertFromDGN(highCoorX);
+
+        int highCoorY = ((raw[11] << 16) & 0xffff0000) + (raw[10] & 0x0000ffff);
+        highCoorY = DgnUtility.convertFromDGN(highCoorY);
+
+        return new Envelope(DgnUtility.converUnitToCoord(lowCoorX), DgnUtility.converUnitToCoord(highCoorX),
+                            DgnUtility.converUnitToCoord(lowCoorY), DgnUtility.converUnitToCoord(highCoorY));
+    }
+
+    public boolean isComponentElement()
+    {
+        return (short) ((raw[0] >>> 7) & 0x0001) == 1;
+    }
+
+    public boolean removeUserAttributeData(int iLinkageId)
+    {
+        return true;
+    }
+
+    public boolean removeUserAttributeData(int iLinkageId, int iLinkageIndex)
+    {
+        return true;
+    }
+
+    public boolean isDeleted()
+    {
+        return (short) ((raw[0] >>> 15) & 0x0001) == 1;
+    }
+
+    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);
+        }
+    }
+
+    protected static int getOffsetPosition(int offset)
+    {
+        return offset * 2;
+    }
+
+    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];
+            try
+            {
+                buffer.get(dst, 4, dst.length - 4);
+            } catch (BufferUnderflowException exception)
+            {
+                throw exception;   
+            }
+
+            ByteBuffer tmpBuffer = ByteBuffer.wrap(dst);
+            tmpBuffer.order(ByteOrder.LITTLE_ENDIAN);
+            tmpBuffer.position(0);
+            tmpBuffer.putShort(signature);
+            tmpBuffer.putShort((short) ((length / 2) - 2));
+            
+            /*
+            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(dst);
+
+            return elm;
+        }
+
+        public void write(ByteBuffer buffer, Object element)
+        {
+            buffer.put(((Element) element).rawBuffer);
+        }
+
+        public int getLength(Object element)
+        {
+            return ((Element) element).raw.length;
+        }
+
+        protected Element createElement(byte[] raw)
+        {
+            return new Element(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java
new file mode 100644
index 0000000..d3ecac1
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ElementType.java
@@ -0,0 +1,385 @@
+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()
+    {
+        return id == 2 || id == 7 || id == 12 || id == 14 || id == 18 ||
+                id == 19 || id == 106 || id == 107;
+
+    }
+
+    public boolean isPointType()
+    {
+        return id == 7 || id == 17;
+
+    }
+
+    public boolean isLineType()
+    {
+        return id == 3 || id == 4 || id == 11 || id == 12 || id == 16;
+
+    }
+
+    public boolean isPolygonType()
+    {
+        return id == 6 || id == 14;
+
+    }
+
+    public boolean isMultiPointType()
+    {
+        return id == 3 || id == 4 || id == 6 || id == 11 || id == 12 ||
+                id == 13 || id == 14 || id == 15 || id == 16 || id == 22;
+
+    }
+
+    public boolean isArcType()
+    {
+        return id == 15 || (id == 16);
+    }
+
+    /**
+     * 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 = EllipseElement.ElementHandler.getInstance();
+            break;
+
+        case 16 :
+            handler = ArcElement.ElementHandler.getInstance();
+            break;
+
+        case 17 :
+            handler = TextElement.ElementHandler.getInstance();
+            break;
+
+        default :
+            handler = null;
+        }
+
+        return handler;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/EllipseElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/EllipseElement.java
new file mode 100644
index 0000000..044d57a
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/EllipseElement.java
@@ -0,0 +1,211 @@
+package com.ximple.io.dgn7;
+
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+public class EllipseElement extends Element implements GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(EllipseElement.class);
+
+    public EllipseElement(byte[] raw)
+    {
+        super(raw);
+    }
+
+    public double getStartAngle()
+    {
+        return 0.0;
+    }
+
+    public void setStartAngle(double value)
+    {
+    }
+
+    public double getSweepAngle()
+    {
+        return 360.0;
+    }
+
+    public void setSweepAngle(double value)
+    {
+    }
+
+    public double getPrimary()
+    {
+        rawBuffer.position(18 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] primary = new byte[8];
+        rawBuffer.get(primary);
+        rawBuffer.order(bo);
+        return DgnUtility.convertDGNToIEEEDouble(primary) / 1000.0;
+    }
+
+    public void setPrimary(double value)
+    {
+        double temp = value * 1000.0;
+        short[] primary = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(primary, 0, raw, 18, 4);
+    }
+
+    public double getSecondary()
+    {
+        rawBuffer.position(22 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] secondary = new byte[8];
+        rawBuffer.get(secondary);
+        rawBuffer.order(bo);
+        return DgnUtility.convertDGNToIEEEDouble(secondary) / 1000.0;
+    }
+
+    public void setSecondary(double value)
+    {
+        double temp = value * 1000.0;
+        short[] secondary = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(secondary, 0, raw, 22, 4);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (raw[26] << 16 & 0xffff0000);
+        rotation |= raw[27] & 0x0000ffff;
+
+        return DgnUtility.converIntToRotation(rotation);
+    }
+
+    public void setRotationAngle(double value)
+    {
+        int angle = DgnUtility.converRotatioToInt(value);
+
+        raw[26] = (short) (angle >> 16 & 0x0000ffff);
+        raw[27] = (short) (angle & 0x0000ffff);
+    }
+
+    public Coordinate getOrigin()
+    {
+        rawBuffer.position(28 * 2);
+        ByteOrder bo = rawBuffer.order();
+        rawBuffer.order(ByteOrder.BIG_ENDIAN);
+        byte[] rawValue = new byte[8];
+
+        rawBuffer.get(rawValue); // x
+        double dx = DgnUtility.converUnitToCoord(DgnUtility.convertDGNToIEEEDouble(rawValue));
+
+        rawBuffer.get(rawValue); // y
+        double dy = DgnUtility.converUnitToCoord(DgnUtility.convertDGNToIEEEDouble(rawValue));
+
+        rawBuffer.order(bo);
+
+        return new Coordinate(dx, dy);
+    }
+
+    public void setOrigin(Coordinate value)
+    {
+        double temp = DgnUtility.converCoordToUnit(value.x);
+        short[] x = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(x, 0, raw, 28, 4);
+        temp = DgnUtility.converCoordToUnit(value.y);
+
+        short[] y = DgnUtility.convertIEEEDoubleToDGN(temp);
+
+        System.arraycopy(y, 0, raw, 32, 4);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        double temp = Math.abs(getStartAngle() - getSweepAngle());
+        temp /= 4;
+        int pts = (temp < 3) ? 3 : (int) temp;
+        return factory.createPolygon(factory.createLinearRing(convertToLineString(pts)), null);
+    }
+
+    private Coordinate[] convertToLineString(int pts)
+    {
+        ArrayList<Coordinate> result = new ArrayList<Coordinate>();
+        double beta = DgnUtility.converRotationToRadian(-getRotationAngle());
+        double startAngle = getStartAngle();
+        double sweepAngle = getSweepAngle();
+        double endAngle = startAngle + sweepAngle;
+        double steps = sweepAngle / pts;
+        double current;
+        if (sweepAngle < 0)
+        {
+            for (current = startAngle; current > endAngle; current += steps)
+            {
+                Coordinate pt = computePointOnArcByAngle(beta, current);
+                result.add(pt);
+            }
+
+        } else
+        {
+            for (current = startAngle; current < endAngle; current += steps)
+            {
+                Coordinate pt = computePointOnArcByAngle(beta, current);
+                result.add(pt);
+            }
+        }
+
+        Coordinate pt = computePointOnArcByAngle(beta, endAngle);
+        result.add(pt);
+
+        if (!result.get(0).equals(result.get(result.size() - 1)))
+        {
+            result.add(result.get(0));
+        }
+
+        return result.toArray(new Coordinate[result.size()]);
+    }
+
+
+    private Coordinate computePointOnArcByAngle(double beta, double current)
+    {
+        double sinbeta = Math.sin(beta);
+        double cosbeta = Math.cos(beta);
+        Coordinate pt = new Coordinate();
+        double alpha = DgnUtility.converRotationToRadian(current);
+        double sinalpha = Math.sin(alpha);
+        double cosalpha = Math.cos(alpha);
+        pt.x = getOrigin().x + (getPrimary() * cosalpha * cosbeta -
+                getSecondary() * sinalpha * sinbeta);
+        pt.y = getOrigin().y + (getPrimary() * cosalpha * sinbeta +
+                getSecondary() * sinalpha * cosbeta);
+        return pt;
+    }
+
+    public static class ElementHandler extends Element.ElementHandler
+    {
+        private static ElementHandler instance = null;
+
+        public ElementHandler()
+        {
+            super(ElementType.ELLIPSE);
+        }
+
+        public static IElementHandler getInstance()
+        {
+            if (instance == null)
+            {
+                instance = new ElementHandler();
+            }
+
+            return instance;
+        }
+
+        protected Element createElement(byte[] raw)
+        {
+            return new EllipseElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java
new file mode 100644
index 0000000..3707168
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java
@@ -0,0 +1,71 @@
+package com.ximple.io.dgn7;
+
+/**
+ * FrammeAttributeData
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 06:36:55
+ */
+public class FrammeAttributeData extends UserAttributeData
+{
+    public FrammeAttributeData(short id)
+    {
+        super(id, 7);
+    }
+
+    public FrammeAttributeData(short[] src)
+    {
+        super(src);
+    }
+
+    public short getFsc()
+    {
+        int fsc = _src[3] & 0x0000ffff;
+
+        return (short) fsc;
+    }
+
+    public int getUfid()
+    {
+        int ufid = (int) (_src[2] << 16 & 0xffff0000);
+
+        ufid += _src[1] & 0x0000ffff;
+
+        return ufid;
+    }
+
+    public byte getComponentID()
+    {
+        int cmpid = (int) (_src[5] & 0x000000ff);
+
+        return (byte) cmpid;
+    }
+
+    public byte getRuleNo()
+    {
+        int no = (int) ((_src[5] >> 8) & 0x000000ff);
+
+        return (byte) no;
+    }
+
+    public short getStatus()
+    {
+        int status = (int) (_src[4] & 0x0000ffff);
+
+        return (short) status;
+    }
+
+    public short getOccID()
+    {
+        int occid = (int) (_src[6] & 0x0000ffff);
+
+        return (short) occid;
+    }
+
+    public String toString()
+    {
+        return "FrammeData{" + getFsc() + "," + getUfid() + "," + getComponentID() + "," + getRuleNo() + "," + getStatus() + ","
+               + getOccID() + "}";
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java
new file mode 100644
index 0000000..2ac6fdc
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/GeometryConverter.java
@@ -0,0 +1,18 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * GeometryConverter
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:38:57
+ */
+public interface GeometryConverter
+{
+    public Geometry toGeometry(GeometryFactory factory);
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
new file mode 100644
index 0000000..c98b9f3
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
@@ -0,0 +1,23 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteBuffer;
+
+/**
+ * IElementHandler
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/17 �U�� 01:50:26
+ */
+public interface IElementHandler
+{
+    public ElementType getElementType();
+
+    public Object read(ByteBuffer buffer, short signature, int length);
+
+    public void write(ByteBuffer buffer, Object element);
+
+    public int getLength(Object element);
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java
new file mode 100644
index 0000000..6c2fd0e
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java
@@ -0,0 +1,130 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * LineElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:34:59
+ */
+public class LineElement extends Element implements GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(LineElement.class);
+
+    public LineElement(byte[] raw)
+    {
+        super(raw);
+    }
+
+    public Coordinate getCentroid(double dTolerance)
+    {
+        return null;
+    }
+
+    public Coordinate getEndPoint()
+    {
+        int endX = ((raw[22] << 16) & 0xffff0000) | (raw[23] & 0x0000ffff);
+        int endY = ((raw[24] << 16) & 0xffff0000) | (raw[25] & 0x0000ffff);
+
+
+        double x = DgnUtility.converUnitToCoord(endX);
+        double y = DgnUtility.converUnitToCoord(endY);
+
+        return new Coordinate(x, y);
+    }
+
+    public Coordinate getNormal()
+    {
+        return null;
+    }
+
+    public Coordinate getOrigin()
+    {
+        return null;
+    }
+
+    public Coordinate getStartPoint()
+    {
+        int startX = ((raw[18] << 16) & 0xffff0000);
+        startX = startX + (raw[19] & 0x0000ffff);
+
+        double x      = DgnUtility.converUnitToCoord(startX);
+        int    startY = ((raw[20] << 16) & 0xffff0000);
+
+        startY = startY + (raw[21] & 0x0000ffff);
+
+        double y = DgnUtility.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 DgnUtility.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(byte[] raw)
+        {
+            return new LineElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java
new file mode 100644
index 0000000..29fad45
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java
@@ -0,0 +1,171 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * LineStringElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 02:48:58
+ */
+public class LineStringElement extends Element implements GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(LineStringElement.class);
+
+    public LineStringElement(byte[] 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 += DgnUtility.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 = ((raw[19 + (4 * index)] << 16) & 0xffff0000);
+
+        x += (raw[20 + (4 * index)] & 0x0000ffff);
+
+        return DgnUtility.converUnitToCoord(x);
+    }
+
+    protected void setX(int index, double dx)
+    {
+        int newVal = DgnUtility.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 = ((raw[21 + (4 * index)] << 16) & 0xffff0000);
+        y = y + (raw[22 + (4 * index)] & 0x0000ffff);
+
+        return DgnUtility.converUnitToCoord(y);
+    }
+
+    protected void setY(int index, double dy)
+    {
+        int newVal = DgnUtility.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(byte[] raw)
+        {
+            return new LineStringElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java
new file mode 100644
index 0000000..e51dbf2
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java
@@ -0,0 +1,263 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+/**
+ * Lock
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 10:27:24
+ */
+public class Lock
+{
+    Logger logger = LogManager.getLogger("com.ximple.io.dgn7");
+
+    /**
+     * indicates a write is occurring
+     */
+    int writeLocks = 0;
+
+    /**
+     * if not null a writer is waiting for the lock or is writing.
+     */
+    Thread writer;
+
+    /**
+     * Thread->Owner map. If empty no read locks exist.
+     */
+    Map owners = new HashMap();
+
+    /**
+     * If the lock can be read locked the lock will be read and default
+     * visibility for tests
+     *
+     * @return
+     * @throws java.io.IOException
+     */
+    synchronized boolean canRead() throws IOException
+    {
+        if ((writer != null) && (writer != Thread.currentThread()))
+        {
+            return false;
+        }
+
+        if (writer == null)
+        {
+            return true;
+        }
+
+        if (owners.size() > 1)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * If the lock can be read locked the lock will be read and default
+     * visibility for tests
+     *
+     * @return
+     * @throws IOException
+     */
+    synchronized boolean canWrite() throws IOException
+    {
+        if (owners.size() > 1)
+        {
+            return false;
+        }
+
+        if ((canRead()) && ((writer == Thread.currentThread()) || (writer == null)))
+        {
+            if (owners.isEmpty())
+            {
+                return true;
+            }
+
+            if (owners.containsKey(Thread.currentThread()))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Called by shapefileReader before a read is started and before an IOStream
+     * is openned.
+     *
+     * @throws IOException
+     */
+    public synchronized void lockRead() throws IOException
+    {
+        if (!canRead())
+        {
+            while ((writeLocks > 0) || (writer != null))
+            {
+                try
+                {
+                    wait();
+                } catch (InterruptedException e)
+                {
+                    throw(IOException) new IOException().initCause(e);
+                }
+            }
+        }
+
+        assertTrue("A write lock exists that is owned by another thread", canRead());
+
+        Thread current = Thread.currentThread();
+        Owner  owner   = (Owner) owners.get(current);
+
+        if (owner != null)
+        {
+            owner.timesLocked++;
+        } else
+        {
+            owner = new Owner(current);
+            owners.put(current, owner);
+        }
+
+        logger.debug("Start Read Lock:" + owner);
+    }
+
+    private void assertTrue(String message, boolean b)
+    {
+        if (!b)
+        {
+            throw new AssertionError(message);
+        }
+    }
+
+    /**
+     * Called by ShapefileReader after a read is complete and after the IOStream
+     * is closed.
+     */
+    public synchronized void unlockRead()
+    {
+        assertTrue("Current thread does not have a readLock", owners.containsKey(Thread.currentThread()));
+
+        Owner owner = (Owner) owners.get(Thread.currentThread());
+
+        assertTrue("Current thread has " + owner.timesLocked + "negative number of locks", owner.timesLocked > 0);
+        owner.timesLocked--;
+
+        if (owner.timesLocked == 0)
+        {
+            owners.remove(Thread.currentThread());
+        }
+
+        notifyAll();
+        logger.debug("unlock Read:" + owner);
+    }
+
+    /**
+     * Called by ShapefileDataStore before a write is started and before an
+     * IOStream is openned.
+     *
+     * @throws IOException
+     */
+    public synchronized void lockWrite() throws IOException
+    {
+        Thread currentThread = Thread.currentThread();
+
+        if (writer == null)
+        {
+            writer = currentThread;
+        }
+
+        while (!canWrite())
+        {
+            try
+            {
+                wait();
+            } catch (InterruptedException e)
+            {
+                throw(IOException) new IOException().initCause(e);
+            }
+
+            if (writer == null)
+            {
+                writer = currentThread;
+            }
+        }
+
+        if (writer == null)
+        {
+            writer = currentThread;
+        }
+
+        assertTrue("The current thread is not the writer", writer == currentThread);
+        assertTrue("There are read locks not belonging to the current thread.", canRead());
+        writeLocks++;
+        logger.debug(currentThread.getName() + " is getting write lock:" + writeLocks);
+    }
+
+    /**
+     * default visibility for tests
+     */
+    synchronized int getReadLocks(Thread thread)
+    {
+        Owner owner = (Owner) owners.get(thread);
+
+        if (owner == null)
+        {
+            return -1;
+        }
+
+        return owner.timesLocked;
+    }
+
+    public synchronized void unlockWrite()
+    {
+        if (writeLocks > 0)
+        {
+            assertTrue("current thread does not own the write lock", writer == Thread.currentThread());
+            assertTrue("writeLock has already been unlocked", writeLocks > 0);
+            writeLocks--;
+
+            if (writeLocks == 0)
+            {
+                writer = null;
+            }
+        }
+
+        logger.debug("unlock write:" + Thread.currentThread().getName());
+        notifyAll();
+    }
+
+    /**
+     * default visibility for tests
+     */
+    synchronized boolean ownWriteLock(Thread thread)
+    {
+        return (writer == thread) && (writeLocks > 0);
+    }
+
+    private class Owner
+    {
+        final Thread owner;
+        int          timesLocked;
+
+        Owner(Thread owner)
+        {
+            this.owner  = owner;
+            timesLocked = 1;
+        }
+
+        public String toString()
+        {
+            return owner.getName() + " has " + timesLocked + " locks";
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
new file mode 100644
index 0000000..4f31770
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
@@ -0,0 +1,114 @@
+package com.ximple.io.dgn7;
+
+/*
+ *    GeoTools - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
+ *
+ *    This library is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU Lesser General Public
+ *    License as published by the Free Software Foundation; either
+ *    version 2.1 of the License, or (at your option) any later version.
+ *
+ *    This library is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *    Lesser General Public License for more details.
+ */
+
+// J2SE dependencies
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Utility class for managing memory mapped buffers.
+ *
+ * @since 2.0
+ * @source $URL$
+ * @version $Id$
+ * @author Andres Aimes
+ */
+public class NIOUtilities {
+    /**
+     * {@code true} if a warning has already been logged.
+     */
+    private static boolean warned = false;
+
+    /**
+     * Do not allows instantiation of this class.
+     *
+     * @todo This constructor will become private when {@code NIOBufferUtils}
+     *       will have been removed.
+     */
+    protected NIOUtilities() {
+    }
+
+    /**
+     * Really closes a {@code MappedByteBuffer} without the need to wait for garbage
+     * collection. Any problems with closing a buffer on Windows (the problem child in this
+     * case) will be logged as {@code SEVERE} to the logger of the package name. To
+     * force logging of errors, set the System property "org.geotools.io.debugBuffer" to "true".
+     *
+     * @param  buffer The buffer to close.
+     * @return true if the operation was successful, false otherwise.
+     *
+     * @see java.nio.MappedByteBuffer
+     */
+    public static boolean clean(final ByteBuffer buffer) {
+        if (buffer == null || ! buffer.isDirect() ) {
+            return false;
+        }
+        Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                Boolean success = Boolean.FALSE;
+                try {
+                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", (Class[])null);
+                    getCleanerMethod.setAccessible(true);
+                    Object cleaner = getCleanerMethod.invoke(buffer,  (Object[])null);
+                    Method clean = cleaner.getClass().getMethod("clean", (Class[])null);
+                    clean.invoke(cleaner, (Object[])null);
+                    success = Boolean.TRUE;
+                } catch (Exception e) {
+                    // This really is a show stopper on windows
+                    if (isLoggable()) {
+                        log(e, buffer);
+                    }
+                }
+                return success;
+            }
+        });
+
+        return b.booleanValue();
+    }
+
+    /**
+     * Check if a warning message should be logged.
+     */
+    private static synchronized boolean isLoggable() {
+        try {
+            return !warned && (
+                    System.getProperty("org.geotools.io.debugBuffer", "false").equalsIgnoreCase("true") ||
+                    System.getProperty("os.name").indexOf("Windows") >= 0 );
+        } catch (SecurityException exception) {
+            // The utilities may be running in an Applet, in which case we
+            // can't read properties. Assumes we are not in debugging mode.
+            return false;
+        }
+    }
+
+    /**
+     * Log a warning message.
+     */
+    private static synchronized void log(final Exception e, final ByteBuffer buffer) {
+        warned = true;
+        String message = "Error attempting to close a mapped byte buffer : " + buffer.getClass().getName()
+                       + "\n JVM : " + System.getProperty("java.version")
+                       + ' '         + System.getProperty("java.vendor");
+        Logger.getLogger("org.geotools.io").log(Level.SEVERE, message, e);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java
new file mode 100644
index 0000000..945d11e
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java
@@ -0,0 +1,62 @@
+package com.ximple.io.dgn7;
+
+import org.apache.log4j.Logger;
+
+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
+{
+    private static final Logger logger = Logger.getLogger(ShapeElement.class);
+
+    public ShapeElement(byte[] raw)
+    {
+        super(raw);
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        try
+        {
+            LinearRing ring = factory.createLinearRing(this.getVertices());
+            return factory.createPolygon(ring, null);
+        } catch (IllegalArgumentException e)
+        {
+            logger.warn(e.getMessage(), e);
+            return 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(byte[] raw)
+        {
+            return new ShapeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java
new file mode 100644
index 0000000..db92597
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java
@@ -0,0 +1,45 @@
+package com.ximple.io.dgn7;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+/**
+ * StreamLogging
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 10:31:08
+ */
+public class StreamLogging
+{
+    private static final Logger LOGGER = LogManager.getLogger("com.ximple.io.dgn7");
+    private String              name;
+    private int                 open = 0;
+
+    /**
+     * The name that will appear in the debug message
+     *
+     * @param name
+     */
+    public StreamLogging(String name)
+    {
+        this.name = name;
+    }
+
+    /**
+     * Call when reader or writer is opened
+     */
+    public synchronized void open()
+    {
+        open++;
+        LOGGER.debug(name + " has been opened. Number open: " + open);
+    }
+
+    public synchronized void close()
+    {
+        open--;
+        LOGGER.debug(name + " has been closed. Number open: " + open);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java
new file mode 100644
index 0000000..2a2e165
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TcbElement.java
@@ -0,0 +1,94 @@
+package com.ximple.io.dgn7;
+
+import org.apache.log4j.Logger;
+
+/**
+ * TcbElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 05:03:46
+ */
+public class TcbElement extends Element
+{
+    private static final Logger logger = Logger.getLogger(TcbElement.class);
+
+    public TcbElement(byte[] 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(byte[] raw)
+        {
+            return new TcbElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
new file mode 100644
index 0000000..454897c
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
@@ -0,0 +1,340 @@
+package com.ximple.io.dgn7;
+
+import java.awt.geom.AffineTransform;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * TextElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �W�� 11:45:29
+ */
+public class TextElement extends Element implements GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(TextElement.class);
+
+    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;
+
+    public static final int TXTJUST_LT = 0;    /* Left Top */
+    public static final int TXTJUST_LC = 1;    /* Left Center */
+    public static final int TXTJUST_LB = 2;    /* Left Bottom */
+    public static final int TXTJUST_LMT = 3;   /* Left Margin Top */
+    public static final int TXTJUST_LMC = 4;   /* Left Margin Center */
+    public static final int TXTJUST_LMB = 5;   /* Left Margin Bottom */
+    public static final int TXTJUST_CT = 6;    /* Center Top */
+    public static final int TXTJUST_CC = 7;    /* Center Center */
+    public static final int TXTJUST_CB = 8;    /* Center Bottom */
+    public static final int TXTJUST_RMT = 9;   /* Right Margin Top */
+    public static final int TXTJUST_RMC = 10;  /* Right Margin Center */
+    public static final int TXTJUST_RMB = 11;  /* Right Margin Bottom */
+    public static final int TXTJUST_RT = 12;   /* Right Top */
+    public static final int TXTJUST_RC = 13;   /* Right Center */
+    public static final int TXTJUST_RB = 14;   /* Right Bottom */
+
+    public static final int TXTJUST_LU = 15;   /* Left Cap */
+    public static final int TXTJUST_LD = 16;   /* Left Descender */
+    public static final int TXTJUST_LMU = 17;  /* Left Margin Cap */
+    public static final int TXTJUST_LMD = 18;  /* Left Margin Descender */
+    public static final int TXTJUST_CU = 19;   /* Center Cap */
+    public static final int TXTJUST_CD = 20;   /* Center Descender */
+    public static final int TXTJUST_RMU = 21;  /* Right Margin Cap */
+    public static final int TXTJUST_RMD = 22;  /* Right Margin Descender */
+    public static final int TXTJUST_RU = 23;   /* Right Cap */
+    public static final int TXTJUST_RD = 24;   /* Right Descender */
+    public static final int TXTJUST_NONE = 127;/* no justfication */
+
+    public TextElement(byte[] raw)
+    {
+        super(raw);
+    }
+
+    public Coordinate getOrigin()
+    {
+        int x = (raw[25] << 16 & 0xffff0000);
+
+        x += raw[26] & 0x0000ffff;
+
+        double dx = DgnUtility.converUnitToCoord(x);
+        int y = (raw[27] << 16 & 0xffff0000);
+
+        y += raw[28] & 0x0000ffff;
+
+        double dy = DgnUtility.converUnitToCoord(y);
+
+        return new Coordinate(dx, dy);
+    }
+
+    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 = ((raw[21] << 16) & 0xffff0000);
+
+        height += raw[22] & 0x0000ffff;
+
+        return DgnUtility.converIntToDouble(height);
+    }
+
+    public double getTextWidth()
+    {
+        int length = (raw[19] << 16 & 0xffff0000);
+
+        length += raw[20] & 0x0000ffff;
+
+        return DgnUtility.converIntToDouble(length);
+    }
+
+    public int getJustification()
+    {
+        return ((raw[18] >>> 8) & 0x00000000ff);
+    }
+
+    public double getRotationAngle()
+    {
+        int totation = ((raw[23] & 0x0000ffff) << 16) | (raw[24] & 0x0000ffff);
+        return DgnUtility.converIntToRotation(totation);
+    }
+
+    public boolean isChinese()
+    {
+        int isChinese = raw[30] & 0x0000ffff;
+
+        return (isChinese == 0xfdff);
+    }
+
+    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]);
+            }
+        } else
+        {
+            byte[] strRaw = new byte[num * 2];
+            for (int i = 0; i < num; i++)
+            {
+                short charValue = raw[i + 31];
+                byte hi = (byte) (charValue >>> 8);
+                byte lo = (byte) charValue;
+                strRaw[i * 2] = hi;
+                strRaw[i * 2 + 1] = lo;
+            }
+
+            try
+            {
+                Charset charsetBig5 = Charset.forName("Big5");
+                CharsetDecoder decoder = charsetBig5.newDecoder();
+                CharBuffer cb = decoder.decode(ByteBuffer.wrap(strRaw));
+                val.append(cb);
+            } catch (CharacterCodingException e)
+            {
+                logger.warn(e.getMessage(), e);
+                return val.toString();
+            } finally
+            {
+                // rawBuffer.position(pos);
+                // rawBuffer.order(order);
+            }
+        }
+
+        return val.toString();
+    }
+
+    protected byte[] convertDBCSToUnicode(byte[] buffer)
+    {
+        byte[] charBuffer = new byte[4];
+        charBuffer[0] = (byte) ((byte) ((buffer[1] & 0xc0) >>> 6) | 0xc0);
+        charBuffer[1] = (byte) (buffer[1] & 0x3f | 0x80);
+        charBuffer[2] = (byte) ((byte) ((buffer[0] & 0xc0) >>> 6) | 0xc0);
+        charBuffer[3] = (byte) (buffer[0] & 0x3f | 0x80);
+        return charBuffer;
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        return factory.createPoint(getUserOrigin());
+    }
+
+    private double getUserWidth()
+    {
+        int just = getJustification();
+        // Envelope range = getRange();         // case -1
+        // double width = (range.getWidth());   // case -1
+        // double width = this.getTextWidth() * this.getTextLength() * 1000.0; // case -2
+        double width = (this.getTextWidth() * this.getTextLength());
+
+        switch (just)
+        {
+        case TXTJUST_LT:
+        case TXTJUST_LC:
+        case TXTJUST_LB:
+            width = 0;
+            break;
+
+        case TXTJUST_CT:
+        case TXTJUST_CC:
+        case TXTJUST_CB:
+            width = width / 2;
+            break;
+
+        case TXTJUST_RT:
+        case TXTJUST_RC:
+        case TXTJUST_RB:
+            break;
+        }
+
+        return width;
+    }
+
+    private double getUserHeight()
+    {
+        int just = getJustification();
+        double height = getTextHeight();
+
+        switch (just)
+        {
+        case TXTJUST_LB:
+        case TXTJUST_CB:
+        case TXTJUST_RB:    // bottom
+            height = 0;
+            break;
+
+        case TXTJUST_LC:
+        case TXTJUST_CC:
+        case TXTJUST_RC:    // center
+            height = height / 2;
+            break;
+
+        case TXTJUST_LT:
+        case TXTJUST_CT:
+        case TXTJUST_RT:    // height
+            break;
+        }
+
+        return height;
+    }
+
+    public Coordinate getUserOrigin()
+    {
+        double width = getUserWidth();
+        double height = getUserHeight();
+        double angle = Math.toRadians(getRotationAngle());
+
+        AffineTransform at = new AffineTransform();
+        at.translate(width, height);
+        Coordinate p = getOrigin();
+        at.setToRotation(angle, p.x, p.y);
+        at.scale(1, 1);
+
+        double[] srcPt = new double[2];
+        double[] dstPt = new double[2];
+
+        srcPt[0] = p.x + width;
+        srcPt[1] = p.y + height;
+
+        at.transform(srcPt, 0, dstPt, 0, 1);
+
+        return new Coordinate(dstPt[0], dstPt[1]);
+    }
+
+    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(byte[] raw)
+        {
+            return new TextElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
new file mode 100644
index 0000000..4b70f52
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
@@ -0,0 +1,289 @@
+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 org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.util.DgnUtility;
+
+/**
+ * TextNodeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 04:02:58
+ */
+public class TextNodeElement extends Element implements ComplexElement, GeometryConverter
+{
+    private static final Logger logger = Logger.getLogger(TextElement.class);
+
+    private ArrayList list = new ArrayList();
+
+    public TextNodeElement(byte[] 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 (raw[19] & 0x0000ffff);
+    }
+
+    public int getNodeNumber()
+    {
+        return (raw[20] & 0x0000ffff);
+    }
+
+    public int getMaxLength()
+    {
+        return (raw[21] & 0x00ff);
+    }
+
+    public int getMaxUsed()
+    {
+        return ((raw[21] >> 8) & 0x00ff);
+    }
+
+    public int getJustification()
+    {
+        return ((raw[22] >> 8) & 0x00ff);
+    }
+
+    public int getFontIndex()
+    {
+        return (raw[22] & 0x00ff);
+    }
+
+    public double getLineSpacing()
+    {
+        int lineSpace;
+        lineSpace = ((raw[23] & 0x0000ffff) << 16) | (raw[24] & 0x0000ffff);
+
+        return lineSpace;
+    }
+
+    public double getTextNodeLength()
+    {
+        int lengthMult;
+
+        lengthMult = ((raw[25] << 16) & 0xffff0000);
+        lengthMult += (raw[26] & 0x0000ffff);
+
+        return DgnUtility.converIntToDouble(lengthMult);
+    }
+
+    public double getTextNodeHeight()
+    {
+        int heightMult;
+
+        heightMult = ((raw[27] << 16) & 0xffff0000);
+        heightMult += (raw[28] & 0x0000ffff);
+
+        return DgnUtility.converIntToDouble(heightMult);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (raw[29] << 16 & 0xffff0000);
+        rotation += raw[30];
+
+        return DgnUtility.converIntToRotation(rotation);
+    }
+
+    public Coordinate getOrigin()
+    {
+        int x = ((raw[31] << 16) & 0xffff0000) | (raw[32] & 0x0000ffff);
+        double dx = DgnUtility.converUnitToCoord(x);
+        // return DgnUtility.convertFromDGN(x);
+
+        int y  = ((raw[33] << 16) & 0xffff0000) | (raw[34] & 0x0000ffff);
+        double dy = DgnUtility.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(byte[] raw)
+        {
+            return new TextNodeElement(raw);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java
new file mode 100644
index 0000000..5f0a9fe
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java
@@ -0,0 +1,34 @@
+package com.ximple.io.dgn7;
+
+/**
+ * UserAttributeData
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 02:29:29
+ */
+public class UserAttributeData
+{
+    protected short[] _src;
+
+    public UserAttributeData(short id, int attributeCount)
+    {
+        _src    = new short[attributeCount];
+        _src[0] = id;
+    }
+
+    public UserAttributeData(short[] src)
+    {
+        _src = src;
+    }
+
+    public short getID()
+    {
+        return _src[0];
+    }
+
+    public void setID(short value)
+    {
+        _src[0] = value;
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java
new file mode 100644
index 0000000..012e150
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java
@@ -0,0 +1,259 @@
+package com.ximple.util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * Utility
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 01:33:00
+ */
+public final class DgnUtility
+{
+    private static final Logger logger = Logger.getLogger(DgnUtility.class);
+
+    public static double converIntToDouble(int src)
+    {
+        return (double) ((long) ((src * 6) / 1000.0 + 0.5)) / 1000.0;
+    }
+
+    public static int converDoubleToInt(double src)
+    {
+
+        return (int) (src / 6 * 1000000.0);
+    }
+
+    public static int convertFromDGN(int aValue)
+    {
+        int newVal;
+
+        newVal = (((aValue ^ 0x00008000) << 16) & 0xffff0000);
+        newVal |= (aValue >>> 16) & 0x0000ffff;
+
+        return newVal;
+    }
+
+    public static int converToDGN(int aValue)
+    {
+        int newVal;
+
+        newVal = (aValue << 16 & 0xffff0000);
+        newVal |= (((aValue ^ 0x80000000) >>> 16) & 0x0000ffff);
+
+        return newVal;
+    }
+
+    public static double converIntToRotation(int aValue)
+    {
+
+        return aValue / 360000.0;
+    }
+
+    public static int converRotatioToInt(double aValue)
+    {
+
+        return (int) (aValue * 360000.0);
+    }
+
+    public static double converRotationToRadian(double aValue)
+    {
+
+        return aValue * Math.PI / 180;
+    }
+
+    public static double converUnitToCoord(int aValue)
+    {
+        double newVal;
+
+        newVal = aValue / 1000.0;
+        newVal += 2147483.648;    // 2147483.648 = 2 ^ 31
+
+        return newVal;
+    }
+
+    public static double converUnitToCoord(double aValue)
+    {
+        double newVal;
+
+        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;
+        }
+
+        return new Envelope(converUnitToCoord((int) range.getMinX()), converUnitToCoord((int) range.getMaxX()),
+                                         converUnitToCoord((int) range.getMinY()), converUnitToCoord((int) range.getMaxY()));
+    }
+
+    public static Envelope converCoordToUnit(Envelope range)
+    {
+        if (range == null)
+        {
+            return null;
+        }
+
+        return new Envelope(converCoordToUnit(range.getMinX()), converCoordToUnit(range.getMaxX()),
+                                         converCoordToUnit(range.getMinY()), converCoordToUnit(range.getMaxY()));
+    }
+
+    public static long convertDGNToRAWIEEEDouble(byte[] org)
+    {
+        ByteBuffer buf = ByteBuffer.allocate(8);
+        buf.order(ByteOrder.LITTLE_ENDIAN);
+        buf.mark();
+        buf.put(org[2]);
+        buf.put(org[3]);
+        buf.put(org[0]);
+        buf.put(org[1]);
+        buf.put(org[6]);
+        buf.put(org[7]);
+        buf.put(org[4]);
+        buf.put(org[5]);
+        buf.position(0);
+
+        int[] tmp = new int[2];
+
+        tmp[0]   = buf.getInt();
+        tmp[1]   = buf.getInt();
+
+        int   exponent;
+
+        int sign     = (tmp[0] & 0x80000000);
+        exponent = (tmp[0] >>> 23) & 0x000000ff;
+
+        if (exponent != 0)
+        {
+            exponent = exponent - 129 + 1023;
+        }
+
+        int 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;
+
+        buf.position(0);
+        buf.order(ByteOrder.BIG_ENDIAN);
+        buf.putInt(tmp[0]);
+        buf.putInt(tmp[1]);
+        buf.position(0);
+        byte[] tmpRaw = new byte[8];
+        buf.get(tmpRaw);
+        buf.position(0);
+        buf.order(ByteOrder.LITTLE_ENDIAN);
+        for (int i = tmpRaw.length; i > 0 ; i--)
+        {
+            buf.put(tmpRaw[i-1]);
+        }
+        buf.position(0);
+        long result = buf.getLong();
+        return result;
+    }
+
+    public static double convertDGNToIEEEDouble(byte[] src)
+    {
+        return Double.longBitsToDouble(convertDGNToRAWIEEEDouble(src));
+    }
+
+    public static short[] convertIEEEDoubleToDGN(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));
+        tmp[1] = (int) (newVal);
+
+        // sign = ( int ) ( ( uint ) tmp[ 0 ] & 0x80000000 );
+        sign     = 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;
+
+        return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java
new file mode 100644
index 0000000..de4c574
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java
@@ -0,0 +1,4774 @@
+//
+//(c) 2000 Sun Microsystems, Inc.
+//ALL RIGHTS RESERVED
+//
+//License Grant-
+//
+//
+//Permission to use, copy, modify, and distribute this Software and its
+//documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
+//hereby granted.
+//
+//This Software is provided "AS IS".  All express warranties, including any
+//implied warranty of merchantability, satisfactory quality, fitness for a
+//particular purpose, or non-infringement, are disclaimed, except to the extent
+//that such disclaimers are held to be legally invalid.
+//
+//You acknowledge that Software is not designed, licensed or intended for use in
+//the design, construction, operation or maintenance of any nuclear facility
+//("High Risk Activities").  Sun disclaims any express or implied warranty of
+//fitness for such uses.
+//
+//Please refer to the file http://www.sun.com/policies/trademarks/ for further
+//important trademark information and to
+//http://java.sun.com/nav/business/index.html for further important licensing
+//information for the Java Technology.
+//
+
+package com.ximple.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.text.DecimalFormatSymbols;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Vector;
+
+/**
+ * PrintfFormat allows the formatting of an array of
+ * objects embedded within a string.  Primitive types
+ * must be passed using wrapper types.  The formatting
+ * is controlled by a control string.
+ * <p>
+ * A control string is a Java string that contains a
+ * control specification.  The control specification
+ * starts at the first percent sign (%) in the string,
+ * provided that this percent sign
+ * <ol>
+ * <li>is not escaped protected by a matching % or is
+ * not an escape % character,
+ * <li>is not at the end of the format string, and
+ * <li>precedes a sequence of characters that parses as
+ * a valid control specification.
+ * </ol>
+ * </p><p>
+ * A control specification usually takes the form:
+ * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+ *                { [hlL] }+ [idfgGoxXeEcs]
+ * </pre>
+ * There are variants of this basic form that are
+ * discussed below.</p>
+ * <p>
+ * The format is composed of zero or more directives
+ * defined as follows:
+ * <ul>
+ * <li>ordinary characters, which are simply copied to
+ * the output stream;
+ * <li>escape sequences, which represent non-graphic
+ * characters; and
+ * <li>conversion specifications,  each of which
+ * results in the fetching of zero or more arguments.
+ * </ul></p>
+ * <p>
+ * The results are undefined if there are insufficient
+ * arguments for the format.  Usually an unchecked
+ * exception will be thrown.  If the format is
+ * exhausted while arguments remain, the excess
+ * arguments are evaluated but are otherwise ignored.
+ * In format strings containing the % form of
+ * conversion specifications, each argument in the
+ * argument list is used exactly once.</p>
+ * <p>
+ * Conversions can be applied to the <code>n</code>th
+ * argument after the format in the argument list,
+ * rather than to the next unused argument.  In this
+ * case, the conversion characer % is replaced by the
+ * sequence %<code>n</code>$, where <code>n</code> is
+ * a decimal integer giving the position of the
+ * argument in the argument list.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of conversion specifications, each argument
+ * in the argument list is used exactly once.</p>
+ *
+ * <h4>Escape Sequences</h4>
+ * <p>
+ * The following table lists escape sequences and
+ * associated actions on display devices capable of
+ * the action.
+ * <table>
+ * <tr><th align=left>Sequence</th>
+ *    <th align=left>Name</th>
+ *    <th align=left>Description</th></tr>
+ * <tr><td>\\</td><td>backlash</td><td>None.
+ * </td></tr>
+ * <tr><td>\a</td><td>alert</td><td>Attempts to alert
+ *          the user through audible or visible
+ *          notification.
+ * </td></tr>
+ * <tr><td>\b</td><td>backspace</td><td>Moves the
+ *          printing position to one column before
+ *          the current position, unless the
+ *          current position is the start of a line.
+ * </td></tr>
+ * <tr><td>\f</td><td>form-feed</td><td>Moves the
+ *          printing position to the initial
+ *          printing position of the next logical
+ *          page.
+ * </td></tr>
+ * <tr><td>\n</td><td>newline</td><td>Moves the
+ *          printing position to the start of the
+ *          next line.
+ * </td></tr>
+ * <tr><td>\r</td><td>carriage-return</td><td>Moves
+ *          the printing position to the start of
+ *          the current line.
+ * </td></tr>
+ * <tr><td>\t</td><td>tab</td><td>Moves the printing
+ *          position to the next implementation-
+ *          defined horizontal tab position.
+ * </td></tr>
+ * <tr><td>\v</td><td>vertical-tab</td><td>Moves the
+ *          printing position to the start of the
+ *          next implementation-defined vertical
+ *          tab position.
+ * </td></tr>
+ * </table></p>
+ * <h4>Conversion Specifications</h4>
+ * <p>
+ * Each conversion specification is introduced by
+ * the percent sign character (%).  After the character
+ * %, the following appear in sequence:</p>
+ * <p>
+ * Zero or more flags (in any order), which modify the
+ * meaning of the conversion specification.</p>
+ * <p>
+ * An optional minimum field width.  If the converted
+ * value has fewer characters than the field width, it
+ * will be padded with spaces by default on the left;
+ * t will be padded on the right, if the left-
+ * adjustment flag (-), described below, is given to
+ * the field width.  The field width takes the form
+ * of a decimal integer.  If the conversion character
+ * is s, the field width is the the minimum number of
+ * characters to be printed.</p>
+ * <p>
+ * An optional precision that gives the minumum number
+ * of digits to appear for the d, i, o, x or X
+ * conversions (the field is padded with leading
+ * zeros); the number of digits to appear after the
+ * radix character for the e, E, and f conversions,
+ * the maximum number of significant digits for the g
+ * and G conversions; or the maximum number of
+ * characters to be written from a string is s and S
+ * conversions.  The precision takes the form of an
+ * optional decimal digit string, where a null digit
+ * string is treated as 0.  If a precision appears
+ * with a c conversion character the precision is
+ * ignored.
+ * </p>
+ * <p>
+ * An optional h specifies that a following d, i, o,
+ * x, or X conversion character applies to a type
+ * short argument (the argument will be promoted
+ * according to the integral promotions and its value
+ * converted to type short before printing).</p>
+ * <p>
+ * An optional l (ell) specifies that a following
+ * d, i, o, x, or X conversion character applies to a
+ * type long argument.</p>
+ * <p>
+ * A field width or precision may be indicated by an
+ * asterisk (*) instead of a digit string.  In this
+ * case, an integer argument supplised the field width
+ * precision.  The argument that is actually converted
+ * is not fetched until the conversion letter is seen,
+ * so the the arguments specifying field width or
+ * precision must appear before the argument (if any)
+ * to be converted.  If the precision argument is
+ * negative, it will be changed to zero.  A negative
+ * field width argument is taken as a - flag, followed
+ * by a positive field width.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of a conversion specification, a field width
+ * or precision may be indicated by the sequence
+ * *<code>m</code>$, where m is a decimal integer
+ * giving the position in the argument list (after the
+ * format argument) of an integer argument containing
+ * the field width or precision.</p>
+ * <p>
+ * The format can contain either numbered argument
+ * specifications (that is, %<code>n</code>$ and
+ * *<code>m</code>$), or unnumbered argument
+ * specifications (that is % and *), but normally not
+ * both.  The only exception to this is that %% can
+ * be mixed with the %<code>n</code>$ form.  The
+ * results of mixing numbered and unnumbered argument
+ * specifications in a format string are undefined.</p>
+ *
+ * <h4>Flag Characters</h4>
+ * <p>
+ * The flags and their meanings are:</p>
+ * <dl>
+ * <dt>'<dd> integer portion of the result of a
+ *      decimal conversion (%i, %d, %f, %g, or %G) will
+ *      be formatted with thousands' grouping
+ *      characters.  For other conversions the flag
+ *      is ignored.  The non-monetary grouping
+ *      character is used.
+ * <dt>-<dd> result of the conversion is left-justified
+ *      within the field.  (It will be right-justified
+ *      if this flag is not specified).</td></tr>
+ * <dt>+<dd> result of a signed conversion always
+ *      begins with a sign (+ or -).  (It will begin
+ *      with a sign only when a negative value is
+ *      converted if this flag is not specified.)
+ * <dt>&lt;space&gt;<dd> If the first character of a
+ *      signed conversion is not a sign, a space
+ *      character will be placed before the result.
+ *      This means that if the space character and +
+ *      flags both appear, the space flag will be
+ *      ignored.
+ * <dt>#<dd> value is to be converted to an alternative
+ *      form.  For c, d, i, and s conversions, the flag
+ *      has no effect.  For o conversion, it increases
+ *      the precision to force the first digit of the
+ *      result to be a zero.  For x or X conversion, a
+ *      non-zero result has 0x or 0X prefixed to it,
+ *      respectively.  For e, E, f, g, and G
+ *      conversions, the result always contains a radix
+ *      character, even if no digits follow the radix
+ *      character (normally, a decimal point appears in
+ *      the result of these conversions only if a digit
+ *      follows it).  For g and G conversions, trailing
+ *      zeros will not be removed from the result as
+ *      they normally are.
+ * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G
+ *      conversions, leading zeros (following any
+ *      indication of sign or base) are used to pad to
+ *      the field width;  no space padding is
+ *      performed.  If the 0 and - flags both appear,
+ *      the 0 flag is ignored.  For d, i, o, x, and X
+ *      conversions, if a precision is specified, the
+ *      0 flag will be ignored. For c conversions,
+ *      the flag is ignored.
+ * </dl>
+ *
+ * <h4>Conversion Characters</h4>
+ * <p>
+ * Each conversion character results in fetching zero
+ * or more arguments.  The results are undefined if
+ * there are insufficient arguments for the format.
+ * Usually, an unchecked exception will be thrown.
+ * If the format is exhausted while arguments remain,
+ * the excess arguments are ignored.</p>
+ *
+ * <p>
+ * The conversion characters and their meanings are:
+ * </p>
+ * <dl>
+ * <dt>d,i<dd>The int argument is converted to a
+ *        signed decimal in the style [-]dddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>o<dd> The int argument is converted to unsigned
+ *        octal format in the style ddddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>x<dd> The int argument is converted to unsigned
+ *        hexadecimal format in the style dddd;  the
+ *        letters abcdef are used.  The precision
+ *        specifies the minimum numberof digits to
+ *        appear; if the value being converted can be
+ *        represented in fewer digits, it will be
+ *        expanded with leading zeros.  The default
+ *        precision is 1.  The result of converting 0
+ *        with an explicit precision of 0 is no
+ *        characters.
+ * <dt>X<dd> Behaves the same as the x conversion
+ *        character except that letters ABCDEF are
+ *        used instead of abcdef.
+ * <dt>f<dd> The floating point number argument is
+ *        written in decimal notation in the style
+ *        [-]ddd.ddd, where the number of digits after
+ *        the radix character (shown here as a decimal
+ *        point) is equal to the precision
+ *        specification.  A Locale is used to determine
+ *        the radix character to use in this format.
+ *        If the precision is omitted from the
+ *        argument, six digits are written after the
+ *        radix character;  if the precision is
+ *        explicitly 0 and the # flag is not specified,
+ *        no radix character appears.  If a radix
+ *        character appears, at least 1 digit appears
+ *        before it.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>e,E<dd>The floating point number argument is
+ *        written in the style [-]d.ddde{+-}dd
+ *        (the symbols {+-} indicate either a plus or
+ *        minus sign), where there is one digit before
+ *        the radix character (shown here as a decimal
+ *        point) and the number of digits after it is
+ *        equal to the precision.  A Locale is used to
+ *        determine the radix character to use in this
+ *        format.  When the precision is missing, six
+ *        digits are written after the radix character;
+ *        if the precision is 0 and the # flag is not
+ *        specified, no radix character appears.  The
+ *        E conversion will produce a number with E
+ *        instead of e introducing the exponent.  The
+ *        exponent always contains at least two digits.
+ *        However, if the value to be written requires
+ *        an exponent greater than two digits,
+ *        additional exponent digits are written as
+ *        necessary.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>g,G<dd>The floating point number argument is
+ *        written in style f or e (or in sytle E in the
+ *        case of a G conversion character), with the
+ *        precision specifying the number of
+ *        significant digits.  If the precision is
+ *        zero, it is taken as one.  The style used
+ *        depends on the value converted:  style e
+ *        (or E) will be used only if the exponent
+ *        resulting from the conversion is less than
+ *        -4 or greater than or equal to the precision.
+ *        Trailing zeros are removed from the result.
+ *        A radix character appears only if it is
+ *        followed by a digit.
+ * <dt>c,C<dd>The integer argument is converted to a
+ *        char and the result is written.
+ *
+ * <dt>s,S<dd>The argument is taken to be a string and
+ *        bytes from the string are written until the
+ *        end of the string or the number of bytes
+ *        indicated by the precision specification of
+ *        the argument is reached.  If the precision
+ *        is omitted from the argument, it is taken to
+ *        be infinite, so all characters up to the end
+ *        of the string are written.
+ * <dt>%<dd>Write a % character;  no argument is
+ *        converted.
+ * </dl>
+ * <p>
+ * If a conversion specification does not match one of
+ * the above forms, an IllegalArgumentException is
+ * thrown and the instance of PrintfFormat is not
+ * created.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for infinity, the output is
+ * [+]Infinity, where Infinity is either Infinity or
+ * Inf, depending on the desired output string length.
+ * Printing of the sign follows the rules described
+ * above.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for "not-a-number," the output is
+ * [+]NaN.  Printing of the sign follows the rules
+ * described above.</p>
+ * <p>
+ * In no case does a non-existent or small field width
+ * cause truncation of a field;  if the result of a
+ * conversion is wider than the field width, the field
+ * is simply expanded to contain the conversion result.
+ * </p>
+ * <p>
+ * The behavior is like printf.  One exception is that
+ * the minimum number of exponent digits is 3 instead
+ * of 2 for e and E formats when the optional L is used
+ * before the e, E, g, or G conversion character.  The
+ * optional L does not imply conversion to a long long
+ * double. </p>
+ * <p>
+ * The biggest divergence from the C printf
+ * specification is in the use of 16 bit characters.
+ * This allows the handling of characters beyond the
+ * small ASCII character set and allows the utility to
+ * interoperate correctly with the rest of the Java
+ * runtime environment.</p>
+ * <p>
+ * Omissions from the C printf specification are
+ * numerous.  All the known omissions are present
+ * because Java never uses bytes to represent
+ * characters and does not have pointers:</p>
+ * <ul>
+ * <li>%c is the same as %C.
+ * <li>%s is the same as %S.
+ * <li>u, p, and n conversion characters.
+ * <li>%ws format.
+ * <li>h modifier applied to an n conversion character.
+ * <li>l (ell) modifier applied to the c, n, or s
+ * conversion characters.
+ * <li>ll (ell ell) modifier to d, i, o, u, x, or X
+ * conversion characters.
+ * <li>ll (ell ell) modifier to an n conversion
+ * character.
+ * <li>c, C, d,i,o,u,x, and X conversion characters
+ * apply to Byte, Character, Short, Integer, Long
+ * types.
+ * <li>f, e, E, g, and G conversion characters apply
+ * to Float and Double types.
+ * <li>s and S conversion characters apply to String
+ * types.
+ * <li>All other reference types can be formatted
+ * using the s or S conversion characters only.
+ * </ul>
+ * <p>
+ * Most of this specification is quoted from the Unix
+ * man page for the sprintf utility.</p>
+ *
+ * @author Allan Jacobs
+ * @version 1
+ * Release 1: Initial release.
+ * Release 2: Asterisk field widths and precisions
+ *            %n$ and *m$
+ *            Bug fixes
+ *              g format fix (2 digits in e form corrupt)
+ *              rounding in f format implemented
+ *              round up when digit not printed is 5
+ *              formatting of -0.0f
+ *              round up/down when last digits are 50000...
+ */
+public final class PrintfFormat
+{
+    /** Vector of control strings and format literals. */
+    private Vector vFmt = new Vector();
+
+    /** Character position.  Used by the constructor. */
+    private int cPos = 0;
+
+    /** Character position.  Used by the constructor. */
+    private DecimalFormatSymbols dfs = null;
+
+    /**
+     * Constructs an array of control specifications
+     * possibly preceded, separated, or followed by
+     * ordinary strings.  Control strings begin with
+     * unpaired percent signs.  A pair of successive
+     * percent signs designates a single percent sign in
+     * the format.
+     * @param fmtArg  Control string.
+     * @exception IllegalArgumentException if the control
+     * string is null, zero length, or otherwise
+     * malformed.
+     */
+    public PrintfFormat(String fmtArg) throws IllegalArgumentException
+    {
+        this(Locale.getDefault(), fmtArg);
+    }
+
+    /**
+     * Constructs an array of control specifications
+     * possibly preceded, separated, or followed by
+     * ordinary strings.  Control strings begin with
+     * unpaired percent signs.  A pair of successive
+     * percent signs designates a single percent sign in
+     * the format.
+     * @param fmtArg  Control string.
+     * @exception IllegalArgumentException if the control
+     * string is null, zero length, or otherwise
+     * malformed.
+     */
+    public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException
+    {
+        dfs = new DecimalFormatSymbols(locale);
+
+        int                     ePos = 0;
+        ConversionSpecification sFmt = null;
+        String                  unCS = this.nonControl(fmtArg, 0);
+
+        if (unCS != null)
+        {
+            sFmt = new ConversionSpecification();
+            sFmt.setLiteral(unCS);
+            vFmt.addElement(sFmt);
+        }
+
+        while ((cPos != -1) && (cPos < fmtArg.length()))
+        {
+            for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++)
+            {
+                char c = 0;
+
+                c = fmtArg.charAt(ePos);
+
+                if (c == 'i')
+                {
+                    break;
+                }
+
+                if (c == 'd')
+                {
+                    break;
+                }
+
+                if (c == 'f')
+                {
+                    break;
+                }
+
+                if (c == 'g')
+                {
+                    break;
+                }
+
+                if (c == 'G')
+                {
+                    break;
+                }
+
+                if (c == 'o')
+                {
+                    break;
+                }
+
+                if (c == 'x')
+                {
+                    break;
+                }
+
+                if (c == 'X')
+                {
+                    break;
+                }
+
+                if (c == 'e')
+                {
+                    break;
+                }
+
+                if (c == 'E')
+                {
+                    break;
+                }
+
+                if (c == 'c')
+                {
+                    break;
+                }
+
+                if (c == 's')
+                {
+                    break;
+                }
+
+                if (c == '%')
+                {
+                    break;
+                }
+            }
+
+            ePos = Math.min(ePos + 1, fmtArg.length());
+            sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos));
+            vFmt.addElement(sFmt);
+            unCS = this.nonControl(fmtArg, ePos);
+
+            if (unCS != null)
+            {
+                sFmt = new ConversionSpecification();
+                sFmt.setLiteral(unCS);
+                vFmt.addElement(sFmt);
+            }
+        }
+    }
+
+    /**
+     * Return a substring starting at
+     * <code>start</code> and ending at either the end
+     * of the String <code>s</code>, the next unpaired
+     * percent sign, or at the end of the String if the
+     * last character is a percent sign.
+     * @param s  Control string.
+     * @param start Position in the string
+     *     <code>s</code> to begin looking for the start
+     *     of a control string.
+     * @return the substring from the start position
+     *     to the beginning of the control string.
+     */
+    private String nonControl(String s, int start)
+    {
+        String ret = "";
+
+        cPos = s.indexOf("%", start);
+
+        if (cPos == -1)
+        {
+            cPos = s.length();
+        }
+
+        return s.substring(start, cPos);
+    }
+
+    /**
+     * Format an array of objects.  Byte, Short,
+     * Integer, Long, Float, Double, and Character
+     * arguments are treated as wrappers for primitive
+     * types.
+     * @param o The array of objects to format.
+     * @return  The formatted String.
+     */
+    public String sprintf(Object[] o)
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        int                     i  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                if (cs.isPositionalSpecification())
+                {
+                    i = cs.getArgumentPosition() - 1;
+
+                    if (cs.isPositionalFieldWidth())
+                    {
+                        int ifw = cs.getArgumentPositionForFieldWidth() - 1;
+
+                        cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue());
+                    }
+
+                    if (cs.isPositionalPrecision())
+                    {
+                        int ipr = cs.getArgumentPositionForPrecision() - 1;
+
+                        cs.setPrecisionWithArg(((Integer) o[ipr]).intValue());
+                    }
+                } else
+                {
+                    if (cs.isVariableFieldWidth())
+                    {
+                        cs.setFieldWidthWithArg(((Integer) o[i]).intValue());
+                        i++;
+                    }
+
+                    if (cs.isVariablePrecision())
+                    {
+                        cs.setPrecisionWithArg(((Integer) o[i]).intValue());
+                        i++;
+                    }
+                }
+
+                if (o[i] instanceof Byte)
+                {
+                    sb.append(cs.internalsprintf(((Byte) o[i]).byteValue()));
+                } else if (o[i] instanceof Short)
+                {
+                    sb.append(cs.internalsprintf(((Short) o[i]).shortValue()));
+                } else if (o[i] instanceof Integer)
+                {
+                    sb.append(cs.internalsprintf(((Integer) o[i]).intValue()));
+                } else if (o[i] instanceof Long)
+                {
+                    sb.append(cs.internalsprintf(((Long) o[i]).longValue()));
+                } else if (o[i] instanceof Float)
+                {
+                    sb.append(cs.internalsprintf(((Float) o[i]).floatValue()));
+                } else if (o[i] instanceof Double)
+                {
+                    sb.append(cs.internalsprintf(((Double) o[i]).doubleValue()));
+                } else if (o[i] instanceof Character)
+                {
+                    sb.append(cs.internalsprintf(((Character) o[i]).charValue()));
+                } else if (o[i] instanceof String)
+                {
+                    sb.append(cs.internalsprintf((String) o[i]));
+                } else
+                {
+                    sb.append(cs.internalsprintf(o[i]));
+                }
+
+                if (!cs.isPositionalSpecification())
+                {
+                    i++;
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format nothing.  Just use the control string.
+     * @return  the formatted String.
+     */
+    public String sprintf()
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an int.
+     * @param x The int to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, G, s,
+     *     or S.
+     */
+    public String sprintf(int x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an long.
+     * @param x The long to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, G, s,
+     *     or S.
+     */
+    public String sprintf(long x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format a double.
+     * @param x The double to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is c, C, s, S,
+     *     d, d, x, X, or o.
+     */
+    public String sprintf(double x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format a String.
+     * @param x The String to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *   conversion character is neither s nor S.
+     */
+    public String sprintf(String x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an Object.  Convert wrapper types to
+     * their primitive equivalents and call the
+     * appropriate internal formatting method. Convert
+     * Strings using an internal formatting method for
+     * Strings. Otherwise use the default formatter
+     * (use toString).
+     * @param x the Object to format.
+     * @return  the formatted String.
+     * @exception IllegalArgumentException if the
+     *    conversion character is inappropriate for
+     *    formatting an unwrapped value.
+     */
+    public String sprintf(Object x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                if (x instanceof Byte)
+                {
+                    sb.append(cs.internalsprintf(((Byte) x).byteValue()));
+                } else if (x instanceof Short)
+                {
+                    sb.append(cs.internalsprintf(((Short) x).shortValue()));
+                } else if (x instanceof Integer)
+                {
+                    sb.append(cs.internalsprintf(((Integer) x).intValue()));
+                } else if (x instanceof Long)
+                {
+                    sb.append(cs.internalsprintf(((Long) x).longValue()));
+                } else if (x instanceof Float)
+                {
+                    sb.append(cs.internalsprintf(((Float) x).floatValue()));
+                } else if (x instanceof Double)
+                {
+                    sb.append(cs.internalsprintf(((Double) x).doubleValue()));
+                } else if (x instanceof Character)
+                {
+                    sb.append(cs.internalsprintf(((Character) x).charValue()));
+                } else if (x instanceof String)
+                {
+                    sb.append(cs.internalsprintf((String) x));
+                } else
+                {
+                    sb.append(cs.internalsprintf(x));
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * <p>
+     * ConversionSpecification allows the formatting of
+     * a single primitive or object embedded within a
+     * string.  The formatting is controlled by a
+     * format string.  Only one Java primitive or
+     * object can be formatted at a time.
+     * <p>
+     * A format string is a Java string that contains
+     * a control string.  The control string starts at
+     * the first percent sign (%) in the string,
+     * provided that this percent sign
+     * <ol>
+     * <li>is not escaped protected by a matching % or
+     *     is not an escape % character,
+     * <li>is not at the end of the format string, and
+     * <li>precedes a sequence of characters that parses
+     *     as a valid control string.
+     * </ol>
+     * <p>
+     * A control string takes the form:
+     * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+     *                { [hlL] }+ [idfgGoxXeEcs]
+     * </pre>
+     * <p>
+     * The behavior is like printf.  One (hopefully the
+     * only) exception is that the minimum number of
+     * exponent digits is 3 instead of 2 for e and E
+     * formats when the optional L is used before the
+     * e, E, g, or G conversion character.  The
+     * optional L does not imply conversion to a long
+     * long double.
+     */
+    private class ConversionSpecification
+    {
+        /** Default precision. */
+        private final static int defaultDigits = 6;
+
+        /**
+         * The integer portion of the result of a decimal
+         * conversion (i, d, u, f, g, or G) will be
+         * formatted with thousands' grouping characters.
+         * For other conversions the flag is ignored.
+         */
+        private boolean thousands = false;
+
+        /**
+         * The result of the conversion will be
+         * left-justified within the field.
+         */
+        private boolean leftJustify = false;
+
+        /**
+         * The result of a signed conversion will always
+         * begin with a sign (+ or -).
+         */
+        private boolean leadingSign = false;
+
+        /**
+         * Flag indicating that left padding with spaces is
+         * specified.
+         */
+        private boolean leadingSpace = false;
+
+        /**
+         * For an o conversion, increase the precision to
+         * force the first digit of the result to be a
+         * zero.  For x (or X) conversions, a non-zero
+         * result will have 0x (or 0X) prepended to it.
+         * For e, E, f, g, or G conversions, the result
+         * will always contain a radix character, even if
+         * no digits follow the point.  For g and G
+         * conversions, trailing zeros will not be removed
+         * from the result.
+         */
+        private boolean alternateForm = false;
+
+        /**
+         * Flag indicating that left padding with zeroes is
+         * specified.
+         */
+        private boolean leadingZeros = false;
+
+        /**
+         * Flag indicating that the field width is *.
+         */
+        private boolean variableFieldWidth = false;
+
+        /**
+         * If the converted value has fewer bytes than the
+         * field width, it will be padded with spaces or
+         * zeroes.
+         */
+        private int fieldWidth = 0;
+
+        /**
+         * Flag indicating whether or not the field width
+         * has been set.
+         */
+        private boolean fieldWidthSet = false;
+
+        /**
+         * The minimum number of digits to appear for the
+         * d, i, o, u, x, or X conversions.  The number of
+         * digits to appear after the radix character for
+         * the e, E, and f conversions.  The maximum number
+         *  of significant digits for the g and G
+         * conversions.  The maximum number of bytes to be
+         * printed from a string in s and S conversions.
+         */
+        private int precision = 0;
+
+        /**
+         * Flag indicating that the precision is *.
+         */
+        private boolean variablePrecision = false;
+
+        /**
+         * Flag indicating whether or not the precision has
+         * been set.
+         */
+        private boolean precisionSet = false;
+
+        /*
+         */
+        private boolean positionalSpecification       = false;
+        private int     argumentPosition              = 0;
+        private boolean positionalFieldWidth          = false;
+        private int     argumentPositionForFieldWidth = 0;
+        private boolean positionalPrecision           = false;
+        private int     argumentPositionForPrecision  = 0;
+
+        /**
+         * Flag specifying that a following d, i, o, u, x,
+         * or X conversion character applies to a type
+         * short int.
+         */
+        private boolean optionalh = false;
+
+        /**
+         * Flag specifying that a following d, i, o, u, x,
+         * or X conversion character applies to a type lont
+         * int argument.
+         */
+        private boolean optionall = false;
+
+        /**
+         * Flag specifying that a following e, E, f, g, or
+         * G conversion character applies to a type double
+         * argument.  This is a noop in Java.
+         */
+        private boolean optionalL = false;
+
+        /** Control string type. */
+        private char conversionCharacter = '\0';
+
+        /**
+         * Position within the control string.  Used by
+         * the constructor.
+         */
+        private int pos = 0;
+
+        /** Literal or control format string. */
+        private String fmt;
+
+        /**
+         * Constructor.  Used to prepare an instance
+         * to hold a literal, not a control string.
+         */
+        ConversionSpecification()
+        {
+        }
+
+        /**
+         * Constructor for a conversion specification.
+         * The argument must begin with a % and end
+         * with the conversion character for the
+         * conversion specification.
+         *  @param fmtArg  String specifying the
+         *     conversion specification.
+         *  @exception IllegalArgumentException if the
+         *     input string is null, zero length, or
+         *     otherwise malformed.
+         */
+        ConversionSpecification(String fmtArg) throws IllegalArgumentException
+        {
+            if (fmtArg == null)
+            {
+                throw new NullPointerException();
+            }
+
+            if (fmtArg.length() == 0)
+            {
+                throw new IllegalArgumentException("Control strings must have positive" + " lengths.");
+            }
+
+            if (fmtArg.charAt(0) == '%')
+            {
+                fmt = fmtArg;
+                pos = 1;
+                setArgPosition();
+                setFlagCharacters();
+                setFieldWidth();
+                setPrecision();
+                setOptionalHL();
+
+                if (setConversionCharacter())
+                {
+                    if (pos == fmtArg.length())
+                    {
+                        if (leadingZeros && leftJustify)
+                        {
+                            leadingZeros = false;
+                        }
+
+                        if (precisionSet && leadingZeros)
+                        {
+                            if ((conversionCharacter == 'd') || (conversionCharacter == 'i') || (conversionCharacter == 'o')
+                                || (conversionCharacter == 'x'))
+                            {
+                                leadingZeros = false;
+                            }
+                        }
+                    } else
+                    {
+                        throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+                    }
+                } else
+                {
+                    throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+                }
+            } else
+            {
+                throw new IllegalArgumentException("Control strings must begin with %.");
+            }
+        }
+
+        /**
+         * Set the String for this instance.
+         * @param s the String to store.
+         */
+        void setLiteral(String s)
+        {
+            fmt = s;
+        }
+
+        /**
+         * Get the String for this instance.  Translate
+         * any escape sequences.
+         *
+         * @return s the stored String.
+         */
+        String getLiteral()
+        {
+            StringBuffer sb = new StringBuffer();
+            int          i  = 0;
+
+            while (i < fmt.length())
+            {
+                if (fmt.charAt(i) == '\\')
+                {
+                    i++;
+
+                    if (i < fmt.length())
+                    {
+                        char c = fmt.charAt(i);
+
+                        switch (c)
+                        {
+                        case 'a' :
+                            sb.append((char) 0x07);
+
+                            break;
+
+                        case 'b' :
+                            sb.append('\b');
+
+                            break;
+
+                        case 'f' :
+                            sb.append('\f');
+
+                            break;
+
+                        case 'n' :
+                            sb.append(System.getProperty("line.separator"));
+
+                            break;
+
+                        case 'r' :
+                            sb.append('\r');
+
+                            break;
+
+                        case 't' :
+                            sb.append('\t');
+
+                            break;
+
+                        case 'v' :
+                            sb.append((char) 0x0b);
+
+                            break;
+
+                        case '\\' :
+                            sb.append('\\');
+
+                            break;
+                        }
+
+                        i++;
+                    } else
+                    {
+                        sb.append('\\');
+                    }
+                } else
+                {
+                    i++;
+                }
+            }
+
+            return fmt;
+        }
+
+        /**
+         * Get the conversion character that tells what
+         * type of control character this instance has.
+         *
+         * @return the conversion character.
+         */
+        char getConversionCharacter()
+        {
+            return conversionCharacter;
+        }
+
+        /**
+         * Check whether the specifier has a variable
+         * field width that is going to be set by an
+         * argument.
+         * @return <code>true</code> if the conversion
+         *   uses an * field width; otherwise
+         *   <code>false</code>.
+         */
+        boolean isVariableFieldWidth()
+        {
+            return variableFieldWidth;
+        }
+
+        /**
+         * Set the field width with an argument.  A
+         * negative field width is taken as a - flag
+         * followed by a positive field width.
+         * @param fw the field width.
+         */
+        void setFieldWidthWithArg(int fw)
+        {
+            if (fw < 0)
+            {
+                leftJustify = true;
+            }
+
+            fieldWidthSet = true;
+            fieldWidth    = Math.abs(fw);
+        }
+
+        /**
+         * Check whether the specifier has a variable
+         * precision that is going to be set by an
+         * argument.
+         * @return <code>true</code> if the conversion
+         *   uses an * precision; otherwise
+         *   <code>false</code>.
+         */
+        boolean isVariablePrecision()
+        {
+            return variablePrecision;
+        }
+
+        /**
+         * Set the precision with an argument.  A
+         * negative precision will be changed to zero.
+         * @param pr the precision.
+         */
+        void setPrecisionWithArg(int pr)
+        {
+            precisionSet = true;
+            precision    = Math.max(pr, 0);
+        }
+
+        /**
+         * Format an int argument using this conversion
+         *  specification.
+         * @param s the int to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is f, e, E, g, or G.
+         */
+        String internalsprintf(int s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'd' :
+            case 'i' :
+                if (optionalh)
+                {
+                    s2 = printDFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printDFormat((long) s);
+                } else
+                {
+                    s2 = printDFormat(s);
+                }
+
+                break;
+
+            case 'x' :
+            case 'X' :
+                if (optionalh)
+                {
+                    s2 = printXFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printXFormat((long) s);
+                } else
+                {
+                    s2 = printXFormat(s);
+                }
+
+                break;
+
+            case 'o' :
+                if (optionalh)
+                {
+                    s2 = printOFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printOFormat((long) s);
+                } else
+                {
+                    s2 = printOFormat(s);
+                }
+
+                break;
+
+            case 'c' :
+            case 'C' :
+                s2 = printCFormat((char) s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot format a int with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a long argument using this conversion
+         * specification.
+         * @param s the long to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is f, e, E, g, or G.
+         */
+        String internalsprintf(long s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'd' :
+            case 'i' :
+                if (optionalh)
+                {
+                    s2 = printDFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printDFormat(s);
+                } else
+                {
+                    s2 = printDFormat((int) s);
+                }
+
+                break;
+
+            case 'x' :
+            case 'X' :
+                if (optionalh)
+                {
+                    s2 = printXFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printXFormat(s);
+                } else
+                {
+                    s2 = printXFormat((int) s);
+                }
+
+                break;
+
+            case 'o' :
+                if (optionalh)
+                {
+                    s2 = printOFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printOFormat(s);
+                } else
+                {
+                    s2 = printOFormat((int) s);
+                }
+
+                break;
+
+            case 'c' :
+            case 'C' :
+                s2 = printCFormat((char) s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot format a long with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a double argument using this conversion
+         * specification.
+         * @param s the double to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is c, C, s, S, i, d,
+         *     x, X, or o.
+         */
+        String internalsprintf(double s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'f' :
+                s2 = printFFormat(s);
+
+                break;
+
+            case 'E' :
+            case 'e' :
+                s2 = printEFormat(s);
+
+                break;
+
+            case 'G' :
+            case 'g' :
+                s2 = printGFormat(s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot " + "format a double with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a String argument using this conversion
+         * specification.
+         * @param s the String to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *   conversion character is neither s nor S.
+         */
+        String internalsprintf(String s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            if ((conversionCharacter == 's') || (conversionCharacter == 'S'))
+            {
+                s2 = printSFormat(s);
+            } else
+            {
+                throw new IllegalArgumentException("Cannot " + "format a String with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format an Object argument using this conversion
+         * specification.
+         * @param s the Object to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is neither s nor S.
+         */
+        String internalsprintf(Object s)
+        {
+            String s2 = "";
+
+            if ((conversionCharacter == 's') || (conversionCharacter == 'S'))
+            {
+                s2 = printSFormat(s.toString());
+            } else
+            {
+                throw new IllegalArgumentException("Cannot format a String with a format using" + " a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * For f format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both
+         * a '+' and a ' ' are specified, the blank flag
+         * is ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the number of digits
+         * to appear after the radix character.  Padding is
+         * with trailing 0s.
+         */
+        private char[] fFormatDigits(double x)
+        {
+            // int defaultDigits=6;
+            String  sx, sxOut;
+            int     i, j, k;
+            int     n1In, n2In;
+            int     expon     = 0;
+            boolean minusSign = false;
+
+            if (x > 0.0)
+            {
+                sx = Double.toString(x);
+            } else if (x < 0.0)
+            {
+                sx        = Double.toString(-x);
+                minusSign = true;
+            } else
+            {
+                sx = Double.toString(x);
+
+                if (sx.charAt(0) == '-')
+                {
+                    minusSign = true;
+                    sx        = sx.substring(1);
+                }
+            }
+
+            int ePos = sx.indexOf('E');
+            int rPos = sx.indexOf('.');
+
+            if (rPos != -1)
+            {
+                n1In = rPos;
+            } else if (ePos != -1)
+            {
+                n1In = ePos;
+            } else
+            {
+                n1In = sx.length();
+            }
+
+            if (rPos != -1)
+            {
+                if (ePos != -1)
+                {
+                    n2In = ePos - rPos - 1;
+                } else
+                {
+                    n2In = sx.length() - rPos - 1;
+                }
+            } else
+            {
+                n2In = 0;
+            }
+
+            if (ePos != -1)
+            {
+                int ie = ePos + 1;
+
+                expon = 0;
+
+                if (sx.charAt(ie) == '-')
+                {
+                    for (++ie; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(ie));
+                    }
+                } else
+                {
+                    if (sx.charAt(ie) == '+')
+                    {
+                        ++ie;
+                    }
+
+                    for (; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(ie));
+                    }
+                }
+            }
+
+            int p;
+
+            if (precisionSet)
+            {
+                p = precision;
+            } else
+            {
+                p = defaultDigits - 1;
+            }
+
+            char[] ca1 = sx.toCharArray();
+            char[] ca2 = new char[n1In + n2In];
+            char[] ca3, ca4, ca5;
+
+            for (j = 0; j < n1In; j++)
+            {
+                ca2[j] = ca1[j];
+            }
+
+            i = j + 1;
+
+            for (k = 0; k < n2In; j++, i++, k++)
+            {
+                ca2[j] = ca1[i];
+            }
+
+            if (n1In + expon <= 0)
+            {
+                ca3 = new char[-expon + n2In];
+
+                for (j = 0, k = 0; k < (-n1In - expon); k++, j++)
+                {
+                    ca3[j] = '0';
+                }
+
+                for (i = 0; i < (n1In + n2In); i++, j++)
+                {
+                    ca3[j] = ca2[i];
+                }
+            } else
+            {
+                ca3 = ca2;
+            }
+
+            boolean carry = false;
+
+            if (p < -expon + n2In)
+            {
+                if (expon < 0)
+                {
+                    i = p;
+                } else
+                {
+                    i = p + n1In;
+                }
+
+                carry = checkForCarry(ca3, i);
+
+                if (carry)
+                {
+                    carry = startSymbolicCarry(ca3, i - 1, 0);
+                }
+            }
+
+            if (n1In + expon <= 0)
+            {
+                ca4 = new char[2 + p];
+
+                if (!carry)
+                {
+                    ca4[0] = '0';
+                } else
+                {
+                    ca4[0] = '1';
+                }
+
+                if (alternateForm ||!precisionSet || (precision != 0))
+                {
+                    ca4[1] = '.';
+
+                    for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++)
+                    {
+                        ca4[j] = ca3[i];
+                    }
+
+                    for (; j < ca4.length; j++)
+                    {
+                        ca4[j] = '0';
+                    }
+                }
+            } else
+            {
+                if (!carry)
+                {
+                    if (alternateForm ||!precisionSet || (precision != 0))
+                    {
+                        ca4 = new char[n1In + expon + p + 1];
+                    } else
+                    {
+                        ca4 = new char[n1In + expon];
+                    }
+
+                    j = 0;
+                } else
+                {
+                    if (alternateForm ||!precisionSet || (precision != 0))
+                    {
+                        ca4 = new char[n1In + expon + p + 2];
+                    } else
+                    {
+                        ca4 = new char[n1In + expon + 1];
+                    }
+
+                    ca4[0] = '1';
+                    j      = 1;
+                }
+
+                for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++)
+                {
+                    ca4[j] = ca3[i];
+                }
+
+                for (; i < n1In + expon; i++, j++)
+                {
+                    ca4[j] = '0';
+                }
+
+                if (alternateForm ||!precisionSet || (precision != 0))
+                {
+                    ca4[j] = '.';
+                    j++;
+
+                    for (k = 0; (i < ca3.length) && (k < p); i++, j++, k++)
+                    {
+                        ca4[j] = ca3[i];
+                    }
+
+                    for (; j < ca4.length; j++)
+                    {
+                        ca4[j] = '0';
+                    }
+                }
+            }
+
+            int nZeros = 0;
+
+            if (!leftJustify && leadingZeros)
+            {
+                int xThousands = 0;
+
+                if (thousands)
+                {
+                    int xlead = 0;
+
+                    if ((ca4[0] == '+') || (ca4[0] == '-') || (ca4[0] == ' '))
+                    {
+                        xlead = 1;
+                    }
+
+                    int xdp = xlead;
+
+                    for (; xdp < ca4.length; xdp++)
+                    {
+                        if (ca4[xdp] == '.')
+                        {
+                            break;
+                        }
+                    }
+
+                    xThousands = (xdp - xlead) / 3;
+                }
+
+                if (fieldWidthSet)
+                {
+                    nZeros = fieldWidth - ca4.length;
+                }
+
+                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+                {
+                    nZeros--;
+                }
+
+                nZeros -= xThousands;
+
+                if (nZeros < 0)
+                {
+                    nZeros = 0;
+                }
+            }
+
+            j = 0;
+
+            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+            {
+                ca5 = new char[ca4.length + nZeros + 1];
+                j++;
+            } else
+            {
+                ca5 = new char[ca4.length + nZeros];
+            }
+
+            if (!minusSign)
+            {
+                if (leadingSign)
+                {
+                    ca5[0] = '+';
+                }
+
+                if (leadingSpace)
+                {
+                    ca5[0] = ' ';
+                }
+            } else
+            {
+                ca5[0] = '-';
+            }
+
+            for (i = 0; i < nZeros; i++, j++)
+            {
+                ca5[j] = '0';
+            }
+
+            for (i = 0; i < ca4.length; i++, j++)
+            {
+                ca5[j] = ca4[i];
+            }
+
+            int lead = 0;
+
+            if ((ca5[0] == '+') || (ca5[0] == '-') || (ca5[0] == ' '))
+            {
+                lead = 1;
+            }
+
+            int dp = lead;
+
+            for (; dp < ca5.length; dp++)
+            {
+                if (ca5[dp] == '.')
+                {
+                    break;
+                }
+            }
+
+            int nThousands = (dp - lead) / 3;
+
+            // Localize the decimal point.
+            if (dp < ca5.length)
+            {
+                ca5[dp] = dfs.getDecimalSeparator();
+            }
+
+            char[] ca6 = ca5;
+
+            if (thousands && (nThousands > 0))
+            {
+                ca6    = new char[ca5.length + nThousands + lead];
+                ca6[0] = ca5[0];
+
+                for (i = lead, k = lead; i < dp; i++)
+                {
+                    if ((i > 0) && (dp - i) % 3 == 0)
+                    {
+                        // ca6[k]=',';
+                        ca6[k]     = dfs.getGroupingSeparator();
+                        ca6[k + 1] = ca5[i];
+                        k          += 2;
+                    } else
+                    {
+                        ca6[k] = ca5[i];
+                        k++;
+                    }
+                }
+
+                for (; i < ca5.length; i++, k++)
+                {
+                    ca6[k] = ca5[i];
+                }
+            }
+
+            return ca6;
+        }
+
+        /**
+         * An intermediate routine on the way to creating
+         * an f format String.  The method decides whether
+         * the input double value is an infinity,
+         * not-a-number, or a finite double and formats
+         * each type of input appropriately.
+         * @param x the double value to be formatted.
+         * @return the converted double value.
+         */
+        private String fFormatString(double x)
+        {
+            boolean noDigits = false;
+            char[]  ca6, ca7;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca6 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca6 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca6 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca6 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca6 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca6 = " NaN".toCharArray();
+                } else
+                {
+                    ca6 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                ca6 = fFormatDigits(x);
+            }
+
+            ca7 = applyFloatPadding(ca6, false);
+
+            return new String(ca7);
+        }
+
+        /**
+         * For e format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         *
+         * The behavior is like printf.  One (hopefully the
+         * only) exception is that the minimum number of
+         * exponent digits is 3 instead of 2 for e and E
+         * formats when the optional L is used before the
+         * e, E, g, or G conversion character. The optional
+         * L does not imply conversion to a long long
+         * double.
+         */
+        private char[] eFormatDigits(double x, char eChar)
+        {
+            char[] ca1, ca2, ca3;
+
+            // int defaultDigits=6;
+            String  sx, sxOut;
+            int     i, j, k, p;
+            int     n1In, n2In;
+            int     expon = 0;
+            int     ePos, rPos, eSize;
+            boolean minusSign = false;
+
+            if (x > 0.0)
+            {
+                sx = Double.toString(x);
+            } else if (x < 0.0)
+            {
+                sx        = Double.toString(-x);
+                minusSign = true;
+            } else
+            {
+                sx = Double.toString(x);
+
+                if (sx.charAt(0) == '-')
+                {
+                    minusSign = true;
+                    sx        = sx.substring(1);
+                }
+            }
+
+            ePos = sx.indexOf('E');
+
+            if (ePos == -1)
+            {
+                ePos = sx.indexOf('e');
+            }
+
+            rPos = sx.indexOf('.');
+
+            if (rPos != -1)
+            {
+                n1In = rPos;
+            } else if (ePos != -1)
+            {
+                n1In = ePos;
+            } else
+            {
+                n1In = sx.length();
+            }
+
+            if (rPos != -1)
+            {
+                if (ePos != -1)
+                {
+                    n2In = ePos - rPos - 1;
+                } else
+                {
+                    n2In = sx.length() - rPos - 1;
+                }
+            } else
+            {
+                n2In = 0;
+            }
+
+            if (ePos != -1)
+            {
+                int ie = ePos + 1;
+
+                expon = 0;
+
+                if (sx.charAt(ie) == '-')
+                {
+                    for (++ie; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(ie));
+                    }
+                } else
+                {
+                    if (sx.charAt(ie) == '+')
+                    {
+                        ++ie;
+                    }
+
+                    for (; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(ie));
+                    }
+                }
+            }
+
+            if (rPos != -1)
+            {
+                expon += rPos - 1;
+            }
+
+            if (precisionSet)
+            {
+                p = precision;
+            } else
+            {
+                p = defaultDigits - 1;
+            }
+
+            if ((rPos != -1) && (ePos != -1))
+            {
+                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
+            } else if (rPos != -1)
+            {
+                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
+            } else if (ePos != -1)
+            {
+                ca1 = sx.substring(0, ePos).toCharArray();
+            } else
+            {
+                ca1 = sx.toCharArray();
+            }
+
+            boolean carry = false;
+            int     i0    = 0;
+
+            if (ca1[0] != '0')
+            {
+                i0 = 0;
+            } else
+            {
+                for (i0 = 0; i0 < ca1.length; i0++)
+                {
+                    if (ca1[i0] != '0')
+                    {
+                        break;
+                    }
+                }
+            }
+
+            if (i0 + p < ca1.length - 1)
+            {
+                carry = checkForCarry(ca1, i0 + p + 1);
+
+                if (carry)
+                {
+                    carry = startSymbolicCarry(ca1, i0 + p, i0);
+                }
+
+                if (carry)
+                {
+                    ca2     = new char[i0 + p + 1];
+                    ca2[i0] = '1';
+
+                    for (j = 0; j < i0; j++)
+                    {
+                        ca2[j] = '0';
+                    }
+
+                    for (i = i0, j = i0 + 1; j < p + 1; i++, j++)
+                    {
+                        ca2[j] = ca1[i];
+                    }
+
+                    expon++;
+                    ca1 = ca2;
+                }
+            }
+
+            if ((Math.abs(expon) < 100) &&!optionalL)
+            {
+                eSize = 4;
+            } else
+            {
+                eSize = 5;
+            }
+
+            if (alternateForm ||!precisionSet || (precision != 0))
+            {
+                ca2 = new char[2 + p + eSize];
+            } else
+            {
+                ca2 = new char[1 + eSize];
+            }
+
+            if (ca1[0] != '0')
+            {
+                ca2[0] = ca1[0];
+                j      = 1;
+            } else
+            {
+                for (j = 1; j < ((ePos == -1)
+                                 ? ca1.length
+                                 : ePos); j++)
+                {
+                    if (ca1[j] != '0')
+                    {
+                        break;
+                    }
+                }
+
+                if (((ePos != -1) && (j < ePos)) || ((ePos == -1) && (j < ca1.length)))
+                {
+                    ca2[0] = ca1[j];
+                    expon  -= j;
+                    j++;
+                } else
+                {
+                    ca2[0] = '0';
+                    j      = 2;
+                }
+            }
+
+            if (alternateForm ||!precisionSet || (precision != 0))
+            {
+                ca2[1] = '.';
+                i      = 2;
+            } else
+            {
+                i = 1;
+            }
+
+            for (k = 0; (k < p) && (j < ca1.length); j++, i++, k++)
+            {
+                ca2[i] = ca1[j];
+            }
+
+            for (; i < ca2.length - eSize; i++)
+            {
+                ca2[i] = '0';
+            }
+
+            ca2[i++] = eChar;
+
+            if (expon < 0)
+            {
+                ca2[i++] = '-';
+            } else
+            {
+                ca2[i++] = '+';
+            }
+
+            expon = Math.abs(expon);
+
+            if (expon >= 100)
+            {
+                switch (expon / 100)
+                {
+                case 1 :
+                    ca2[i] = '1';
+
+                    break;
+
+                case 2 :
+                    ca2[i] = '2';
+
+                    break;
+
+                case 3 :
+                    ca2[i] = '3';
+
+                    break;
+
+                case 4 :
+                    ca2[i] = '4';
+
+                    break;
+
+                case 5 :
+                    ca2[i] = '5';
+
+                    break;
+
+                case 6 :
+                    ca2[i] = '6';
+
+                    break;
+
+                case 7 :
+                    ca2[i] = '7';
+
+                    break;
+
+                case 8 :
+                    ca2[i] = '8';
+
+                    break;
+
+                case 9 :
+                    ca2[i] = '9';
+
+                    break;
+                }
+
+                i++;
+            }
+
+            switch ((expon % 100) / 10)
+            {
+            case 0 :
+                ca2[i] = '0';
+
+                break;
+
+            case 1 :
+                ca2[i] = '1';
+
+                break;
+
+            case 2 :
+                ca2[i] = '2';
+
+                break;
+
+            case 3 :
+                ca2[i] = '3';
+
+                break;
+
+            case 4 :
+                ca2[i] = '4';
+
+                break;
+
+            case 5 :
+                ca2[i] = '5';
+
+                break;
+
+            case 6 :
+                ca2[i] = '6';
+
+                break;
+
+            case 7 :
+                ca2[i] = '7';
+
+                break;
+
+            case 8 :
+                ca2[i] = '8';
+
+                break;
+
+            case 9 :
+                ca2[i] = '9';
+
+                break;
+            }
+
+            i++;
+
+            switch (expon % 10)
+            {
+            case 0 :
+                ca2[i] = '0';
+
+                break;
+
+            case 1 :
+                ca2[i] = '1';
+
+                break;
+
+            case 2 :
+                ca2[i] = '2';
+
+                break;
+
+            case 3 :
+                ca2[i] = '3';
+
+                break;
+
+            case 4 :
+                ca2[i] = '4';
+
+                break;
+
+            case 5 :
+                ca2[i] = '5';
+
+                break;
+
+            case 6 :
+                ca2[i] = '6';
+
+                break;
+
+            case 7 :
+                ca2[i] = '7';
+
+                break;
+
+            case 8 :
+                ca2[i] = '8';
+
+                break;
+
+            case 9 :
+                ca2[i] = '9';
+
+                break;
+            }
+
+            int nZeros = 0;
+
+            if (!leftJustify && leadingZeros)
+            {
+                int xThousands = 0;
+
+                if (thousands)
+                {
+                    int xlead = 0;
+
+                    if ((ca2[0] == '+') || (ca2[0] == '-') || (ca2[0] == ' '))
+                    {
+                        xlead = 1;
+                    }
+
+                    int xdp = xlead;
+
+                    for (; xdp < ca2.length; xdp++)
+                    {
+                        if (ca2[xdp] == '.')
+                        {
+                            break;
+                        }
+                    }
+
+                    xThousands = (xdp - xlead) / 3;
+                }
+
+                if (fieldWidthSet)
+                {
+                    nZeros = fieldWidth - ca2.length;
+                }
+
+                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+                {
+                    nZeros--;
+                }
+
+                nZeros -= xThousands;
+
+                if (nZeros < 0)
+                {
+                    nZeros = 0;
+                }
+            }
+
+            j = 0;
+
+            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+            {
+                ca3 = new char[ca2.length + nZeros + 1];
+                j++;
+            } else
+            {
+                ca3 = new char[ca2.length + nZeros];
+            }
+
+            if (!minusSign)
+            {
+                if (leadingSign)
+                {
+                    ca3[0] = '+';
+                }
+
+                if (leadingSpace)
+                {
+                    ca3[0] = ' ';
+                }
+            } else
+            {
+                ca3[0] = '-';
+            }
+
+            for (k = 0; k < nZeros; j++, k++)
+            {
+                ca3[j] = '0';
+            }
+
+            for (i = 0; (i < ca2.length) && (j < ca3.length); i++, j++)
+            {
+                ca3[j] = ca2[i];
+            }
+
+            int lead = 0;
+
+            if ((ca3[0] == '+') || (ca3[0] == '-') || (ca3[0] == ' '))
+            {
+                lead = 1;
+            }
+
+            int dp = lead;
+
+            for (; dp < ca3.length; dp++)
+            {
+                if (ca3[dp] == '.')
+                {
+                    break;
+                }
+            }
+
+            int nThousands = dp / 3;
+
+            // Localize the decimal point.
+            if (dp < ca3.length)
+            {
+                ca3[dp] = dfs.getDecimalSeparator();
+            }
+
+            char[] ca4 = ca3;
+
+            if (thousands && (nThousands > 0))
+            {
+                ca4    = new char[ca3.length + nThousands + lead];
+                ca4[0] = ca3[0];
+
+                for (i = lead, k = lead; i < dp; i++)
+                {
+                    if ((i > 0) && (dp - i) % 3 == 0)
+                    {
+                        // ca4[k]=',';
+                        ca4[k]     = dfs.getGroupingSeparator();
+                        ca4[k + 1] = ca3[i];
+                        k          += 2;
+                    } else
+                    {
+                        ca4[k] = ca3[i];
+                        k++;
+                    }
+                }
+
+                for (; i < ca3.length; i++, k++)
+                {
+                    ca4[k] = ca3[i];
+                }
+            }
+
+            return ca4;
+        }
+
+        /**
+         * Check to see if the digits that are going to
+         * be truncated because of the precision should
+         * force a round in the preceding digits.
+         * @param ca1 the array of digits
+         * @param icarry the index of the first digit that
+         *     is to be truncated from the print
+         * @return <code>true</code> if the truncation forces
+         *     a round that will change the print
+         */
+        private boolean checkForCarry(char[] ca1, int icarry)
+        {
+            boolean carry = false;
+
+            if (icarry < ca1.length)
+            {
+                if ((ca1[icarry] == '6') || (ca1[icarry] == '7') || (ca1[icarry] == '8') || (ca1[icarry] == '9'))
+                {
+                    carry = true;
+                } else if (ca1[icarry] == '5')
+                {
+                    int ii = icarry + 1;
+
+                    for (; ii < ca1.length; ii++)
+                    {
+                        if (ca1[ii] != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    carry = ii < ca1.length;
+
+                    if (!carry && (icarry > 0))
+                    {
+                        carry = ((ca1[icarry - 1] == '1') || (ca1[icarry - 1] == '3') || (ca1[icarry - 1] == '5')
+                                 || (ca1[icarry - 1] == '7') || (ca1[icarry - 1] == '9'));
+                    }
+                }
+            }
+
+            return carry;
+        }
+
+        /**
+         * Start the symbolic carry process.  The process
+         * is not quite finished because the symbolic
+         * carry may change the length of the string and
+         * change the exponent (in e format).
+         * @param cLast index of the last digit changed
+         *     by the round
+         * @param cFirst index of the first digit allowed
+         *     to be changed by this phase of the round
+         * @return <code>true</code> if the carry forces
+         *     a round that will change the print still
+         *     more
+         */
+        private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst)
+        {
+            boolean carry = true;
+
+            for (int i = cLast; carry && (i >= cFirst); i--)
+            {
+                carry = false;
+
+                switch (ca[i])
+                {
+                case '0' :
+                    ca[i] = '1';
+
+                    break;
+
+                case '1' :
+                    ca[i] = '2';
+
+                    break;
+
+                case '2' :
+                    ca[i] = '3';
+
+                    break;
+
+                case '3' :
+                    ca[i] = '4';
+
+                    break;
+
+                case '4' :
+                    ca[i] = '5';
+
+                    break;
+
+                case '5' :
+                    ca[i] = '6';
+
+                    break;
+
+                case '6' :
+                    ca[i] = '7';
+
+                    break;
+
+                case '7' :
+                    ca[i] = '8';
+
+                    break;
+
+                case '8' :
+                    ca[i] = '9';
+
+                    break;
+
+                case '9' :
+                    ca[i] = '0';
+                    carry = true;
+
+                    break;
+                }
+            }
+
+            return carry;
+        }
+
+        /**
+         * An intermediate routine on the way to creating
+         * an e format String.  The method decides whether
+         * the input double value is an infinity,
+         * not-a-number, or a finite double and formats
+         * each type of input appropriately.
+         * @param x the double value to be formatted.
+         * @param eChar an 'e' or 'E' to use in the
+         *     converted double value.
+         * @return the converted double value.
+         */
+        private String eFormatString(double x, char eChar)
+        {
+            boolean noDigits = false;
+            char[]  ca4, ca5;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca4 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca4 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca4 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca4 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca4 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca4 = " NaN".toCharArray();
+                } else
+                {
+                    ca4 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                ca4 = eFormatDigits(x, eChar);
+            }
+
+            ca5 = applyFloatPadding(ca4, false);
+
+            return new String(ca5);
+        }
+
+        /**
+         * Apply zero or blank, left or right padding.
+         * @param ca4 array of characters before padding is
+         *     finished
+         * @param noDigits NaN or signed Inf
+         * @return a padded array of characters
+         */
+        private char[] applyFloatPadding(char[] ca4, boolean noDigits)
+        {
+            char[] ca5 = ca4;
+
+            if (fieldWidthSet)
+            {
+                int i, j, nBlanks;
+
+                if (leftJustify)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+
+                        for (i = 0; i < ca4.length; i++)
+                        {
+                            ca5[i] = ca4[i];
+                        }
+
+                        for (j = 0; j < nBlanks; j++, i++)
+                        {
+                            ca5[i] = ' ';
+                        }
+                    }
+                } else if (!leadingZeros || noDigits)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+
+                        for (i = 0; i < nBlanks; i++)
+                        {
+                            ca5[i] = ' ';
+                        }
+
+                        for (j = 0; j < ca4.length; i++, j++)
+                        {
+                            ca5[i] = ca4[j];
+                        }
+                    }
+                } else if (leadingZeros)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+                        i   = 0;
+                        j   = 0;
+
+                        if (ca4[0] == '-')
+                        {
+                            ca5[0] = '-';
+                            i++;
+                            j++;
+                        }
+
+                        for (int k = 0; k < nBlanks; i++, k++)
+                        {
+                            ca5[i] = '0';
+                        }
+
+                        for (; j < ca4.length; i++, j++)
+                        {
+                            ca5[i] = ca4[j];
+                        }
+                    }
+                }
+            }
+
+            return ca5;
+        }
+
+        /**
+         * Format method for the f conversion character.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printFFormat(double x)
+        {
+            return fFormatString(x);
+        }
+
+        /**
+         * Format method for the e or E conversion
+         * character.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printEFormat(double x)
+        {
+            if (conversionCharacter == 'e')
+            {
+                return eFormatString(x, 'e');
+            } else
+            {
+                return eFormatString(x, 'E');
+            }
+        }
+
+        /**
+         * Format method for the g conversion character.
+         *
+         * For g format, the flag character '-', means that
+         *  the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printGFormat(double x)
+        {
+            String  sx, sy, sz, ret;
+            int     savePrecision = precision;
+            int     i;
+            char[]  ca4, ca5;
+            boolean noDigits = false;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca4 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca4 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca4 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca4 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca4 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca4 = " NaN".toCharArray();
+                } else
+                {
+                    ca4 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                if (!precisionSet)
+                {
+                    precision = defaultDigits;
+                }
+
+                if (precision == 0)
+                {
+                    precision = 1;
+                }
+
+                int ePos = -1;
+
+                if (conversionCharacter == 'g')
+                {
+                    sx   = eFormatString(x, 'e').trim();
+                    ePos = sx.indexOf('e');
+                } else
+                {
+                    sx   = eFormatString(x, 'E').trim();
+                    ePos = sx.indexOf('E');
+                }
+
+                i = ePos + 1;
+
+                int expon = 0;
+
+                if (sx.charAt(i) == '-')
+                {
+                    for (++i; i < sx.length(); i++)
+                    {
+                        if (sx.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (i < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(i));
+                    }
+                } else
+                {
+                    if (sx.charAt(i) == '+')
+                    {
+                        ++i;
+                    }
+
+                    for (; i < sx.length(); i++)
+                    {
+                        if (sx.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (i < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(i));
+                    }
+                }
+
+                // Trim trailing zeros.
+                // If the radix character is not followed by
+                // a digit, trim it, too.
+                if (!alternateForm)
+                {
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        sy = fFormatString(x).trim();
+                    } else
+                    {
+                        sy = sx.substring(0, ePos);
+                    }
+
+                    i = sy.length() - 1;
+
+                    for (; i >= 0; i--)
+                    {
+                        if (sy.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if ((i >= 0) && (sy.charAt(i) == '.'))
+                    {
+                        i--;
+                    }
+
+                    if (i == -1)
+                    {
+                        sz = "0";
+                    } else if (!Character.isDigit(sy.charAt(i)))
+                    {
+                        sz = sy.substring(0, i + 1) + "0";
+                    } else
+                    {
+                        sz = sy.substring(0, i + 1);
+                    }
+
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        ret = sz;
+                    } else
+                    {
+                        ret = sz + sx.substring(ePos);
+                    }
+                } else
+                {
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        ret = fFormatString(x).trim();
+                    } else
+                    {
+                        ret = sx;
+                    }
+                }
+
+                // leading space was trimmed off during
+                // construction
+                if (leadingSpace)
+                {
+                    if (x >= 0)
+                    {
+                        ret = " " + ret;
+                    }
+                }
+
+                ca4 = ret.toCharArray();
+            }
+
+            // Pad with blanks or zeros.
+            ca5       = applyFloatPadding(ca4, false);
+            precision = savePrecision;
+
+            return new String(ca5);
+        }
+
+        /**
+         * Format method for the d conversion specifer and
+         * short argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(short x)
+        {
+            return printDFormat(Short.toString(x));
+        }
+
+        /**
+         * Format method for the d conversion character and
+         * long argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(long x)
+        {
+            return printDFormat(Long.toString(x));
+        }
+
+        /**
+         * Format method for the d conversion character and
+         * int argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(int x)
+        {
+            return printDFormat(Integer.toString(x));
+        }
+
+        /**
+         * Utility method for formatting using the d
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printDFormat(String sx)
+        {
+            int     nLeadingZeros = 0;
+            int     nBlanks       = 0,
+                    n             = 0;
+            int     i             = 0,
+                    jFirst        = 0;
+            boolean neg           = sx.charAt(0) == '-';
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (!neg)
+            {
+                if (precisionSet && (sx.length() < precision))
+                {
+                    nLeadingZeros = precision - sx.length();
+                }
+            } else
+            {
+                if (precisionSet && (sx.length() - 1) < precision)
+                {
+                    nLeadingZeros = precision - sx.length() + 1;
+                }
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+
+                if (!neg && (leadingSign || leadingSpace))
+                {
+                    nBlanks--;
+                }
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            if (leadingSign)
+            {
+                n++;
+            } else if (leadingSpace)
+            {
+                n++;
+            }
+
+            n += nBlanks;
+            n += nLeadingZeros;
+            n += sx.length();
+
+            char[] ca = new char[n];
+
+            if (leftJustify)
+            {
+                if (neg)
+                {
+                    ca[i++] = '-';
+                } else if (leadingSign)
+                {
+                    ca[i++] = '+';
+                } else if (leadingSpace)
+                {
+                    ca[i++] = ' ';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                jFirst = neg
+                         ? 1
+                         : 0;
+
+                for (int j = 0; j < nLeadingZeros; i++, j++)
+                {
+                    ca[i] = '0';
+                }
+
+                for (int j = jFirst; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; i++, j++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (!leadingZeros)
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = ' ';
+                    }
+
+                    if (neg)
+                    {
+                        ca[i++] = '-';
+                    } else if (leadingSign)
+                    {
+                        ca[i++] = '+';
+                    } else if (leadingSpace)
+                    {
+                        ca[i++] = ' ';
+                    }
+                } else
+                {
+                    if (neg)
+                    {
+                        ca[i++] = '-';
+                    } else if (leadingSign)
+                    {
+                        ca[i++] = '+';
+                    } else if (leadingSpace)
+                    {
+                        ca[i++] = ' ';
+                    }
+
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = '0';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                jFirst = neg
+                         ? 1
+                         : 0;
+
+                for (int j = jFirst; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * short argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(short x)
+        {
+            String sx = null;
+
+            if (x == Short.MIN_VALUE)
+            {
+                sx = "8000";
+            } else if (x < 0)
+            {
+                String t;
+
+                if (x == Short.MIN_VALUE)
+                {
+                    t = "0";
+                } else
+                {
+                    t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
+
+                    if ((t.charAt(0) == 'F') || (t.charAt(0) == 'f'))
+                    {
+                        t = t.substring(16, 32);
+                    }
+                }
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 4 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 4);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 4);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 4);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 4);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 4);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 4);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 4);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString((int) x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * long argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(long x)
+        {
+            String sx = null;
+
+            if (x == Long.MIN_VALUE)
+            {
+                sx = "8000000000000000";
+            } else if (x < 0)
+            {
+                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "800000000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "80000000000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "8000000000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "800000000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "80000000000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "8000000000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "800000000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "80000000" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "8000000" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "800000" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "80000" + t;
+
+                    break;
+
+                case 12 :
+                    sx = "8000" + t;
+
+                    break;
+
+                case 13 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 14 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 15 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 16 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 16);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 16);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 16);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 16);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 16);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 16);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 16);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Long.toString(x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * int argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(int x)
+        {
+            String sx = null;
+
+            if (x == Integer.MIN_VALUE)
+            {
+                sx = "80000000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "8000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "800000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "80000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "8000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 8 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 8);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 8);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 8);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 8);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 8);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 8);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 8);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString(x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Utility method for formatting using the x
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printXFormat(String sx)
+        {
+            int nLeadingZeros = 0;
+            int nBlanks       = 0;
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (precisionSet)
+            {
+                nLeadingZeros = precision - sx.length();
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+
+                if (alternateForm)
+                {
+                    nBlanks = nBlanks - 2;
+                }
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            int n = 0;
+
+            if (alternateForm)
+            {
+                n += 2;
+            }
+
+            n += nLeadingZeros;
+            n += sx.length();
+            n += nBlanks;
+
+            char[] ca = new char[n];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                if (alternateForm)
+                {
+                    ca[i++] = '0';
+                    ca[i++] = 'x';
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (!leadingZeros)
+                {
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = ' ';
+                    }
+                }
+
+                if (alternateForm)
+                {
+                    ca[i++] = '0';
+                    ca[i++] = 'x';
+                }
+
+                if (leadingZeros)
+                {
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = '0';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            String caReturn = new String(ca);
+
+            if (conversionCharacter == 'X')
+            {
+                caReturn = caReturn.toUpperCase();
+            }
+
+            return caReturn;
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * short argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(short x)
+        {
+            String sx = null;
+
+            if (x == Short.MIN_VALUE)
+            {
+                sx = "100000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "10000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "1000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "100" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "10" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "1" + t;
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString((int) x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * long argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(long x)
+        {
+            String sx = null;
+
+            if (x == Long.MIN_VALUE)
+            {
+                sx = "1000000000000000000000";
+            } else if (x < 0)
+            {
+                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "100000000000000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "10000000000000000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "1000000000000000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "100000000000000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "10000000000000000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "1000000000000000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "100000000000000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "10000000000000" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "1000000000000" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "100000000000" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "10000000000" + t;
+
+                    break;
+
+                case 12 :
+                    sx = "1000000000" + t;
+
+                    break;
+
+                case 13 :
+                    sx = "100000000" + t;
+
+                    break;
+
+                case 14 :
+                    sx = "10000000" + t;
+
+                    break;
+
+                case 15 :
+                    sx = "1000000" + t;
+
+                    break;
+
+                case 16 :
+                    sx = "100000" + t;
+
+                    break;
+
+                case 17 :
+                    sx = "10000" + t;
+
+                    break;
+
+                case 18 :
+                    sx = "1000" + t;
+
+                    break;
+
+                case 19 :
+                    sx = "100" + t;
+
+                    break;
+
+                case 20 :
+                    sx = "10" + t;
+
+                    break;
+
+                case 21 :
+                    sx = "1" + t;
+
+                    break;
+                }
+            } else
+            {
+                sx = Long.toString(x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * int argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(int x)
+        {
+            String sx = null;
+
+            if (x == Integer.MIN_VALUE)
+            {
+                sx = "20000000000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "2000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "200000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "20000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "2000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "200000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "20000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "2000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "200" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "20" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "2" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "3" + t.substring(1);
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString(x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Utility method for formatting using the o
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printOFormat(String sx)
+        {
+            int nLeadingZeros = 0;
+            int nBlanks       = 0;
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (precisionSet)
+            {
+                nLeadingZeros = precision - sx.length();
+            }
+
+            if (alternateForm)
+            {
+                nLeadingZeros++;
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            int    n  = nLeadingZeros + sx.length() + nBlanks;
+            char[] ca = new char[n];
+            int    i;
+
+            if (leftJustify)
+            {
+                for (i = 0; i < nLeadingZeros; i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (leadingZeros)
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = '0';
+                    }
+                } else
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = ' ';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the c conversion character and
+         * char argument.
+         *
+         * The only flag character that affects c format is
+         * the '-', meaning that the output should be left
+         * justified within the field.  The default is to
+         * pad with blanks on the left.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  Padding is with
+         * blanks by default.  The default width is 1.
+         *
+         * The precision, if set, is ignored.
+         * @param x the char to format.
+         * @return the formatted String.
+         */
+        private String printCFormat(char x)
+        {
+            int nPrint = 1;
+            int width  = fieldWidth;
+
+            if (!fieldWidthSet)
+            {
+                width = nPrint;
+            }
+
+            char[] ca = new char[width];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                ca[0] = x;
+
+                for (i = 1; i <= width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                for (i = 0; i < width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+
+                ca[i] = x;
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the s conversion character and
+         * String argument.
+         *
+         * The only flag character that affects s format is
+         * the '-', meaning that the output should be left
+         * justified within the field.  The default is to
+         * pad with blanks on the left.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is the
+         * smaller of the number of characters in the the
+         * input and the precision.  Padding is with blanks
+         * by default.
+         *
+         * The precision, if set, specifies the maximum
+         * number of characters to be printed from the
+         * string.  A null digit string is treated
+         * as a 0.  The default is not to set a maximum
+         * number of characters to be printed.
+         * @param x the String to format.
+         * @return the formatted String.
+         */
+        private String printSFormat(String x)
+        {
+            int nPrint = x.length();
+            int width  = fieldWidth;
+
+            if (precisionSet && (nPrint > precision))
+            {
+                nPrint = precision;
+            }
+
+            if (!fieldWidthSet)
+            {
+                width = nPrint;
+            }
+
+            int n = 0;
+
+            if (width > nPrint)
+            {
+                n += width - nPrint;
+            }
+
+            if (nPrint >= x.length())
+            {
+                n += x.length();
+            } else
+            {
+                n += nPrint;
+            }
+
+            char[] ca = new char[n];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                if (nPrint >= x.length())
+                {
+                    char[] csx = x.toCharArray();
+
+                    for (i = 0; i < x.length(); i++)
+                    {
+                        ca[i] = csx[i];
+                    }
+                } else
+                {
+                    char[] csx = x.substring(0, nPrint).toCharArray();
+
+                    for (i = 0; i < nPrint; i++)
+                    {
+                        ca[i] = csx[i];
+                    }
+                }
+
+                for (int j = 0; j < width - nPrint; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                for (i = 0; i < width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+
+                if (nPrint >= x.length())
+                {
+                    char[] csx = x.toCharArray();
+
+                    for (int j = 0; j < x.length(); i++, j++)
+                    {
+                        ca[i] = csx[j];
+                    }
+                } else
+                {
+                    char[] csx = x.substring(0, nPrint).toCharArray();
+
+                    for (int j = 0; j < nPrint; i++, j++)
+                    {
+                        ca[i] = csx[j];
+                    }
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Check for a conversion character.  If it is
+         * there, store it.
+         * @return <code>true</code> if the conversion
+         *     character is there, and
+         *     <code>false</code> otherwise.
+         */
+        private boolean setConversionCharacter()
+        {
+            /* idfgGoxXeEcs */
+            boolean ret = false;
+
+            conversionCharacter = '\0';
+
+            if (pos < fmt.length())
+            {
+                char c = fmt.charAt(pos);
+
+                if ((c == 'i') || (c == 'd') || (c == 'f') || (c == 'g') || (c == 'G') || (c == 'o') || (c == 'x') || (c == 'X')
+                    || (c == 'e') || (c == 'E') || (c == 'c') || (c == 's') || (c == '%'))
+                {
+                    conversionCharacter = c;
+                    pos++;
+                    ret = true;
+                }
+            }
+
+            return ret;
+        }
+
+        /**
+         * Check for an h, l, or L in a format.  An L is
+         * used to control the minimum number of digits
+         * in an exponent when using floating point
+         * formats.  An l or h is used to control
+         * conversion of the input to a long or short,
+         * respectively, before formatting.  If any of
+         * these is present, store them.
+         */
+        private void setOptionalHL()
+        {
+            optionalh = false;
+            optionall = false;
+            optionalL = false;
+
+            if (pos < fmt.length())
+            {
+                char c = fmt.charAt(pos);
+
+                if (c == 'h')
+                {
+                    optionalh = true;
+                    pos++;
+                } else if (c == 'l')
+                {
+                    optionall = true;
+                    pos++;
+                } else if (c == 'L')
+                {
+                    optionalL = true;
+                    pos++;
+                }
+            }
+        }
+
+        /**
+         * Set the precision.
+         */
+        private void setPrecision()
+        {
+            int firstPos = pos;
+
+            precisionSet = false;
+
+            if ((pos < fmt.length()) && (fmt.charAt(pos) == '.'))
+            {
+                pos++;
+
+                if ((pos < fmt.length()) && (fmt.charAt(pos) == '*'))
+                {
+                    pos++;
+
+                    if (!setPrecisionArgPosition())
+                    {
+                        variablePrecision = true;
+                        precisionSet      = true;
+                    }
+
+                    return;
+                } else
+                {
+                    while (pos < fmt.length())
+                    {
+                        char c = fmt.charAt(pos);
+
+                        if (Character.isDigit(c))
+                        {
+                            pos++;
+                        } else
+                        {
+                            break;
+                        }
+                    }
+
+                    if (pos > firstPos + 1)
+                    {
+                        String sz = fmt.substring(firstPos + 1, pos);
+
+                        precision    = Integer.parseInt(sz);
+                        precisionSet = true;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Set the field width.
+         */
+        private void setFieldWidth()
+        {
+            int firstPos = pos;
+
+            fieldWidth    = 0;
+            fieldWidthSet = false;
+
+            if ((pos < fmt.length()) && (fmt.charAt(pos) == '*'))
+            {
+                pos++;
+
+                if (!setFieldWidthArgPosition())
+                {
+                    variableFieldWidth = true;
+                    fieldWidthSet      = true;
+                }
+            } else
+            {
+                while (pos < fmt.length())
+                {
+                    char c = fmt.charAt(pos);
+
+                    if (Character.isDigit(c))
+                    {
+                        pos++;
+                    } else
+                    {
+                        break;
+                    }
+                }
+
+                if ((firstPos < pos) && (firstPos < fmt.length()))
+                {
+                    String sz = fmt.substring(firstPos, pos);
+
+                    fieldWidth    = Integer.parseInt(sz);
+                    fieldWidthSet = true;
+                }
+            }
+        }
+
+        /**
+         * Store the digits <code>n</code> in %n$ forms.
+         */
+        private void setArgPosition()
+        {
+            int xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalSpecification = true;
+                    argumentPosition        = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                     = xPos + 1;
+                }
+            }
+        }
+
+        /**
+         * Store the digits <code>n</code> in *n$ forms.
+         */
+        private boolean setFieldWidthArgPosition()
+        {
+            boolean ret = false;
+            int     xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalFieldWidth          = true;
+                    argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                           = xPos + 1;
+                    ret                           = true;
+                }
+            }
+
+            return ret;
+        }
+
+        /**
+         * Store the digits <code>n</code> in *n$ forms.
+         */
+        private boolean setPrecisionArgPosition()
+        {
+            boolean ret = false;
+            int     xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalPrecision          = true;
+                    argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                          = xPos + 1;
+                    ret                          = true;
+                }
+            }
+
+            return ret;
+        }
+
+        boolean isPositionalSpecification()
+        {
+            return positionalSpecification;
+        }
+
+        int getArgumentPosition()
+        {
+            return argumentPosition;
+        }
+
+        boolean isPositionalFieldWidth()
+        {
+            return positionalFieldWidth;
+        }
+
+        int getArgumentPositionForFieldWidth()
+        {
+            return argumentPositionForFieldWidth;
+        }
+
+        boolean isPositionalPrecision()
+        {
+            return positionalPrecision;
+        }
+
+        int getArgumentPositionForPrecision()
+        {
+            return argumentPositionForPrecision;
+        }
+
+        /**
+         * Set flag characters, one of '-+#0 or a space.
+         */
+        private void setFlagCharacters()
+        {
+            /* '-+ #0 */
+            thousands     = false;
+            leftJustify   = false;
+            leadingSign   = false;
+            leadingSpace  = false;
+            alternateForm = false;
+            leadingZeros  = false;
+
+            for (; pos < fmt.length(); pos++)
+            {
+                char c = fmt.charAt(pos);
+
+                if (c == '\'')
+                {
+                    thousands = true;
+                } else if (c == '-')
+                {
+                    leftJustify  = true;
+                    leadingZeros = false;
+                } else if (c == '+')
+                {
+                    leadingSign  = true;
+                    leadingSpace = false;
+                } else if (c == ' ')
+                {
+                    if (!leadingSign)
+                    {
+                        leadingSpace = true;
+                    }
+                } else if (c == '#')
+                {
+                    alternateForm = true;
+                } else if (c == '0')
+                {
+                    if (!leftJustify)
+                    {
+                        leadingZeros = true;
+                    }
+                } else
+                {
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java
new file mode 100644
index 0000000..0ab0b93
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java
@@ -0,0 +1,125 @@
+package com.ximple.io.dgn7;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.ximple.util.PrintfFormat;
+import oracle.jdbc.OracleConnection;
+
+/**
+ * Dgn7OracleReaderTest
+ * User: Ulysses
+ * Date: 2007/10/24
+ * Time: �W�� 10:49:54
+ */
+public class Dgn7OracleReaderTest
+{
+    @BeforeTest
+    public void setUp()
+    {
+
+    }
+
+    @Test
+    public void testOracleReader() throws SQLException, IOException
+    {
+        OracleConnection connection = OracleTarget.getInstance().getOracleConnection();
+        // String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" ORDER BY ROWID";
+        String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" WHERE TAG_SFSC=106 ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchSrcStmtFmt);
+        String srcschema = "SPATIALDB";
+        String srctable = "IGSET_1";
+        String fetchSrcStmt = spf.sprintf(new Object[]{srcschema, srctable});
+
+        Dgn7OracleReader reader = new Dgn7OracleReader(fetchSrcStmt, "IGDSELM", connection);
+        int count = 0;
+        while (reader.hasNext())
+        {
+            Element element = reader.next();
+
+            if (element instanceof ComplexChainElement)
+            {
+                ComplexChainElement complexChain = (ComplexChainElement) element;
+                FrammeAttributeData frammeLinkage = null;
+
+                List<UserAttributeData> attrs = complexChain.getUserAttributeData();
+                for (int k = 0; k < attrs.size(); k++)
+                {
+                    UserAttributeData userAttr = attrs.get(k);
+                    if (userAttr instanceof FrammeAttributeData)
+                    {
+                        frammeLinkage = (FrammeAttributeData) userAttr;
+                        break;
+                    }
+                }
+
+                System.out.print("complexChain:");
+                if (frammeLinkage != null)
+                    System.out.print(":FSC-" + frammeLinkage.getFsc() +
+                                       ":UFID-" + frammeLinkage.getUfid() +
+                                       ":COMP-" + frammeLinkage.getComponentID());
+                else
+                    System.out.print("Linkage is null");
+
+                for (int i = 0; i < complexChain.size(); i++)
+                {
+                    Element elm = (Element) complexChain.get(i);
+                    if (elm instanceof LineStringElement)
+                    {
+                        LineStringElement lineStringElement = (LineStringElement) elm;
+                        int size = lineStringElement.getVerticeSize();
+                        System.out.print("size=" + size + ":");
+                        Coordinate[] coords = lineStringElement.getVertices();
+                        for (int j = 0; j < coords.length; j++)
+                            System.out.print("[" + j + "]" + coords[j].toString());
+                    }
+                }
+
+                System.out.println();
+            } else if (element instanceof TextNodeElement)
+            {
+                TextNodeElement textNode = (TextNodeElement) element;
+
+                FrammeAttributeData frammeLinkage = null;
+
+                List<UserAttributeData> attrs = textNode.getUserAttributeData();
+                for (int k = 0; k < attrs.size(); k++)
+                {
+                    UserAttributeData userAttr = attrs.get(k);
+                    if (userAttr instanceof FrammeAttributeData)
+                    {
+                        frammeLinkage = (FrammeAttributeData) userAttr;
+                        break;
+                    }
+                }
+
+                Coordinate coord = textNode.getOrigin();
+                System.out.print("TextNode:origin=" + coord.toString());
+                if (frammeLinkage != null)
+                    System.out.print(":FSC-" + frammeLinkage.getFsc() +
+                                       ":UFID-" + frammeLinkage.getUfid() +
+                                       ":COMP-" + frammeLinkage.getComponentID());
+                else
+                    System.out.print("Linkage is null");
+                for (int i = 0; i < textNode.size(); i++)
+                {
+                    Element elm = (Element) textNode.get(i);
+                    if (elm instanceof TextElement)
+                    {
+                        TextElement textElm = (TextElement) elm;
+                        System.out.print("---");
+                        String text = textElm.getText();
+                        System.out.print("'" + text + "'");
+                    }
+                }
+                System.out.println();
+            }
+        }
+    }
+
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java
new file mode 100644
index 0000000..cc1ed35
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java
@@ -0,0 +1,180 @@
+package com.ximple.io.dgn7;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.geotools.TestData;
+
+/**
+ * Dgn7TextElementReaderTest
+ * User: Ulysses
+ * Date: 2008/1/10
+ * Time: �W�� 12:19:14
+ */
+public class Dgn7TextElementReaderTest
+{
+    private final static Logger logger = Logger.getLogger(Dgn7fileReaderTest.class);
+
+    private final static String testFilePathCreated = "demo.dgn";
+    private final static String testFilePathExist = "HV88491-1.dgn";
+    private final static String testFilePathPostComplete = "HV88494_0.dgn";
+
+    private FileInputStream _fs;
+
+    @BeforeTest
+    public void setUp() throws FileNotFoundException
+    {
+    }
+
+    @Test
+    public void testRead() throws Dgn7fileException, IOException
+    {
+        File dataFile = TestData.file(this, testFilePathCreated);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathCreated);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+
+        dataFile = TestData.file(this, testFilePathExist);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathExist);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+
+        dataFile = TestData.file(this, testFilePathPostComplete);
+        if (dataFile.exists())
+        {
+            System.out.println("Output--" + testFilePathPostComplete);
+            _fs = new FileInputStream(dataFile);
+            FileChannel fc = _fs.getChannel();
+            dumpElements(fc);
+            fc.close();
+            _fs.close();
+        }
+    }
+
+    public void dumpElements(FileChannel fc) throws Dgn7fileException, IOException
+    {
+        Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    if (lastComplex != null)
+                    {
+                        // @todo add process in here
+                        lastComplex = null;
+                    }
+
+                    // @todo add process in here
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        // @todo add process in here
+                        lastComplex = element;
+                    }
+                }
+
+                if (element.getElementType().isComplexElement())
+                {
+                    if (element instanceof ComplexChainElement)
+                    {
+                        ComplexChainElement complexChain = (ComplexChainElement) element;
+                        int size = complexChain.size();
+                        for (Object aComplexChain : complexChain)
+                        {
+                            Element subElement = (Element) aComplexChain;
+                            subElement.getType();
+                        }
+                    }
+
+                    if (element instanceof ComplexShapeElement)
+                    {
+                        ComplexShapeElement complexShape = (ComplexShapeElement) element;
+                    }
+
+                    if (element instanceof TextNodeElement)
+                    {
+                        TextNodeElement textNode = (TextNodeElement) element;
+                        int size = textNode.size();
+                        for (int i = 0; i < size; i++)
+                        {
+                            Element subElement = (Element) textNode.get(i);
+                            subElement.getElementType();
+                        }
+                    }
+
+                } else
+                {
+                    boolean hasLinkage = false;
+                    if (element instanceof TextElement)
+                    {
+                        TextElement textElm = (TextElement) element;
+                        List<UserAttributeData> usrData = textElm.getUserAttributeData();
+                        Iterator<UserAttributeData> it = usrData.iterator();
+                        while (it.hasNext())
+                        {
+                            UserAttributeData attr = it.next();
+                            if (attr instanceof FrammeAttributeData)
+                            {
+                                hasLinkage = true;
+                                System.out.println("------------------------------------------");
+                                System.out.println("FSC=" + ((FrammeAttributeData) attr).getFsc() + ":" +
+                                        ((FrammeAttributeData) attr).getUfid());
+                            }
+                        }
+
+                        if (hasLinkage)
+                        {
+                            System.out.println("Text.Font=" + textElm.getFontIndex());
+                            System.out.println("Text.Just=" + textElm.getJustification());
+                            System.out.println("usrData.len=" + usrData.size());
+                            System.out.println("text=" + textElm.getText());
+                            System.out.println("Origin=" + textElm.getOrigin());
+                            System.out.println("UserOrigin=" + textElm.getUserOrigin());
+                        }
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.info("ElementRecord Count=" + count);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java
new file mode 100644
index 0000000..314427c
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java
@@ -0,0 +1,119 @@
+package com.ximple.io.dgn7;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import org.apache.log4j.Logger;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import org.geotools.TestData;
+
+/**
+ * Dgn7fileReaderTest
+ * User: Ulysses
+ * Date: 2007/10/24
+ * Time: �W�� 01:43:41
+ * To change this template use File | Settings | File Templates.
+ */
+public class Dgn7fileReaderTest
+{
+    private final static Logger logger = Logger.getLogger(Dgn7fileReaderTest.class);
+
+    // private final static String testFilePath = "test-data\\testHV.dgn";
+    private final static String testFilePath = "testHV.dgn";
+    private FileInputStream _fs;
+
+    @BeforeTest
+    public void setUp() throws IOException
+    {
+        File dataFile = TestData.file(this, testFilePath);
+        if (!dataFile.exists())
+        {
+            return;
+        }
+
+        _fs = new FileInputStream(dataFile);
+    }
+
+    @Test
+    public void testRead() throws Dgn7fileException, IOException
+    {
+        if (_fs == null) return;
+        FileChannel fc = _fs.getChannel();
+        Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    if (lastComplex != null)
+                    {
+                        // @todo add process in here
+                        lastComplex = null;
+                    }
+
+                    // @todo add process in here
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        // @todo add process in here
+                        lastComplex = element;
+                    }
+                }
+
+                if (element.getElementType().isComplexElement())
+                {
+                    if (element instanceof ComplexChainElement)
+                    {
+                        ComplexChainElement complexChain = (ComplexChainElement) element;
+                        int size = complexChain.size();
+                        for (Object aComplexChain : complexChain)
+                        {
+                            Element subElement = (Element) aComplexChain;
+                            subElement.getType();
+                        }
+                    }
+
+                    if (element instanceof ComplexShapeElement)
+                    {
+                        ComplexShapeElement complexShape = (ComplexShapeElement) element;
+                    }
+
+                    if (element instanceof TextNodeElement)
+                    {
+                        TextNodeElement textNode = (TextNodeElement) element;
+                        int size = textNode.size();
+                        for (int i = 0; i < size; i++)
+                        {
+                            Element subElement = (Element) textNode.get(i);
+                            subElement.getElementType();
+                        }
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.info("ElementRecord Count=" + count);
+    }
+}
diff --git a/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java
new file mode 100644
index 0000000..1279340
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java
@@ -0,0 +1,162 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.util.Assert;
+import oracle.jdbc.OracleConnection;
+
+/**
+ * OracleTarget
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: ?U?? 03:12:43
+ * To change this template use File | Settings | File Templates.
+ */
+public class OracleTarget
+{
+    private static final Logger logger          = Logger.getLogger(OracleTarget.class);
+    private static OracleTarget _instance       = null;
+    private static final String ORACLE_URL      = "jdbc:oracle:thin:@";
+    private static final String _propUsrKey     = "user";
+    private static final String _propPassKey    = "password";
+    private static String       _oracleHost     = "192.168.11.200";
+    private static String       _oracleInstance = "NNTPC";
+    private static String       _oraclePort     = "1521";
+
+    static
+    {
+        try
+        {
+            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
+        } catch (SQLException e)
+        {
+            Assert.shouldNeverReachHere(e.getMessage());
+        }
+    }
+
+    private OracleConnection oracleConnection = null;
+    private Properties       properties;
+
+    private OracleTarget()
+    {
+        properties = new Properties();
+        properties.put(_propUsrKey, "SPATIALDB");
+        properties.put(_propPassKey, "SPATIALDB000");
+    }
+
+    public static String getOracleHost()
+    {
+        return _oracleHost;
+    }
+
+    public static void setOracleHost(String oracleHost)
+    {
+        OracleTarget._oracleHost = oracleHost;
+    }
+
+    public static String getOracleInstance()
+    {
+        return _oracleInstance;
+    }
+
+    public static void setOracleInstance(String oracleInstance)
+    {
+        OracleTarget._oracleInstance = oracleInstance;
+    }
+
+    public static String getOraclePort()
+    {
+        return _oraclePort;
+    }
+
+    public static void setOraclePort(String oraclePort)
+    {
+        OracleTarget._oraclePort = oraclePort;
+    }
+
+    public static String getCurrentURL()
+    {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(ORACLE_URL);
+        builder.append(_oracleHost);
+        builder.append(":");
+        builder.append(_oraclePort);
+        builder.append(":");
+        builder.append(_oracleInstance);
+
+        return builder.toString();
+    }
+
+    public String getLoginPass()
+    {
+        return (String) properties.get(_propPassKey);
+    }
+
+    public void setLoginPass(String loginPass)
+    {
+        properties.put(_propPassKey, loginPass);
+    }
+
+    public String getLoginUsr()
+    {
+        return (String) properties.get(_propUsrKey);
+    }
+
+    public void setLoginUsr(String loginUsr)
+    {
+        properties.put(_propUsrKey, loginUsr);
+    }
+
+    public static OracleTarget getInstance()
+    {
+        if (_instance == null)
+        {
+            _instance = new OracleTarget();
+        }
+
+        return _instance;
+    }
+
+    public OracleConnection getOracleConnection()
+    {
+        try
+        {
+            if (oracleConnection == null)
+            {
+                oracleConnection = (OracleConnection) DriverManager.getConnection(getCurrentURL(), properties);
+            }
+
+            return oracleConnection;
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+        }
+
+        oracleConnection = null;
+
+        return null;
+    }
+
+    public void closeConnection()
+    {
+        try
+        {
+            if (oracleConnection != null)
+            {
+                oracleConnection.close();
+                oracleConnection = null;
+            }
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+}
+
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn
new file mode 100644
index 0000000..d749f68
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/Demo.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn
new file mode 100644
index 0000000..acbb8a2
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491-1.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn
new file mode 100644
index 0000000..f29f86f
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88491_0888888.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn
new file mode 100644
index 0000000..4899d19
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn
new file mode 100644
index 0000000..05fb839
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn
Binary files differ
diff --git a/xdgnjobs/ximple-jobcarrier/pom.xml b/xdgnjobs/ximple-jobcarrier/pom.xml
new file mode 100644
index 0000000..62822cf
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/pom.xml
@@ -0,0 +1,141 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.3.0</version>
+  </parent>
+
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-jobcarrier</artifactId>
+  <version>0.3.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-jobcarrier</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <xdgnio.version>0.3.0</xdgnio.version>
+  </properties>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xspatialjob/truck/</url>
+  </scm>
+
+  <description>
+    Ximple Job Carrier for Quartz
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>quartz</artifactId>
+      <groupId>opensymphony</groupId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.transaction</groupId>
+      <artifactId>jta</artifactId>
+      <version>1.0.1B</version>
+    </dependency>
+
+    <!-- Ximple Library -->
+    <dependency>
+      <artifactId>ximple-dgnio</artifactId>
+      <groupId>com.ximple.eofms</groupId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+    <dependency>
+      <artifactId>ximple-spatialjob</artifactId>
+      <groupId>com.ximple.eofms</groupId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- ======================================================= -->
+      <!--     JAR packaging.                                      -->
+      <!-- ======================================================= -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <mainClass>com.ximple.eofms.XQuartzJobCarrier</mainClass>
+              <addClasspath>true</addClasspath>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+      <!-- ======================================================= -->
+      <!--     exec jar.                                           -->
+      <!-- ======================================================= -->
+      <plugin>
+        <!--
+           Use maven from the command line:
+             mvn exec:java -Dexec.mainClass="com.ximple.eofms.XQuartzJobCarrier"
+        -->
+        <artifactId>exec-maven-plugin</artifactId>
+        <groupId>org.codehaus.mojo</groupId>
+        <!--
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+        -->
+        <configuration>
+          <mainClass>com.ximple.eofms.XQuartzJobCarrier</mainClass>
+        </configuration>
+        <!--
+        <dependencies>
+          <dependency>
+             <groupId>com.ximple.eofms</groupId>
+             <artifactId>ximple-jobcarrier</artifactId>
+             <version>0.0.1</version>
+             <type>jar</type>
+           </dependency>
+        </dependencies>
+        -->
+      </plugin>
+    </plugins>
+    <resources>
+    </resources>
+  </build>
+
+</project>
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java b/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java
new file mode 100644
index 0000000..f2eef84
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/java/com/ximple/eofms/XQuartzJobCarrier.java
@@ -0,0 +1,95 @@
+package com.ximple.eofms;
+
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerUtils;
+import org.quartz.impl.StdSchedulerFactory;
+
+import com.ximple.eofms.jobs.OracleConvertDgn2ShpJob;
+
+/**
+ * Hello world!
+ */
+public class XQuartzJobCarrier
+{
+    static Log logger = LogFactory.getLog(XQuartzJobCarrier.class);
+
+
+    public static void main(String[] args)
+    {
+        XQuartzJobCarrier instance = new XQuartzJobCarrier();
+        instance.startScheduler();
+    }
+
+    public void startScheduler()
+    {
+        Scheduler scheduler = null;
+        boolean shutdown = false;
+
+        try
+        {
+            // Get a Scheduler instance from the Factory
+            scheduler = StdSchedulerFactory.getDefaultScheduler();
+
+            // Start the scheduler
+            scheduler.start();
+            logger.info("Scheduler started at " + new Date());
+
+        } catch (SchedulerException ex)
+        {
+            // deal with any exceptions
+            logger.error(ex);
+            shutdown = true;
+        } catch (Throwable throwable)
+        {
+            logger.error(throwable.getMessage(), throwable);
+            shutdown = true;
+        }
+        if (shutdown)
+        {
+            try
+            {
+                scheduler.shutdown();
+            } catch (SchedulerException e)
+            {
+                logger.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    /*
+     * return an instance of the Scheduler from the factory
+     */
+    public Scheduler createScheduler() throws SchedulerException
+    {
+        return StdSchedulerFactory.getDefaultScheduler();
+    }
+
+    // Create and Schedule a ScanDirectoryJob with the Scheduler
+    private void scheduleJob(Scheduler scheduler) throws SchedulerException
+    {
+
+        // Create a JobDetail for the Job
+        JobDetail jobDetail = new JobDetail("ScanDirectory", Scheduler.DEFAULT_GROUP,
+                OracleConvertDgn2ShpJob.class);
+
+        // Configure the directory to scan
+        jobDetail.getJobDataMap().put("SCAN_DIR", "c:\\quartz-book\\input");
+
+        // Create a trigger that fires every 10 seconds, forever
+        Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);
+        trigger.setName("scanTrigger");
+        // Start the trigger firing from now
+        trigger.setStartTime(new Date());
+
+        // Associate the trigger with the job in the scheduler
+        scheduler.scheduleJob(jobDetail, trigger);
+    }
+
+}
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
new file mode 100644
index 0000000..2691982
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
@@ -0,0 +1,28 @@
+# Create stdout appender
+log4j.rootLogger=error, logfile, stdout
+
+# Configure the stdout appender to go to the Console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+
+# Configure stdout appender to use the PatternLayout
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# Pattern output the caller's filename and line #
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+#log4j.appender.stdout.encoding=UTF-8
+
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.file=xjobcarrier.log
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+#log4j.appender.logfile.encoding=UTF-8
+
+log4j.appender.remoteout=com.holub.log4j.RemoteAppender
+log4j.appender.remoteout.Port=8011
+log4j.appender.remoteout.layout=org.apache.log4j.PatternLayout
+log4j.appender.remoteout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+#log4j.appender.remoteout.encoding=UTF-8
+
+# Print messages of level INFO or above for examples
+log4j.logger.org.cavaness.quartzbook=INFO
+log4j.logger.com.ximple.eofms=INFO
\ No newline at end of file
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties
new file mode 100644
index 0000000..be35e19
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties
@@ -0,0 +1,28 @@
+#===============================================================
+#Configure Main Scheduler Properties
+#===============================================================
+org.quartz.scheduler.instanceName = QuartzScheduler
+org.quartz.scheduler.instanceId = AUTO
+
+#===============================================================
+#Configure ThreadPool
+#===============================================================
+org.quartz.threadPool.threadCount =  5
+org.quartz.threadPool.threadPriority = 5
+org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
+
+#===============================================================
+#Configure JobStore
+#===============================================================
+org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
+
+#===============================================================
+#Configure Plugins
+#===============================================================
+org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
+
+org.quartz.plugin.jobInitializer.fileName = quartz_jobs.xml
+
+org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
+org.quartz.plugin.jobInitializer.failOnFileNotFound = true
+org.quartz.plugin.jobInitializer.validating=false
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
new file mode 100644
index 0000000..be5a288
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
@@ -0,0 +1,99 @@
+<?xml version='1.0' encoding='utf-8'?>
+
+<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData
+        http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"
+        version="1.5">
+
+  <job>
+    <job-detail>
+      <name>ConvertDgn2ShpIntoDirectory</name>
+      <group>DEFAULT</group>
+      <description>A job that convert dgn to shapefiles</description>
+      <job-class>com.ximple.eofms.jobs.OracleConvertDgn2ShpJob</job-class>
+      <volatility>false</volatility>
+      <durability>false</durability>
+      <recover>false</recover>
+      <job-data-map allows-transient-data="true">
+        <entry>
+          <key>SHPDATA_DIR</key>
+          <value>g:\temp\data</value>
+        </entry>
+        <!--
+        <entry>
+          <key>SHPFILTER_CONF</key>
+          <value></value>
+        </entry>
+        -->
+        <entry>
+          <key>ORAHOST</key>
+          <value>192.168.11.200</value>
+        </entry>
+        <entry>
+          <key>ORAINST</key>
+          <value>nntpc</value>
+        </entry>
+        <entry>
+          <key>ORAPORT</key>
+          <value>1521</value>
+        </entry>
+        <entry>
+          <key>ORAUSER</key>
+          <value>spatialdb</value>
+        </entry>
+        <entry>
+          <key>ORAPASS</key>
+          <value>spatialdb000</value>
+        </entry>
+        <entry>
+          <key>ORGSCHEMA</key>
+          <value>SPATIALDB, CMMS_SPATIALDB</value>
+        </entry>
+        <entry>
+          <key>CONVERTDB</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>CONVERTFILE</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>CONVERTELEMIN</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>CREATEDUMMY</key>
+          <value>true</value>
+        </entry>
+        <entry>
+          <key>ELEMLOG</key>
+          <value>true</value>
+        </entry>
+        <entry>
+          <key>TESTMODE</key>
+          <value>FALSE</value>
+        </entry>
+        <entry>
+          <key>TESTCOUNT</key>
+          <value>2</value>
+        </entry>
+      </job-data-map>
+    </job-detail>
+
+    <trigger>
+      <simple>
+        <name>convertTrigger</name>
+        <group>DEFAULT</group>
+        <job-name>ConvertDgn2ShpIntoDirectory</job-name>
+        <job-group>DEFAULT</job-group>
+        <start-time>2008-03-01T18:10:00</start-time>
+        <!-- repeat indefinitely every 10 seconds -->
+        <repeat-count>0</repeat-count>
+        <repeat-interval>500</repeat-interval>
+        <!-- <repeat-interval>72000000</repeat-interval> -->
+      </simple>
+    </trigger>
+
+  </job>
+</quartz>
diff --git a/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java b/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java
new file mode 100644
index 0000000..d907cc1
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java
@@ -0,0 +1,19 @@
+package com.ximple.eofms;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Unit test for simple App.
+ */
+public class XQuartzJobCarrierTest
+{
+    /**
+     * Rigourous Test :-)
+     */
+    @Test
+    public void testApp()
+    {
+        Assert.assertTrue(true);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/pom.xml b/xdgnjobs/ximple-spatialjob/pom.xml
new file mode 100644
index 0000000..a3fd413
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.ximple.eofms</groupId>
+    <artifactId>ximple-dgnjobs</artifactId>
+    <version>0.3.0</version>
+  </parent>
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-spatialjob</artifactId>
+  <version>0.3.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-spatialjob</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <properties>
+    <xdgnio.version>0.3.0</xdgnio.version>
+  </properties>
+
+  <description>
+    Ximple Spatial Data Job for Quartz
+  </description>
+
+  <organization>
+    <name>Ximple</name>
+    <url>http://www.ximple.com.tw</url>
+  </organization>
+
+  <inceptionYear>2008</inceptionYear>
+
+  <developers>
+    <developer>
+      <name>Kuo-Feng Kao</name>
+      <id>ulysseskao</id>
+      <email>ulysseskao@ximple.com.tw</email>
+      <organization>Ximple</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+  </developers>
+
+  <contributors>
+  </contributors>
+
+  <!-- =========================================================== -->
+  <!--     Dependencies to be inherited by all modules.            -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <artifactId>quartz</artifactId>
+      <groupId>opensymphony</groupId>
+    </dependency>
+
+    <!-- Ximple Library -->
+    <dependency>
+      <groupId>com.ximple.eofms</groupId>
+      <artifactId>ximple-dgnio</artifactId>
+      <version>${xdgnio.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+    </plugins>
+    <resources>
+    </resources>
+  </build>
+</project>
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java
new file mode 100644
index 0000000..b39643c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java
@@ -0,0 +1,75 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ximple.io.dgn7.Element;
+
+public abstract class AbstractDispatchableFilter implements ElementDispatchableFilter
+{
+    private String name;
+    private String description;
+    private LinkedList<ElementTypeCriterion> typeIdCriterions;
+    private LinkedList<ElementLevelCriterion> levelCriterions;
+
+    protected Log logger = LogFactory.getLog(AbstractFLinkageDispatchableFilter.class);
+
+    public AbstractDispatchableFilter()
+    {
+        typeIdCriterions = new LinkedList<ElementTypeCriterion>();
+        levelCriterions = new LinkedList<ElementLevelCriterion>();
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    public void addCriterion(ElementTypeCriterion criterion)
+    {
+        typeIdCriterions.add(criterion);
+    }
+
+    public void addLevelCriterion(ElementLevelCriterion criterion)
+    {
+        levelCriterions.add(criterion);
+    }
+
+    protected int compareType(Element element)
+    {
+        for (ElementTypeCriterion criterion : typeIdCriterions)
+        {
+            if (criterion.compareTo(element) == 0)
+                return 0;
+        }
+        return -1;
+    }
+
+    protected int compareLevel(Element element)
+    {
+        for (ElementLevelCriterion criterion : levelCriterions)
+        {
+            if (criterion.compareTo(element) == 0)
+                return 0;
+        }
+        return -1;
+    }
+
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java
new file mode 100644
index 0000000..48be949
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java
@@ -0,0 +1,26 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public abstract class AbstractFLinkageDispatchableFilter extends AbstractDispatchableFilter
+{
+    public static FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java
new file mode 100644
index 0000000..6cc80e7
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java
@@ -0,0 +1,96 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.ArcElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateArcLineStringStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateLineStringStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateArcLineStringStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof ArcElement)
+        {
+            ArcElement lineStringElement = (ArcElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    lineStringElement.getWeight(),
+                    lineStringElement.getLineStyle()
+            });
+            return feature;
+        }
+        return null;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java
new file mode 100644
index 0000000..12e32d4
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java
@@ -0,0 +1,97 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.EllipseElement;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateEllipseShapeStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateShapeStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateEllipseShapeStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof EllipseElement)
+        {
+            EllipseElement shapeElement = (EllipseElement) element;
+            convertDecorator.setConverter(shapeElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    shapeElement.getLevelIndex(),
+                    colorTable.getColorCode(shapeElement.getColorIndex()),
+                    shapeElement.getWeight(),
+                    shapeElement.getLineStyle()
+            });
+            return feature;
+        }
+        return null;
+    }
+}
+
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java
new file mode 100644
index 0000000..7f8c9f7
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java
@@ -0,0 +1,15 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+
+public interface CreateFeatureTypeStrategy
+{
+    public FeatureType createFeatureElement(String featureName) throws SchemaException;
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException;
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
new file mode 100644
index 0000000..3df0256
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
@@ -0,0 +1,147 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.ComplexChainElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.LineElement;
+import com.ximple.io.dgn7.LineStringElement;
+import com.ximple.io.dgn7.UserAttributeData;
+import com.ximple.io.dgn7.ArcElement;
+
+public class CreateLineStringStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateLineStringStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateLineStringStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    lineStringElement.getWeight(),
+                    lineStringElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChain);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    complexChain.getLevelIndex(),
+                    colorTable.getColorCode(complexChain.getColorIndex()),
+                    complexChain.getWeight(),
+                    complexChain.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof LineElement)
+        {
+            LineElement lineElement = (LineElement) element;
+            convertDecorator.setConverter(lineElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineElement.getLevelIndex(),
+                    colorTable.getColorCode(lineElement.getColorIndex()),
+                    lineElement.getWeight(),
+                    lineElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ArcElement)
+        {
+            ArcElement lineStringElement = (ArcElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    lineStringElement.getWeight(),
+                    lineStringElement.getLineStyle()
+            });
+            return feature;
+        }
+
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
new file mode 100644
index 0000000..3d5c47f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
@@ -0,0 +1,164 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.TWDDatumConverter;
+import com.ximple.io.dgn7.ComplexChainElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.LineElement;
+import com.ximple.io.dgn7.LineStringElement;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateLineTextStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateLineTextStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateLineTextStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    lineStringElement.getWeight(),
+                    lineStringElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof TextElement)
+        {
+            TextElement txtElement = (TextElement) element;
+            Coordinate ptOrigin = txtElement.getUserOrigin();
+            Coordinate ptEnd = new Coordinate();
+            ptEnd.x = ptOrigin.x;
+            ptEnd.y = ptOrigin.y + txtElement.getTextHeight();
+            Coordinate[] vect = new Coordinate[2];
+            vect[0] = TWDDatumConverter.fromTM2ToTWD97(ptOrigin);
+            vect[1] = TWDDatumConverter.fromTM2ToTWD97(ptEnd);
+
+            LineString line = geometryFactory.createLineString(vect);
+            // convertDecorator.setConverter(txtElement);
+            // Geometry geom = convertDecorator.toGeometry(geometryFactory);
+
+            txtElement.getRotationAngle();
+
+            Feature feature = featureType.create(new Object[]{
+                    line,
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    txtElement.getWeight(),
+                    txtElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChain);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    complexChain.getLevelIndex(),
+                    colorTable.getColorCode(complexChain.getColorIndex()),
+                    complexChain.getWeight(),
+                    complexChain.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof LineElement)
+        {
+            LineElement lineElement = (LineElement) element;
+            convertDecorator.setConverter(lineElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    lineElement.getLevelIndex(),
+                    colorTable.getColorCode(lineElement.getColorIndex()),
+                    lineElement.getWeight(),
+                    lineElement.getLineStyle()
+            });
+            return feature;
+        }
+
+        return null;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java
new file mode 100644
index 0000000..2d0d165
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java
@@ -0,0 +1,112 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.ComplexShapeElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.ShapeElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateShapeStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateShapeStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateShapeStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof ShapeElement)
+        {
+            ShapeElement shapeElement = (ShapeElement) element;
+            convertDecorator.setConverter(shapeElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    shapeElement.getLevelIndex(),
+                    colorTable.getColorCode(shapeElement.getColorIndex()),
+                    shapeElement.getWeight(),
+                    shapeElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ComplexShapeElement)
+        {
+            ComplexShapeElement complexShape = (ComplexShapeElement) element;
+            convertDecorator.setConverter(complexShape);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    complexShape.getLevelIndex(),
+                    colorTable.getColorCode(complexShape.getColorIndex()),
+                    complexShape.getWeight(),
+                    complexShape.getLineStyle()
+            });
+            return feature;
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java
new file mode 100644
index 0000000..4e27822
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java
@@ -0,0 +1,126 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateSymbolStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateSymbolStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateSymbolStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("JUST", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("HEIGHT", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("WIDTH", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("ANGLE", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMBOL", String.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof TextElement)
+        {
+            TextElement txtElement = (TextElement) element;
+            double angle = txtElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            if (txtElement.getText().length() == 0)
+            {
+                logger.info("CreateSymbolStrategy cannot conver " + element.toString() +
+                        "to Feature - getText() is empty.");
+                return null;
+            }
+            StringBuilder sb = new StringBuilder();
+            sb.append("OCT");
+            char id = txtElement.getText().toCharArray()[0];
+            sb.append(Integer.toOctalString((int) id));
+            sb.append("-");
+            sb.append(txtElement.getFontIndex());
+
+            convertDecorator.setConverter(txtElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    txtElement.getWeight(),
+                    txtElement.getLineStyle(),
+                    txtElement.getJustification(),
+                    txtElement.getTextHeight(),
+                    txtElement.getTextWidth(),
+                    angle,
+                    sb.toString()
+            });
+            return feature;
+        } else
+        {
+            logger.info("CreateSymbolStrategy cannot conver " + element.toString() + "to Feature");
+        }
+        return null;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java
new file mode 100644
index 0000000..98a9d75
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java
@@ -0,0 +1,142 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.TextNodeElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class CreateTextStrategy implements CreateFeatureTypeStrategy
+{
+    static final Log logger = LogFactory.getLog(CreateTextStrategy.class);
+    GeometryFactory geometryFactory = new GeometryFactory();
+    TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    public CreateTextStrategy()
+    {
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("TID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("OID", Long.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LID", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("LEVEL", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("JUST", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("HEIGHT", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("WIDTH", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("ANGLE", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CONTEXT", String.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        if (fLinkage == null) return null;
+        if (element instanceof TextElement)
+        {
+            TextElement txtElement = (TextElement) element;
+            double angle = txtElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            convertDecorator.setConverter(txtElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    txtElement.getWeight(),
+                    txtElement.getLineStyle(),
+                    txtElement.getJustification(),
+                    txtElement.getTextHeight(),
+                    txtElement.getTextWidth(),
+                    angle,
+                    txtElement.getText()
+            });
+            return feature;
+        } else if (element instanceof TextNodeElement)
+        {
+            TextNodeElement nodeElement = (TextNodeElement) element;
+            String[] texts = nodeElement.getTextArray();
+            StringBuffer sb = new StringBuffer();
+            for (String text : texts)
+            {
+                if (sb.length() != 0)
+                    sb.append("\n");
+                sb.append(text);
+            }
+
+            double angle = nodeElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            convertDecorator.setConverter(nodeElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    (int) fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (int) fLinkage.getComponentID(),
+                    0,
+                    nodeElement.getLevelIndex(),
+                    colorTable.getColorCode(nodeElement.getColorIndex()),
+                    nodeElement.getWeight(),
+                    nodeElement.getLineStyle(),
+                    nodeElement.getJustification(),
+                    nodeElement.getTextNodeHeight(),
+                    nodeElement.getTextNodeLength(),
+                    angle,
+                    sb.toString()
+            });
+            return feature;
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java
new file mode 100644
index 0000000..9ed97d1
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java
@@ -0,0 +1,12 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+
+import com.ximple.io.dgn7.Element;
+
+public interface ElementDispatchableFilter
+{
+    public boolean isDispatchable(Element element);
+
+    public Feature execute(Element element);
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java
new file mode 100644
index 0000000..48df964
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java
@@ -0,0 +1,39 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import org.geotools.feature.Feature;
+
+import com.ximple.io.dgn7.Element;
+
+public class ElementDispatcher
+{
+    private LinkedList<ElementDispatchableFilter> rules;
+
+    public ElementDispatcher()
+    {
+        rules = new LinkedList<ElementDispatchableFilter>();
+    }
+
+    public LinkedList<ElementDispatchableFilter> getRules()
+    {
+        return rules;
+    }
+
+    public void addRule(ElementDispatchableFilter rule)
+    {
+        rules.add(rule);
+    }
+
+    public Feature execute(Element element)
+    {
+        for (ElementDispatchableFilter rule : rules)
+        {
+            if (rule.isDispatchable(element))
+            {
+                return rule.execute(element);
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java
new file mode 100644
index 0000000..679c157
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementLevelCriterion.java
@@ -0,0 +1,48 @@
+package com.ximple.eofms.filter;
+
+import java.util.ArrayList;
+
+import com.ximple.io.dgn7.Element;
+
+public class ElementLevelCriterion implements Comparable
+{
+    private int elementLevel;
+    private ArrayList<Integer> elementLevelArray;
+
+    public ElementLevelCriterion()
+    {
+        elementLevelArray = new ArrayList<Integer>();
+    }
+
+    public int compareTo(Object o)
+    {
+        if (o instanceof Element)
+        {
+            Element elm = (Element) o;
+
+            for (Integer elevel : getElementLevelArray())
+            {
+                if (elm.getElementType().id == elevel.intValue())
+                    return 0;
+            }
+
+        }
+        return -1;
+    }
+
+    public int getElementLevel()
+    {
+        return elementLevel;
+    }
+
+    public ArrayList<Integer> getElementLevelArray()
+    {
+        return elementLevelArray;
+    }
+
+    public void setElementLevel(int iLevel)
+    {
+        this.elementLevel = iLevel;
+        this.elementLevelArray.add(iLevel);
+    }
+}
\ No newline at end of file
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java
new file mode 100644
index 0000000..7d79918
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementTypeCriterion.java
@@ -0,0 +1,48 @@
+package com.ximple.eofms.filter;
+
+import java.util.ArrayList;
+
+import com.ximple.io.dgn7.Element;
+
+public class ElementTypeCriterion implements Comparable
+{
+    private int elementType;
+    private ArrayList<Integer> elementTypeArray;
+
+    public ElementTypeCriterion()
+    {
+        elementTypeArray = new ArrayList<Integer>();
+    }
+
+    public int compareTo(Object o)
+    {
+        if (o instanceof Element)
+        {
+            Element elm = (Element) o;
+
+            for (Integer etype : getElementTypeArray())
+            {
+                if (elm.getElementType().id == etype.intValue())
+                    return 0;
+            }
+
+        }
+        return -1;
+    }
+
+    public int getElementType()
+    {
+        return elementType;
+    }
+
+    public ArrayList<Integer> getElementTypeArray()
+    {
+        return elementTypeArray;
+    }
+
+    public void setElementType(int itype)
+    {
+        this.elementType = itype;
+        this.elementTypeArray.add(itype);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java
new file mode 100644
index 0000000..3731d86
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java
@@ -0,0 +1,101 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class TypeCompIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private int cid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeCompIdDispatchableFilter()
+    {
+    }
+
+    public TypeCompIdDispatchableFilter(String fname,
+                                        CreateFeatureTypeStrategy createStrategy,
+                                        int tid, int compid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.cid = compid;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public int getCid()
+    {
+        return cid;
+    }
+
+    public void setCid(int cid)
+    {
+        this.cid = cid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+
+    //�P�_�O�_�ũM����
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (cid == featureLinkage.getComponentID()) &&
+                (compareType(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            String ftName = getFeatureTypeName(element);
+            FeatureType ftype = createStrategy.createFeatureElement(ftName);
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (NullPointerException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public String getFeatureTypeName(Element element)
+    {
+        StringBuilder sb= new StringBuilder();
+        sb.append(getName());
+        sb.append("_");
+        sb.append(element.getLevelIndex());
+        sb.append("_");
+        sb.append(element.getWeight());
+        return sb.toString();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java
new file mode 100644
index 0000000..a26c19c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java
@@ -0,0 +1,108 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class TypeCompLevelIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private int cid;
+    private int lid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeCompLevelIdDispatchableFilter()
+    {
+    }
+
+    public TypeCompLevelIdDispatchableFilter(String fname,
+                                             CreateFeatureTypeStrategy createStrategy,
+                                             int tid, int compid, int level)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.cid = compid;
+        this.lid = level;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public int getCid()
+    {
+        return cid;
+    }
+
+    public void setCid(int cid)
+    {
+        this.cid = cid;
+    }
+
+    public int getLid()
+    {
+        return lid;
+    }
+
+    public void setLid(int lid)
+    {
+        this.lid = lid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (cid == featureLinkage.getComponentID()) &&
+                (lid == element.getLevelIndex()) && (compareLevel(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            String ftName = getFeatureTypeName(element);
+            FeatureType ftype = createStrategy.createFeatureElement(ftName);
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public String getFeatureTypeName(Element element)
+    {
+        StringBuilder sb= new StringBuilder();
+        sb.append(getName());
+        sb.append("_");
+        sb.append(element.getLevelIndex());
+        sb.append("_");
+        sb.append(element.getWeight());
+        return sb.toString();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java
new file mode 100644
index 0000000..4a50da9
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java
@@ -0,0 +1,82 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class TypeIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+{
+    private int tid;
+    private CreateFeatureTypeStrategy createStrategy;
+
+    public TypeIdDispatchableFilter()
+    {
+    }
+
+    public TypeIdDispatchableFilter(String fname, CreateFeatureTypeStrategy createStrategy,
+                                    int tid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.createStrategy = createStrategy;
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        this.createStrategy = createStrategy;
+    }
+
+    public boolean isDispatchable(Element element)
+    {
+        FrammeAttributeData featureLinkage = getFeatureLinkage(element);
+        return featureLinkage != null && tid == featureLinkage.getFsc() &&
+                (compareType(element) == 0);
+    }
+
+    public Feature execute(Element element)
+    {
+        try
+        {
+            String ftName = getFeatureTypeName(element);
+            FeatureType ftype = createStrategy.createFeatureElement(ftName);
+            return createStrategy.createFeature(ftype, element);
+        } catch (SchemaException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public String getFeatureTypeName(Element element)
+    {
+        StringBuilder sb= new StringBuilder();
+        sb.append(getName());
+        sb.append("_");
+        sb.append(element.getLevelIndex());
+        sb.append("_");
+        sb.append(element.getWeight());
+        return sb.toString();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractDgnFileJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractDgnFileJobContext.java
new file mode 100644
index 0000000..c7feace
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractDgnFileJobContext.java
@@ -0,0 +1,82 @@
+package com.ximple.eofms.jobs;
+
+import java.util.Properties;
+
+import org.quartz.JobExecutionContext;
+
+import com.ximple.io.dgn7.Dgn7fileReader;
+
+public abstract class AbstractDgnFileJobContext
+{
+    /**
+     * Encoding of URL path.
+     */
+    protected static final String ENCODING = "UTF-8";
+
+    private JobExecutionContext executionContext = null;
+
+    protected String _dataPath = null;
+    protected Properties properties = null;
+
+    private Dgn7fileReader reader = null;
+    private String filename = null;
+    private boolean _elementLogging;
+
+    public AbstractDgnFileJobContext(String dataPath)
+    {
+        _dataPath = dataPath;
+    }
+
+    public String getDataPath()
+    {
+        return _dataPath;
+    }
+
+    public JobExecutionContext getExecutionContext()
+    {
+        return executionContext;
+    }
+
+    public void setExecutionContext(JobExecutionContext context)
+    {
+        executionContext = context;
+    }
+
+    public abstract void startTransaction();
+
+    public abstract void commitTransaction();
+
+    public abstract void rollbackTransaction();
+
+    public abstract String getDataOutPath();
+
+    public Dgn7fileReader getReader()
+    {
+        return this.reader;
+    }
+
+    public void setReader(Dgn7fileReader reader)
+    {
+        this.reader = reader;
+    }
+
+    public String getFilename()
+    {
+        return filename;
+    }
+
+    public void setFilename(String filename)
+    {
+        this.filename = filename;
+    }
+
+    public boolean getElementLogging()
+    {
+        return _elementLogging;
+    }
+
+    public void setElementLogging(boolean elementLogging)
+    {
+        this._elementLogging = elementLogging;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
new file mode 100644
index 0000000..fff760a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
@@ -0,0 +1,305 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import com.vividsolutions.jts.util.Assert;
+
+import oracle.sql.BLOB;
+
+public abstract class AbstractOracleDatabaseJob implements Job
+{
+    private static final String SHPDATA_DIR = "SHPDATA_DIR";
+    private static final String CONFSHPFILTER = "SHPFILTER_CONF";
+    private static final String SPATAILSCHEMA = "ORGSCHEMA";
+    private static final String CONVERTDB = "CONVERTDB";
+    private static final String CONVERTFILE = "CONVERTFILE";
+    private static final String CONVERTELEMIN = "CONVERTELEMIN";
+    private static final String CREATEDUMMY = "CREATEDUMMY";
+    private static final String ELEMLOG = "ELEMLOG";
+    private static final String ORAHOST = "ORAHOST";
+    private static final String ORAINST = "ORAINST";
+    private static final String ORAPORT = "ORAPORT";
+    private static final String ORAUSER = "ORAUSER";
+    private static final String ORAPASS = "ORAPASS";
+    private static final String TESTMODE = "TESTMODE";
+    private static final String TESTCOUNT = "TESTCOUNT";
+
+    protected String _dataPath;
+    protected String _filterPath;
+    protected String _oracleHost;
+    protected String _oracleInstance;
+    protected String _oraclePort;
+    protected String _username;
+    protected String _password;
+    protected String _convertDB;
+    protected String _convertFile;
+    protected String _convertElementIn;
+    protected String _elementLogging;
+    protected String _createDummy;
+    protected ArrayList<String> _orgSchema = new ArrayList<String>();
+    protected boolean _testMode = false;
+    protected int _testCount = -1;
+
+    public abstract void execute(JobExecutionContext context) throws JobExecutionException;
+
+    public Log getLogger() { return null; }
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        // The directory to scan is stored in the job map
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _dataPath = dataMap.getString(SHPDATA_DIR);
+        _filterPath = dataMap.getString(CONFSHPFILTER);
+        _oracleHost = dataMap.getString(ORAHOST);
+        _oracleInstance = dataMap.getString(ORAINST);
+        _oraclePort = dataMap.getString(ORAPORT);
+        _username = dataMap.getString(ORAUSER);
+        _password = dataMap.getString(ORAPASS);
+        _convertDB = dataMap.getString(CONVERTDB);
+        _convertFile = dataMap.getString(CONVERTFILE);
+        _convertElementIn = dataMap.getString(CONVERTELEMIN);
+        _elementLogging = dataMap.getString(ELEMLOG);
+        _createDummy = dataMap.getString(CREATEDUMMY);
+
+        Log logger = getLogger();
+        logger.info("SHPDATA_DIR=" + _dataPath);
+        logger.info("CONFSHPFILTER=" + _filterPath);
+        logger.info("ORAHOST=" + _oracleHost);
+        logger.info("ORAINST=" + _oracleInstance);
+        logger.info("ORAPORT=" + _oraclePort);
+        logger.info("ORAUSER=" + _username);
+        logger.info("ORAPASS=" + _password);
+        logger.info("CONVERTDB=" + _convertDB);
+        logger.info("CONVERTFILE=" + _convertFile);
+        logger.info("CONVERTELEMIN=" + _convertElementIn);
+        logger.info("ELEMLOG=" + _elementLogging);
+
+        String strSchema = dataMap.getString(SPATAILSCHEMA);
+        StringTokenizer st = new StringTokenizer(strSchema, ",");
+        while (st.hasMoreTokens())
+        {
+            String aSchema = st.nextToken().trim();
+            _orgSchema.add(aSchema);
+        }
+        _testMode = dataMap.getBooleanFromString(TESTMODE);
+        _testCount = dataMap.getIntFromString(TESTCOUNT);
+
+        // Validate the required input
+        if (_dataPath == null)
+        {
+            if (logger != null)
+            {
+                logger.warn("Cannot found data directory in configarion.");
+            }
+            throw new JobExecutionException("Directory not configured");
+        }
+
+        // Make sure the directory exists
+        File dir = new File(_dataPath);
+        if (!dir.exists())
+        {
+            logger = getLogger();
+            if (logger != null)
+            {
+                logger.warn("Cannot found data directory in file system.[" + _dataPath + "]");
+            }
+            throw new JobExecutionException("Invalid Dir " + _dataPath);
+        }
+
+        if (_oracleHost == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Host.");
+        }
+        if (_oracleInstance == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Instance.");
+        }
+        if (_username == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Username.");
+        }
+        if (_password == null)
+        {
+            throw new JobExecutionException("Unknown Oracle Password.");
+        }
+        if (_orgSchema == null)
+        {
+            throw new JobExecutionException("Unknown Spatial Database Schema.");
+        }
+    }
+
+    protected abstract AbstractOracleJobContext prepareJobContext(String filterPath);
+
+    protected static byte[] getBytesFromBLOB(BLOB blob) throws SQLException
+    {
+        byte[] raw = null;
+
+        // BLOB        blob        = (BLOB) rs.getBlob(1);
+        int optimalSize = blob.getChunkSize();
+        byte[] chunk = new byte[optimalSize];
+        InputStream is = blob.getBinaryStream(0);
+        ByteBuffer buffer = null;    // ByteBuffer.allocate(optimalSize);
+        int len;
+
+        try
+        {
+            while ((len = (is.read(chunk))) != -1)
+            {
+                if (buffer != null)
+                {
+                    buffer.limit(buffer.limit() + len);
+                } else
+                {
+                    buffer = ByteBuffer.allocate(len);
+                }
+
+                buffer.put(chunk);
+            }
+
+            is.close();
+
+            assert buffer != null;
+            buffer.position(0);
+            raw = buffer.array();
+        } catch (IOException e)
+        {
+            e.printStackTrace();    // To change body of catch statement use File | Settings | File Templates.
+            Assert.shouldNeverReachHere();
+        }
+
+        return raw;
+    }
+
+    public String getDataPath()
+    {
+        return _dataPath;
+    }
+
+    public String getFilterPath()
+    {
+        return _filterPath;
+    }
+
+    public String getOracleHost()
+    {
+        return _oracleHost;
+    }
+
+    public String getOracleInstance()
+    {
+        return _oracleInstance;
+    }
+
+    public String getOraclePort()
+    {
+        return _oraclePort;
+    }
+
+    public String getUsername()
+    {
+        return _username;
+    }
+
+    public String getPassword()
+    {
+        return _password;
+    }
+
+    public ArrayList<String> getOriginSchema()
+    {
+        return _orgSchema;
+    }
+
+    public boolean isTestMode()
+    {
+        return _testMode;
+    }
+
+    public int getTestCount()
+    {
+        return _testCount;
+    }
+
+    public String getConvertDB()
+    {
+        return _convertDB;
+    }
+
+    public void setConvertDB(String convertDB)
+    {
+        _convertDB = convertDB;
+    }
+
+    public String getConvertFile()
+    {
+        return _convertFile;
+    }
+
+    public void setConvertFile(String convertFile)
+    {
+        _convertFile = convertFile;
+    }
+
+    public String getConvertElementIn()
+    {
+        return _convertElementIn;
+    }
+
+    public void setConvertElementIn(String convertElementIn)
+    {
+        _convertElementIn = convertElementIn;
+    }
+
+    public boolean checkConvertDB()
+    {
+        return _convertDB != null && !_convertDB.equalsIgnoreCase("false") &&
+                !_convertDB.equalsIgnoreCase("no") && !_convertDB.equalsIgnoreCase("0");
+    }
+
+    public boolean checkConvertFile()
+    {
+        return _convertFile != null && !_convertFile.equalsIgnoreCase("false") &&
+                !_convertFile.equalsIgnoreCase("no") && !_convertFile.equalsIgnoreCase("0");
+    }
+
+    public boolean checkConvertElementIn()
+    {
+        return _convertElementIn != null && !_convertElementIn.equalsIgnoreCase("false") &&
+                !_convertElementIn.equalsIgnoreCase("no") && !_convertElementIn.equalsIgnoreCase("0");
+    }
+
+    public String getElementLogging()
+    {
+        return _elementLogging;
+    }
+
+    public void setElementLogging(String elementLogging)
+    {
+        this._elementLogging = elementLogging;
+    }
+
+    public boolean checkElementLogging()
+    {
+        return _elementLogging != null && !_elementLogging.equalsIgnoreCase("false") &&
+                !_elementLogging.equalsIgnoreCase("no") && !_elementLogging.equalsIgnoreCase("0");
+    }
+
+    public boolean checkCreateDummy()
+    {
+        return _createDummy != null && !_createDummy.equalsIgnoreCase("false") &&
+                !_createDummy.equalsIgnoreCase("no") && !_createDummy.equalsIgnoreCase("0");
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java
new file mode 100644
index 0000000..f1d867c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleJobContext.java
@@ -0,0 +1,326 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import oracle.jdbc.OracleConnection;
+
+public abstract class AbstractOracleJobContext
+{
+    private static final String ORACLE_URL = "jdbc:oracle:thin:@";
+    private static final String PROPUsrKey = "user";
+    private static final String PROPPassKey = "password";
+    /**
+     * Table Prefiex
+     */
+    protected static final String TABLE_PREFIX = "GEO$";
+    protected static final String UDT_SCHEMA = "SPATIALDB";
+    /**
+     * User Types
+     */
+    protected static final String UDT_RAWS = "CREATE OR REPLACE TYPE \"" + UDT_SCHEMA + "\".\"RAWS\" AS VARRAY (1048576) OF NUMBER(38)";
+    protected static final String UDT_OFMID = "CREATE OR REPLACE TYPE  \"" + UDT_SCHEMA + "\".\"OFMID\" AS OBJECT ("
+            + "\"CLSID\" NUMBER(5), \"OID\" NUMBER(10), \"STATUS\" NUMBER(5), \"COMPID\" NUMBER(3), "
+            + "\"RULEID\" NUMBER(3), \"OCCID\" NUMBER(3))";
+    protected static final String UDT_RAWSNAME = "RAWS";
+    protected static final String UDT_OFMIDNAME = "OFMID";
+    /**
+     * Utility SQL
+     */
+    protected static final String TAB_DROP = "DROP TABLE %s.%s CASCADE CONSTRAINTS";
+    protected static final String TAB_DELETE = "DELETE FROM %s.%s";
+    /**
+     * Table Schema
+     */
+    protected static final String TAB_RANGENODEINDEX_1 = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RPID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWX\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWY\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHX\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHY\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"EXTENTS\" MDSYS.SDO_GEOMETRY,\n"
+            + "    \"RNDESCR\" VARCHAR2(255), \n"
+            + "    PRIMARY KEY ( \"RNID\" ) ENABLE )";
+    protected static final String TAB_RANGENODEINDEX = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RPID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWX\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_LOWY\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHX\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNG_HIGHY\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"RNDESCR\" VARCHAR2(255), \n"
+            + "    PRIMARY KEY ( \"RNID\" ) ENABLE )";
+    protected static final String TAB_RANGENODESTORAGE = "CREATE TABLE \"%s\".\"%s\"\n"
+            + "   (    \"RNID\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"LAYERID\" NUMBER(5,0) NOT NULL ENABLE,\n"
+            + "    \"LASTUPDATE\" DATE NOT NULL ENABLE,\n"
+            + "    \"SPACETABLE\" VARCHAR2(255)\n" + "   )";
+    protected static final String TAB_ELEMENTINDEX_1 = "CREATE TABLE %s.%s (\n"
+            + "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"TYPE\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"ZLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"ZHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n"
+            + "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n"
+            + "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"SPACENAME\" VARCHAR2(255) NOT NULL ENABLE,\n"
+            + "     PRIMARY KEY (\"ELMNO\") ENABLE\n"
+            + "   )";
+    protected static final String TAB_ELEMENTINDEX = "CREATE TABLE %s.%s (\n"
+            + "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "    \"TYPE\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"XLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"YLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"ZLOW\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"XHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"YHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"ZHIGH\" FLOAT NOT NULL ENABLE,\n"
+            + "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n"
+            + "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n"
+            + "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n"
+            + "    \"SPACENAME\" VARCHAR2(255) NOT NULL ENABLE,\n"
+            + "     PRIMARY KEY (\"ELMNO\") ENABLE\n"
+            + "   )";
+    protected static final String TAB_IGDSSEED = "CREATE TABLE  %s.%s (\n"
+            + "   \"ELMNO\" INTEGER NOT NULL ENABLE,\n"
+            + "   \"SEEDELM\" \"" + UDT_SCHEMA
+            + "\".\"RAWS\" NOT NULL ENABLE\n" + "   )";
+    protected static final String TAB_STORAGE_1 = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"EXTENTS\" MDSYS.SDO_GEOMETRY, \n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n" +
+            "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE, \n" +
+            "    \"GEOM\" MDSYS.SDO_GEOMETRY \n" +
+            "   )";
+    protected static final String TAB_STORAGE = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"YLOW\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" FLOAT NOT NULL ENABLE,\n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"TAG_LUFID\" NUMBER(10) NOT NULL ENABLE,\n" +
+            "    \"TAG_SFSC\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_SSTAT\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"TAG_BCOMPID\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_BRULENO\" NUMBER(3) NOT NULL ENABLE,\n" +
+            "    \"TAG_SOCCID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE \n" +
+            "   )";
+    protected static final String TAB_STORAGE2 = "CREATE TABLE %s.%s (\n" +
+            "    \"ELMNO\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"XLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YLOW\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"XHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"YHIGH\" BINARY_DOUBLE NOT NULL ENABLE,\n" +
+            "    \"EXTENTS\" MDSYS.SDO_GEOMETRY, \n" +
+            "    \"PROPS\" INTEGER NOT NULL ENABLE,\n" +
+            "    \"ID\" " + UDT_SCHEMA + ".OFMID NOT NULL ENABLE,\n" +
+            "    \"LAYERID\" NUMBER(5) NOT NULL ENABLE,\n" +
+            "    \"ELEMENT\" \"" + UDT_SCHEMA + "\".\"RAWS\" NOT NULL ENABLE \n" +
+            "   )";
+    /**
+     * Trigger
+     */
+    protected static final String TRG_SPACENODE = "CREATE OR REPLACE TRIGGER \"%s\".\"%s\"\n"
+            + "    AFTER DELETE OR INSERT OR UPDATE ON \"%s\".\"%s\"\n" + "    BEGIN\n"
+            + "        UPDATE SPACENODES SET LASTUPDATE = SYSDATE\n" + "        WHERE SNID = \"%d\";\n"
+            + "    END;";
+    protected static final String TRG_ELMINDEX =
+            "CREATE OR REPLACE TRIGGER \"%s\".\"%s\"\n"
+                    + "    AFTER INSERT OR UPDATE OR DELETE ON \"%s\".\"%s\"\n"
+                    + "    REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW\n"
+                    + "    BEGIN\n"
+                    + "        IF INSERTING THEN\n"
+                    + "            INSERT INTO \"%s\".\"%s\" (ELMNO, TYPE, XLOW, YLOW, XHIGH, YHIGH,\n"
+                    + "                UFID, FSC, COMPID, OCCID, SPACENAME)\n"
+                    + "            VALUES (SD$ELEMENTNUMBER_SEQ.NEXTVAL, :new.ELMTYPE, :new.XLOW, :new.YLOW, :new.XHIGH, :new.YHIGH,\n"
+                    + "                :new.UFID, :new.FSC, :new.COMPID, :new.OCCID, '%s');\n"
+                    + "        ELSIF DELETING THEN\n"
+                    + "            DELETE FROM \"%s\".\"%s\"\n"
+                    + "            WHERE \"%s\".UFID = :old.UFID AND\n"
+                    + "                \"%s\".FSC = :old.FSC AND\n"
+                    + "                \"%s\".COMPID = :old.COMPID AND\n"
+                    + "                \"%s\".OCCID = :old.OCCID;\n"
+                    + "        ELSE\n" + "            UPDATE \"%s\"\n"
+                    + "            SET XLOW = :new.XLOW, YLOW = :new.YLOW, XHIGH = :new.XHIGH, YHIGH = :new.YHIGH\n"
+                    + "            WHERE FSC = :new.FSC AND UFID = :new.UFID AND COMPID = :new.COMPID AND OCCID = :new.OCCID;\n"
+                    + "        END IF;\n" + "    END;";
+
+    /**
+     * copy connectivity to connectivity_webcheck sql
+     */
+    protected static final String TRUNCATE_CONNECTIVITY_WEBCHECK = "TRUNCATE TABLE  BASEDB.CONNECTIVITY_WEBCHECK";
+    protected static final String CREATE_CONNECTIVITY_WEBCHECK = "CREATE TABLE BASEDB.CONNECTIVITY_WEBCHECK\n" +
+            "( FSC      NUMBER(5) NOT NULL," +
+            "  UFID     NUMBER(10) NOT NULL," +
+            "  N1       NUMBER(10)," +
+            "  N2       NUMBER(10)," +
+            "  FDR1     NUMBER(5)," +
+            "  FDR2     NUMBER(5)," +
+            "  DIR      NUMBER(3)," +
+            "  OHUG     NUMBER(3)," +
+            "  OSTATUS  NUMBER(3)," +
+            "  PHASE    NUMBER(3)," +
+            "  X        NUMBER(10,3) NOT NULL," +
+            "  Y        NUMBER(10,3) NOT NULL" +
+            ")\n" +
+            "TABLESPACE BASE_DATA PCTUSED 40 PCTFREE 10 INITRANS 1 MAXTRANS 255\n" +
+            "STORAGE    (INITIAL 19120K MINEXTENTS 1 MAXEXTENTS 2147483645 \n" +
+            "PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT )\n" +
+            "LOGGING NOCOMPRESS NOCACHE NOPARALLEL MONITORING;";
+
+     protected static final String COPY_CONNECTIVITY_TO_WEBCHECK = "INSERT /*+ APPEND */ INTO BASEDB.CONNECTIVITY_WEBCHECK\n" +
+             "(FSC, UFID, N1, N2, FDR1, FDR2, DIR, OHUG,OSTATUS, PHASE, X, Y)\n" +
+             "SELECT FSC, UFID, N1, N2, FDR1, FDR2, DIR, OHUG, OSTATUS, PHASE, X, Y FROM BASEDB.CONNECTIVITY ";
+
+
+    /**
+     *
+     */
+    protected static final String TAB_ELEMENTSET_PREFIX = "ELMSET_";
+    protected static final String STMT_CLEARCYCLEBIN = "PURGE RECYCLEBIN";
+    protected static final String SMTM_GRANTOBJECTTYPE = "GRANT EXECUTE ANY TYPE TO \"" + UDT_SCHEMA + "\"";
+    protected static final long TIMEOUT = Long.MAX_VALUE;
+
+    /**
+     * Encoding of URL path.
+     */
+    protected static final String ENCODING = "UTF-8";
+    private String _oracleHost;
+    private String _oracleInstance;
+    private String _oraclePort;
+    protected String _dataPath;
+    private OracleConnection oracleConnection = null;
+    protected Properties properties;
+    protected boolean _elementLogging;
+
+    public static String getCurrentURL(String oracleHost, String oraclePort, String oracleInstance)
+    {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(ORACLE_URL);
+        builder.append(oracleHost);
+        builder.append(":");
+        builder.append(oraclePort);
+        builder.append(":");
+        builder.append(oracleInstance);
+
+        return builder.toString();
+    }
+
+    public void setLogin(String userName, String password)
+    {
+        properties.put(PROPUsrKey, userName);
+        properties.put(PROPPassKey, password);
+    }
+
+    public void setShapeData(String dataPath)
+    {
+        _dataPath = dataPath;
+    }
+
+    public OracleConnection getOracleConnection()
+    {
+        try
+        {
+            if (oracleConnection == null)
+            {
+                oracleConnection = (OracleConnection) DriverManager.getConnection(
+                        getCurrentURL(_oracleHost, _oraclePort, _oracleInstance),
+                        properties);
+            }
+
+            return oracleConnection;
+        } catch (SQLException e)
+        {
+            OracleConvertJobContext.logger.warn(e.getMessage(), e);
+        }
+
+        oracleConnection = null;
+
+        return null;
+    }
+
+    public void closeConnection()
+    {
+        try
+        {
+            if (oracleConnection != null)
+            {
+                oracleConnection.close();
+                oracleConnection = null;
+            }
+        } catch (SQLException e)
+        {
+            OracleConvertJobContext.logger.warn(e.getMessage(), e);
+        }
+    }
+
+    public void setConnectionInfo(String oracleHost, String oraclePort, String oracleInstance)
+    {
+        _oracleHost = oracleHost;
+        _oracleInstance = oracleInstance;
+        _oraclePort = oraclePort;
+    }
+
+    public String getDataPath()
+    {
+        return _dataPath;
+    }
+
+    public String getOracleHost()
+    {
+        return _oracleHost;
+    }
+
+    public String getOracleInstance()
+    {
+        return _oracleInstance;
+    }
+
+    public String getOraclePort()
+    {
+        return _oraclePort;
+    }
+
+    public boolean getElementLogging()
+    {
+        return _elementLogging;
+    }
+
+    public void setElementLogging(boolean elementLogging)
+    {
+        _elementLogging = elementLogging;
+    }
+
+    public abstract void startTransaction();
+
+    public abstract void commitTransaction();
+
+    public abstract void rollbackTransaction();
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java
new file mode 100644
index 0000000..8c84650
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java
@@ -0,0 +1,297 @@
+package com.ximple.eofms.jobs;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.geotools.feature.Feature;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.SimpleFeature;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.AbstractFLinkageDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeIdDispatchableFilter;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class DummyFeatureConvertJobContext extends AbstractDgnFileJobContext
+{
+    static final Log logger = LogFactory.getLog(DummyFeatureConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    static final String SHPOUTPATH = "shpout";
+
+    private String dataOut = null;
+
+    private HashMap<String, ArrayList<Feature>> featuresContext = new HashMap<String, ArrayList<Feature>>();
+    private HashMap<String, FeatureWriter> featuresWriterContext = new HashMap<String, FeatureWriter>();
+
+    private PessimisticMapWrapper txFeaturesContext;
+
+    private ElementDispatcher elementDispatcher;
+    private String _filterConfig;
+
+    public DummyFeatureConvertJobContext(String dataPath, String filterConfig)
+    {
+        super(dataPath);
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+        _filterConfig = filterConfig;
+        elementDispatcher = createElementDispatcher();
+    }
+
+    private ElementDispatcher createElementDispatcher()
+    {
+        try
+        {
+            URL rulesURL = ElementDispatcher.class.getResource("ElementDispatcherRules.xml");
+            assert rulesURL != null;
+            Digester digester = DigesterLoader.createDigester(rulesURL);
+            URL filterURL = null;
+            if (_filterConfig != null)
+            {
+                File config = new File(_filterConfig);
+                if (config.exists())
+                {
+                    filterURL = config.toURI().toURL();
+                }
+            }
+            if (filterURL == null)
+            {
+                // config = new File("conf/DefaultConvertShpFilter.xml");
+                filterURL = this.getClass().getResource("/conf/DefaultConvertShpFilter.xml");
+                // filterURL = this.getClass().getResource("/conf/ConvertShpFilterForLevel.xml");
+            }
+            assert filterURL != null;
+            return (ElementDispatcher) digester.parse(filterURL);
+        } catch (UnsupportedEncodingException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (MalformedURLException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (SAXException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public void putFeatureCollection(Element element) throws IllegalAttributeException, SchemaException
+    {
+        assert elementDispatcher != null;
+
+        if (element == null)
+        {
+            logger.warn("Unknown Element:" + null);
+            return;
+        }
+
+        // �P�_�O�_�ũM����
+        Feature feature = elementDispatcher.execute(element);
+        if (feature == null)
+        {
+            FrammeAttributeData linkage =
+                    AbstractFLinkageDispatchableFilter.getFeatureLinkage(element);
+            logger.warn("Unknown Element:" + element.getElementType().toString() +
+                    ":type=" + element.getType() + ":lv=" + element.getLevelIndex() + ":id=" +
+                    (linkage == null ? "NULL" : (linkage.getFsc() + "|" + linkage.getComponentID())));
+            if (element instanceof ComplexElement)
+            {
+                ComplexElement complex = (ComplexElement) element;
+                logger.warn("----Complex Element size=" + complex.size());
+            }
+
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        ArrayList arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+    }
+
+    public void startTransaction()
+    {
+        assert elementDispatcher != null;
+        for (ElementDispatchableFilter filter : elementDispatcher.getRules())
+        {
+            if (filter instanceof TypeCompIdDispatchableFilter)
+            {
+                ((TypeCompIdDispatchableFilter) filter).getCreateStrategy();
+            } else if (filter instanceof TypeCompLevelIdDispatchableFilter)
+            {
+                ((TypeCompIdDispatchableFilter) filter).getCreateStrategy();
+            } else if(filter instanceof TypeIdDispatchableFilter)
+            {
+                ((TypeCompIdDispatchableFilter) filter).getCreateStrategy();
+            }
+        }
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+        //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        Iterator it = featuresContext.keySet().iterator();
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(getDataOutPath() + File.separator + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    // ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL());
+                    /*
+                    ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                            true, Charset.forName("UTF-8"));
+                    */
+                    if (!sfile.exists())
+                    {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        writer = shapefileDataStore.getFeatureWriterAppend(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    }
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList<Feature> features = featuresContext.get(featureType);
+                Iterator itFeature = features.iterator();
+                while (itFeature.hasNext())
+                {
+                    Feature feature = (Feature) itFeature.next();
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), SHPOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public void closeFeatureWriter() throws IOException {
+
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/FeatureDgnConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/FeatureDgnConvertJobContext.java
new file mode 100644
index 0000000..5b730ca
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/FeatureDgnConvertJobContext.java
@@ -0,0 +1,280 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.SimpleFeature;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.filter.AbstractFLinkageDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+import com.ximple.io.dgn7.ComplexElement;
+
+public class FeatureDgnConvertJobContext extends AbstractDgnFileJobContext
+{
+    static final Log logger = LogFactory.getLog(FeatureDgnConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    static final String SHPOUTPATH = "shpout";
+
+    private String dataOut = null;
+
+    private HashMap<String, ArrayList<Feature>> featuresContext = new HashMap<String, ArrayList<Feature>>();
+    private HashMap<String, FeatureWriter> featuresWriterContext = new HashMap<String, FeatureWriter>();
+
+    private PessimisticMapWrapper txFeaturesContext;
+
+    private ElementDispatcher elementDispatcher;
+    private String _filterConfig;
+
+    public FeatureDgnConvertJobContext(String dataPath, String filterConfig)
+    {
+        super(dataPath);
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+        _filterConfig = filterConfig;
+        elementDispatcher = createElementDispatcher();
+    }
+
+    private ElementDispatcher createElementDispatcher()
+    {
+        try
+        {
+            URL rulesURL = ElementDispatcher.class.getResource("ElementDispatcherRules.xml");
+            assert rulesURL != null;
+            Digester digester = DigesterLoader.createDigester(rulesURL);
+            URL filterURL = null;
+            if (_filterConfig != null)
+            {
+                File config = new File(_filterConfig);
+                if (config.exists())
+                {
+                    filterURL = config.toURI().toURL();
+                }
+            }
+            if (filterURL == null)
+            {
+                // config = new File("conf/DefaultConvertShpFilter.xml");
+                filterURL = this.getClass().getResource("/conf/DefaultConvertShpFilter.xml");
+                // filterURL = this.getClass().getResource("/conf/ConvertShpFilterForLevel.xml");
+            }
+            assert filterURL != null;
+            return (ElementDispatcher) digester.parse(filterURL);
+        } catch (UnsupportedEncodingException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (MalformedURLException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (SAXException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public void putFeatureCollection(Element element) throws IllegalAttributeException, SchemaException
+    {
+        assert elementDispatcher != null;
+
+        if (element == null)
+        {
+            logger.warn("Unknown Element:" + null);
+            return;
+        }
+
+        // �P�_�O�_�ũM����
+        Feature feature = elementDispatcher.execute(element);
+        if (feature == null)
+        {
+            FrammeAttributeData linkage =
+                    AbstractFLinkageDispatchableFilter.getFeatureLinkage(element);
+            logger.warn("Unknown Element:" + element.getElementType().toString() +
+                    ":type=" + element.getType() + ":lv=" + element.getLevelIndex() + ":id=" +
+                    (linkage == null ? "NULL" : (linkage.getFsc() + "|" + linkage.getComponentID())));
+            if (element instanceof ComplexElement)
+            {
+                ComplexElement complex = (ComplexElement) element;
+                logger.warn("----Complex Element size=" + complex.size());
+            }
+
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        ArrayList arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+    }
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+        //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        Iterator it = featuresContext.keySet().iterator();
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(getDataOutPath() + File.separator + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    // ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL());
+                    /*
+                    ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                            true, Charset.forName("UTF-8"));
+                    */
+                    if (!sfile.exists())
+                    {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        writer = shapefileDataStore.getFeatureWriterAppend(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    }
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList<Feature> features = featuresContext.get(featureType);
+                Iterator itFeature = features.iterator();
+                while (itFeature.hasNext())
+                {
+                    Feature feature = (Feature) itFeature.next();
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), SHPOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public void closeFeatureWriter() throws IOException {
+
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/GeneralDgnConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/GeneralDgnConvertJobContext.java
new file mode 100644
index 0000000..26fd457
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/GeneralDgnConvertJobContext.java
@@ -0,0 +1,554 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.MalformedURLException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.SimpleFeature;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.ArcElement;
+import com.ximple.io.dgn7.ComplexChainElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.EllipseElement;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.LineElement;
+import com.ximple.io.dgn7.LineStringElement;
+import com.ximple.io.dgn7.ShapeElement;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.TextNodeElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class GeneralDgnConvertJobContext extends AbstractDgnFileJobContext
+{
+    static final Log logger = LogFactory.getLog(GeneralDgnConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    static final String SHPOUTPATH = "shpout";
+
+    private String dataOut = null;
+
+    private HashMap<String, ArrayList<Feature>> featuresContext = new HashMap<String, ArrayList<Feature>>();
+    private HashMap<String, FeatureWriter> featuresWriterContext = new HashMap<String, FeatureWriter>();
+
+    private PessimisticMapWrapper txFeaturesContext;
+    private TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
+    private TreeMap<String, FeatureType> featureTypes = new TreeMap<String, FeatureType>();
+
+    private TWD97GeometryConverterDecorator convertDecorator = null;
+    private String featureBaseName = null;
+
+    public GeneralDgnConvertJobContext(String dataPath)
+    {
+        super(dataPath);
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+        convertDecorator = new TWD97GeometryConverterDecorator();
+    }
+
+    public void putFeatureCollection(Element element) throws IllegalAttributeException, SchemaException
+    {
+        FeatureType ft = lookupFeatureType(element);
+        if (ft != null)
+        {
+            Feature feature = createFeature(ft, element);
+            if (feature == null)
+            {
+                if (element instanceof TextElement)
+                    logger.info("cannot craete feature." + element.toString() + "'" +
+                            ((TextElement) element).getText() + "'");
+                else if (element instanceof ShapeElement)
+                    logger.info("cannot craete feature." + element.toString() + "'" +
+                            ((ShapeElement) element).getVerticeSize() + "'" +
+                            ((ShapeElement) element).getStartPoint());
+                else if (element instanceof LineStringElement)
+                    logger.info("cannot craete feature." + element.toString() + "'" +
+                            ((LineStringElement) element).getVerticeSize() + "'" +
+                            ((LineStringElement) element).getStartPoint());
+                else if (element instanceof ArcElement)
+                    logger.info("cannot craete feature." + element.toString() + "'" +
+                            ((ArcElement) element).getOrigin().toString() + "'" +
+                            ((ArcElement) element).getRotationAngle());
+
+                return;
+            }
+
+            if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+            {
+                txFeaturesContext.put(feature.getFeatureType(), new ArrayList<Feature>());
+            }
+            ArrayList<Feature> arrayList = (ArrayList<Feature>) txFeaturesContext.get(feature.getFeatureType());
+            arrayList.add(feature);
+        } else
+        {
+            logger.info("Unknown Element :" + element.getType() + ", lv=" + element.getLevelIndex());
+        }
+    }
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+        //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        Iterator it = featuresContext.keySet().iterator();
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(getDataOutPath() + File.separator + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    // ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL());
+                    /*
+                    ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                            true, Charset.forName("UTF-8"));
+                    */
+                    if (!sfile.exists())
+                    {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        writer = shapefileDataStore.getFeatureWriterAppend(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    }
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList<Feature> features = featuresContext.get(featureType);
+                Iterator itFeature = features.iterator();
+                while (itFeature.hasNext())
+                {
+                    Feature feature = (Feature) itFeature.next();
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), SHPOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public void closeFeatureWriter() throws IOException
+    {
+
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+    }
+
+    public FeatureType createPointFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("FONT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("JUST", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("HEIGHT", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("WIDTH", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("ANGLE", Double.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("CONTEXT", String.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createArcFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createEllipseFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilder.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        if (element instanceof TextElement)
+        {
+            TextElement textElement = (TextElement) element;
+            convertDecorator.setConverter(textElement);
+
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            double angle = textElement.getRotationAngle();
+            String content = textElement.getText();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            if (geom != null)
+            {
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(textElement.getColorIndex()),
+                        textElement.getFontIndex(),
+                        textElement.getJustification(),
+                        textElement.getTextHeight(),
+                        textElement.getTextWidth(),
+                        angle,
+                        content
+                });
+            } else
+            {
+                logger.info("geometry is null." + element.toString());
+            }
+            return null;
+        } else if (element instanceof TextNodeElement)
+        {
+            TextNodeElement textNodeElement = (TextNodeElement) element;
+            convertDecorator.setConverter(textNodeElement);
+
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            double angle = textNodeElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            String[] texts = textNodeElement.getTextArray();
+            StringBuffer sb = new StringBuffer();
+            for (String text : texts)
+            {
+                if (sb.length() != 0)
+                    sb.append("\n");
+                sb.append(text);
+            }
+
+            if (geom != null)
+            {
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(textNodeElement.getColorIndex()),
+                        textNodeElement.getFontIndex(),
+                        textNodeElement.getJustification(),
+                        textNodeElement.getTextNodeHeight(),
+                        textNodeElement.getTextNodeLength(),
+                        angle,
+                        sb.toString()
+                });
+            } else
+            {
+                logger.info("geometry is null." + element.toString());
+            }
+            return null;
+        } else if (element instanceof ShapeElement)
+        {
+            ShapeElement shapeElement = (ShapeElement) element;
+            convertDecorator.setConverter(shapeElement);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+            {
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(shapeElement.getColorIndex()),
+                        shapeElement.getWeight(),
+                        shapeElement.getLineStyle()
+                });
+            } else
+            {
+                logger.info("geometry is null." + element.toString());
+            }
+            return null;
+        } else if (element instanceof LineStringElement)
+        {
+            LineStringElement linestring = (LineStringElement) element;
+            convertDecorator.setConverter(linestring);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(linestring.getColorIndex()),
+                        linestring.getWeight(),
+                        linestring.getLineStyle()
+                });
+            return null;
+        } else if (element instanceof LineElement)
+        {
+            LineElement line = (LineElement) element;
+            convertDecorator.setConverter(line);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(line.getColorIndex()),
+                        line.getWeight(),
+                        line.getLineStyle()
+                });
+            return null;
+        } else if (element instanceof ArcElement)
+        {
+            ArcElement arcElement = (ArcElement) element;
+            /*
+            logger.fatal("" + arcElement.getPrimary() + ":" + arcElement.getSecondary() +
+                    "-" + arcElement.getStartAngle() + ":" + arcElement.getSweepAngle() + ":" +
+            arcElement.getRotationAngle() + ":" + arcElement.getOrigin());
+            */
+            convertDecorator.setConverter(arcElement);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(arcElement.getColorIndex()),
+                        arcElement.getWeight(),
+                        arcElement.getLineStyle()
+                });
+            return null;
+        } else if (element instanceof EllipseElement)
+        {
+            EllipseElement arcElement = (EllipseElement) element;
+            convertDecorator.setConverter(arcElement);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(arcElement.getColorIndex()),
+                        arcElement.getWeight(),
+                        arcElement.getLineStyle()
+                });
+            return null;
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChainElement = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChainElement);
+            Geometry geom = convertDecorator.toGeometry(geometryFactory);
+            if (geom != null)
+                return featureType.create(new Object[]{
+                        geom,
+                        colorTable.getColorCode(complexChainElement.getColorIndex()),
+                        complexChainElement.getWeight(),
+                        complexChainElement.getLineStyle()
+                });
+            return null;
+        }
+        return null;
+    }
+
+    private String getFeatureBaseName()
+    {
+        if (featureBaseName == null)
+        {
+            String dgnname = getFilename().toLowerCase();
+            int i = dgnname.lastIndexOf(".");
+            if (i != -1)
+            {
+                dgnname = dgnname.substring(0, i);
+            }
+            featureBaseName = dgnname;
+        }
+        return featureBaseName;
+    }
+
+    private FeatureType lookupFeatureType(Element element) throws SchemaException, IllegalAttributeException
+    {
+        String typeName;
+        if (element instanceof TextElement)
+        {
+            typeName = getFeatureBaseName() + "P";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createPointFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof TextNodeElement)
+        {
+            typeName = getFeatureBaseName() + "P";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createPointFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof LineStringElement)
+        {
+            if (element instanceof ShapeElement)
+            {
+                typeName = getFeatureBaseName() + "R";
+                if (!featureTypes.containsKey(typeName))
+                {
+                    featureTypes.put(typeName, createLineFeatureElement(typeName));
+                }
+                return featureTypes.get(typeName);
+            } else
+            {
+                typeName = getFeatureBaseName() + "L";
+                if (!featureTypes.containsKey(typeName))
+                {
+                    featureTypes.put(typeName, createLineFeatureElement(typeName));
+                }
+                return featureTypes.get(typeName);
+            }
+        } else if (element instanceof LineElement)
+        {
+            typeName = getFeatureBaseName() + "L";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createLineFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof ComplexChainElement)
+        {
+            typeName = getFeatureBaseName() + "L";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createLineFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof ArcElement)
+        {
+            typeName = getFeatureBaseName() + "A";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createArcFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof EllipseElement)
+        {
+            typeName = getFeatureBaseName() + "R";
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createEllipseFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        }
+
+        return null;
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/IndexDgnConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/IndexDgnConvertJobContext.java
new file mode 100644
index 0000000..ae3d42f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/IndexDgnConvertJobContext.java
@@ -0,0 +1,355 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.MalformedURLException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.SimpleFeature;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.TPCLIDConverter;
+import com.ximple.eofms.util.TWDDatumConverter;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class IndexDgnConvertJobContext extends AbstractDgnFileJobContext
+{
+    static final Log logger = LogFactory.getLog(IndexDgnConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+    static final String SHPOUTPATH = "shpout";
+
+    private String dataOut = null;
+
+    private HashMap<String, ArrayList<Feature>> featuresContext = new HashMap<String, ArrayList<Feature>>();
+    private HashMap<String, FeatureWriter> featuresWriterContext = new HashMap<String, FeatureWriter>();
+
+    private PessimisticMapWrapper txFeaturesContext;
+    private FeatureTypeBuilder typeBuilderPnt = null;
+    private FeatureTypeBuilder typeBuilderRect = null;
+    private FeatureType featureType = null;
+    private FeatureType featureType2 = null;
+
+    public IndexDgnConvertJobContext(String dataPath)
+    {
+        super(dataPath);
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+    }
+
+    public void putFeatureCollection(Element element) throws IllegalAttributeException, SchemaException
+    {
+        if (!(element instanceof TextElement))
+        {
+            return;
+        }
+
+        Feature feature = createFeature((TextElement) element);
+        if (feature == null)
+        {
+            logger.info("cannot craete feature." + element.toString() + "'" +
+                    ((TextElement) element).getText() + "'");
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        ArrayList arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+
+        feature = createFeature2((TextElement) element);
+        if (feature == null)
+        {
+            logger.info("cannot craete feature2." + element.toString() + "'" +
+                    ((TextElement) element).getText() + "'");
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+    }
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+        //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        Iterator it = featuresContext.keySet().iterator();
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(getDataOutPath() + File.separator + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    /*
+                    ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                            true, Charset.forName("UTF-8"));
+                    */
+                    ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                            null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                    shapefileDataStore.createSchema(featureType);
+                    writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList<Feature> features = featuresContext.get(featureType);
+                for (Feature feature1 : features)
+                {
+                    ((SimpleFeature) writer.next()).setAttributes(feature1.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), SHPOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public void closeFeatureWriter() throws IOException
+    {
+
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (typeBuilderRect == null)
+        {
+            typeBuilderRect = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("X1", Double.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("Y1", Double.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("X2", Double.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("Y2", Double.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("TPCID", String.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilderRect.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+        }
+        return typeBuilderRect.getFeatureType();
+    }
+
+    public FeatureType createFeatureElement2(String featureName) throws SchemaException
+    {
+        if (typeBuilderPnt == null)
+        {
+            typeBuilderPnt = FeatureTypeBuilder.newInstance(featureName);
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("GEOM", Geometry.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("SYMCOLOR", String.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("SYMWEIGHT", Integer.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("SYMSTYLE", Integer.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("JUST", Integer.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("HEIGHT", Double.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("WIDTH", Double.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("ANGLE", Double.class));
+            typeBuilderPnt.addType(AttributeTypeFactory.newAttributeType("TPCID", String.class));
+        }
+        return typeBuilderPnt.getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        if (element instanceof TextElement)
+        {
+            TextElement textElement = (TextElement) element;
+            String tpclid = textElement.getText();
+
+            Envelope extent = TPCLIDConverter.convertTpclIdToEnvelope(tpclid);
+            Geometry geom = geometryFactory.createLinearRing(new Coordinate[]
+                    {
+                            TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMinY())),
+                            TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMaxX(), extent.getMinY())),
+                            TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMaxX(), extent.getMaxY())),
+                            TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMaxY())),
+                            TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMinY())),
+                    });
+
+            return featureType.create(new Object[]{
+                    geom,
+                    extent.getMinX(),
+                    extent.getMinY(),
+                    extent.getMaxX(),
+                    extent.getMaxY(),
+                    tpclid,
+                    colorTable.getColorCode(textElement.getColorIndex()),
+                    textElement.getWeight(),
+                    textElement.getLineStyle()
+            });
+        }
+        return null;
+    }
+
+    public Feature createFeature2(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        if (element instanceof TextElement)
+        {
+            TextElement txtElement = (TextElement) element;
+            double angle = txtElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            convertDecorator.setConverter(txtElement);
+            Feature feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    txtElement.getWeight(),
+                    txtElement.getLineStyle(),
+                    txtElement.getJustification(),
+                    txtElement.getTextHeight(),
+                    txtElement.getTextWidth(),
+                    angle,
+                    txtElement.getText()
+            });
+            return feature;
+        }
+        return null;
+    }
+
+    private Feature createFeature(TextElement element) throws SchemaException, IllegalAttributeException
+    {
+        if (featureType == null)
+        {
+            String dgnname = getFilename().toLowerCase();
+            int i = dgnname.lastIndexOf(".");
+            if (i != -1)
+            {
+                dgnname = dgnname.substring(0, i);
+            }
+            featureType = createFeatureElement(dgnname);
+        }
+        return createFeature(featureType, element);
+    }
+
+    private Feature createFeature2(TextElement element) throws SchemaException, IllegalAttributeException
+    {
+        if (featureType2 == null)
+        {
+            String dgnname = getFilename().toLowerCase();
+            int i = dgnname.lastIndexOf(".");
+            if (i != -1)
+            {
+                dgnname = dgnname.substring(0, i);
+            }
+            dgnname = dgnname + "P";
+            featureType2 = createFeatureElement2(dgnname);
+        }
+        return createFeature2(featureType2, element);
+    }
+
+    protected FrammeAttributeData getFeatureLinkage(Element element)
+    {
+        if (!element.hasUserAttributeData())
+            return null;
+
+        List<UserAttributeData> usrDatas = element.getUserAttributeData();
+        for (UserAttributeData anUsrData : usrDatas)
+        {
+            if (anUsrData instanceof FrammeAttributeData)
+            {
+                return (FrammeAttributeData) anUsrData;
+            }
+        }
+        return null;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
new file mode 100644
index 0000000..95f104c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
@@ -0,0 +1,917 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Date;
+
+import org.apache.commons.collections.OrderedMap;
+import org.apache.commons.collections.OrderedMapIterator;
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OracleResultSet;
+import oracle.sql.ARRAY;
+import oracle.sql.BLOB;
+
+import com.ximple.eofms.util.BinConverter;
+import com.ximple.eofms.util.ByteArrayCompressor;
+import com.ximple.eofms.util.StringUtils;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Dgn7fileException;
+import com.ximple.io.dgn7.Dgn7fileReader;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.ElementType;
+import com.ximple.io.dgn7.IElementHandler;
+import com.ximple.io.dgn7.Lock;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.util.PrintfFormat;
+
+/**
+ *
+ */
+public class OracleConvertDgn2ShpJob extends AbstractOracleDatabaseJob
+{
+    final static Log logger = LogFactory.getLog(OracleConvertDgn2ShpJob.class);
+
+    private static final int FETCHSIZE = 30;
+    private static final int BATCHSIZE = 25;
+    private static final int COMMITSIZE = 20;
+
+    class Pair
+    {
+        Object first;
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    GeometryFactory _geomFactory = new GeometryFactory();
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleConvertJobContext(filterPath);
+    }
+
+    public void execute(JobExecutionContext context) throws JobExecutionException
+    {
+        // Every job has its own job detail
+        JobDetail jobDetail = context.getJobDetail();
+
+        // The name is defined in the job definition
+        String jobName = jobDetail.getName();
+
+        // Log the time the job started
+        logger.info(jobName + " fired at " + new Date());
+        extractJobConfiguration(jobDetail);
+
+        try
+        {
+            logger.info("-- step:clearOutputDirectory --");
+            clearOutputDirectory();
+            boolean bFirst = true;
+            if (checkConvertDB())
+            {
+                logger.info("-- step:convertOracleDB --");
+
+                for (String orgSchema : _orgSchema)
+                {
+                    OracleConvertJobContext jobContext = (OracleConvertJobContext) prepareJobContext(_filterPath);
+                    jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+                    jobContext.setLogin(_username, _password);
+                    jobContext.setShapeData(_dataPath);
+                    jobContext.setConvertDB(_convertDB);
+                    jobContext.setConvertFile(_convertFile);
+                    jobContext.setConvertElementIn(_convertElementIn);
+                    jobContext.setElementLogging(checkElementLogging());
+                    jobContext.setExecutionContext(context);
+
+                    if (bFirst)
+                        copyConnectivity(jobContext);
+                    else
+                        bFirst = false;
+
+                    logger.info("----- start schema:" + orgSchema + " -----");
+                    exetcuteConvert(jobContext, orgSchema, _dataPath);
+
+                    //close all open filewriter instance
+                    jobContext.closeFeatureWriter();
+                }
+            }
+
+            if (checkConvertFile())
+            {
+                logger.info("-- step:convertIndexDesignFile --");
+                convertIndexDesignFile(context);
+                logger.info("-- step:convertOtherDesignFile --");
+                convertOtherDesignFile(context);
+            }
+
+            if (checkConvertElementIn())
+            {
+                logger.info("-- step:convertFeatureDesignFile --");
+                convertFeatureDesignFile(context);
+            }
+
+            if (checkCreateDummy())
+            {
+                logger.info("-- step:createDummyFeatureFile --");
+                createDummyFeatureFile(context);
+            }
+        } catch (SQLException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException("Database error. " + e.getMessage(), e);
+        } catch (IOException ex)
+        {
+            logger.warn(ex.getMessage(), ex);
+            throw new JobExecutionException("IO error. " + ex.getMessage(), ex);
+        }
+        logger.info(jobName + " end at " + new Date());
+    }
+
+    /**
+     * Connectivity�ƻs�@�Ӫ����A�b�d�߹q�y��V�ɥΨӤ��OMS��Ʈw���q���s����(Connectivity)
+     *
+     * @param jobContext job context
+     * @throws SQLException sql exception
+     */
+    private void copyConnectivity(OracleConvertJobContext jobContext) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        Statement stmt = connection.createStatement();
+        stmt.execute(OracleConvertJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+        stmt.execute(OracleConvertJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+    }
+
+    private void exetcuteConvert(OracleConvertJobContext jobContext,
+                                 String querySchema, String dataPath) throws SQLException
+    {
+        int order = 0;
+        OrderedMap map = getBlobStorageList(jobContext.getOracleConnection(), querySchema, "SD$SPACENODES"
+                , null);
+
+        logger.info("begin convert job:[" + map.size() + "]:testmode=" + _testMode);
+
+        int total = map.size(); //spacenodes count
+        int step = total / 100;
+        int current = 0;
+
+        //jobContext.startTransaction();
+        jobContext.setCurrentSchema(querySchema);
+        jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", 0);
+        for (OrderedMapIterator it = map.orderedMapIterator(); it.hasNext();)
+        {
+            it.next();
+
+            Pair pair = (Pair) it.getValue();
+            String tableSrc = (String) pair.first;
+
+            logger.info("begin convert:[" + order + "]-" + tableSrc);
+            queryIgsetElement(jobContext, querySchema, tableSrc);
+
+            order++;
+
+            if (_testMode)
+            {
+                if ((_testCount < 0) || (order >= _testCount))
+                    break;
+            }
+
+            if ((order % COMMITSIZE) == 0)
+            {
+                // OracleConnection connection = jobContext.getOracleConnection();
+                // connection.commitTransaction();
+                jobContext.commitTransaction();
+                //jobContext.startTransaction();
+                System.gc();
+            }
+
+            int now = order % step;
+            if (now != current)
+            {
+                current = now;
+                jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", current);
+
+            }
+        }
+        jobContext.getExecutionContext().put("ConvertDgn2ShpJobProgress", 100);
+
+        jobContext.commitTransaction();
+
+        logger.info("end convert job:[" + order + "]");
+        System.gc();
+    }
+
+    protected OrderedMap getBlobStorageList(OracleConnection connection, String schemaSrc, String tableSrc,
+                                            OrderedMap orderedMap) throws SQLException
+    {
+        if (orderedMap == null)
+            orderedMap = new LinkedMap(99);
+        String fetchStmtFmt = "SELECT SNID, SPACETABLE FROM \"%s\".\"%s\"";
+        PrintfFormat spf = new PrintfFormat(fetchStmtFmt);
+        String fetchStmt = spf.sprintf(new Object[]{schemaSrc, tableSrc});
+        Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+        ResultSet rs = null;
+
+        stmt.setFetchSize(FETCHSIZE);
+
+        try
+        {
+            rs = stmt.executeQuery(fetchStmt);
+
+            while (rs.next())
+            {
+                int size = rs.getMetaData().getColumnCount();
+                Object[] values = new Object[size];
+
+                for (int i = 0; i < size; i++)
+                {
+                    values[i] = rs.getObject(i + 1);
+                }
+
+                Integer key = ((BigDecimal) values[0]).intValue();
+                String name = (String) values[1];
+
+                Pair pair = (Pair) orderedMap.get(key);
+                if (pair == null)
+                    orderedMap.put(key, new Pair(name, null));
+                else
+                    pair.first = name;
+            }
+        } catch (SQLException e)
+        {
+            logger.error(e.toString(), e);
+            logger.error("stmt=" + fetchStmt);
+            throw e;
+        } finally
+        {
+            if (rs != null) rs.close();
+            stmt.close();
+        }
+
+        return orderedMap;
+    }
+
+    protected OrderedMap getRawFormatStorageList(OracleConnection connection, String schemaSrc, String tableSrc,
+                                                 OrderedMap orderedMap) throws SQLException
+    {
+        if (orderedMap == null)
+            orderedMap = new LinkedMap(99);
+        String fetchStmtFmt = "SELECT RNID, SPACETABLE FROM \"%s\".\"%s\"";
+        PrintfFormat spf = new PrintfFormat(fetchStmtFmt);
+        String fetchStmt = spf.sprintf(new Object[]{schemaSrc, tableSrc});
+        Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmt.setFetchSize(FETCHSIZE);
+
+        ResultSet rs = stmt.executeQuery(fetchStmt);
+
+        while (rs.next())
+        {
+            int size = rs.getMetaData().getColumnCount();
+            Object[] values = new Object[size];
+
+            for (int i = 0; i < size; i++)
+            {
+                values[i] = rs.getObject(i + 1);
+            }
+
+            Integer key = ((BigDecimal) values[0]).intValue();
+            String name = (String) values[1];
+
+            Pair pair = (Pair) orderedMap.get(key);
+            if (pair == null)
+                orderedMap.put(key, new Pair(null, name));
+            else
+                pair.second = name;
+        }
+
+        rs.close();
+        stmt.close();
+
+        return orderedMap;
+    }
+
+    protected void queryIgsetElement(OracleConvertJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchSrcStmtFmt);
+        String fetchSrcStmt = spf.sprintf(new Object[]{srcschema, srctable});
+        Statement stmtSrc = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmtSrc.setFetchSize(FETCHSIZE);
+
+        ResultSet rsSrc = stmtSrc.executeQuery(fetchSrcStmt);
+
+        while (rsSrc.next())
+        {
+            byte[] raw = null;
+
+            if (rsSrc.getMetaData().getColumnType(1) == Types.BLOB)
+            {
+                BLOB blob = (BLOB) rsSrc.getBlob(1);
+
+                raw = getBytesFromBLOB(blob);
+                blob.close();
+            } else
+            {
+                raw = rsSrc.getBytes(1);
+            }
+
+            try
+            {
+                Element element = fetchBinaryElement(raw);
+                jobContext.putFeatureCollection(element);
+            } catch (Dgn7fileException e)
+            {
+                logger.warn("Dgn7Exception", e);
+            }
+        }
+
+        rsSrc.close();
+        stmtSrc.close();
+    }
+
+    protected void queryRawElement(OracleConvertJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        String fetchDestStmtFmt = "SELECT ELEMENT FROM \"%s\".\"%s\" ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchDestStmtFmt);
+        String fetchDestStmt = spf.sprintf(new Object[]{srcschema, srctable});
+        Statement stmtDest = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+        stmtDest.setFetchSize(FETCHSIZE);
+
+        ResultSet rsDest = stmtDest.executeQuery(fetchDestStmt);
+
+        while (rsDest.next())
+        {
+            ARRAY rawsValue = ((OracleResultSet) rsDest).getARRAY(1);
+            long[] rawData = rawsValue.getLongArray();
+            byte[] comparessedValue;
+
+            /*
+            if (dataMode == TransferTask.DataMode.Normal)
+            {
+                comparessedValue = BinConverter.unmarshalByteArray(rawData, true);
+            } else
+            {
+                comparessedValue = BinConverter.unmarshalCompactByteArray(rawData);
+            }
+            */
+            comparessedValue = BinConverter.unmarshalByteArray(rawData, true);
+
+            byte[] rawDest = ByteArrayCompressor.decompressByteArray(comparessedValue);
+
+
+            try
+            {
+                Element element = fetchBinaryElement(rawDest);
+                jobContext.putFeatureCollection(element);
+            } catch (Dgn7fileException e)
+            {
+                logger.warn("Dgn7Exception:" + e.getMessage(), e);
+            }
+        }
+
+        rsDest.close();
+        stmtDest.close();
+    }
+
+    // Binary to Element
+    private Element fetchBinaryElement(byte[] raws) throws Dgn7fileException
+    {
+        ByteBuffer buffer = ByteBuffer.wrap(raws);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+        short signature = buffer.getShort();
+
+        // byte type = (byte) (buffer.get() & 0x7f);
+        byte type = (byte) ((signature >>> 8) & 0x007f);
+
+        // silly Bentley say contentLength is in 2-byte words
+        // and ByteByffer uses raws.
+        // track the record location
+        int elementLength = (buffer.getShort() * 2) + 4;
+        ElementType recordType = ElementType.forID(type);
+        IElementHandler handler;
+
+        handler = recordType.getElementHandler();
+
+        Element dgnElement = (Element) handler.read(buffer, signature, elementLength);
+        if (recordType.isComplexElement() && (elementLength < raws.length))
+        {
+            int offset = elementLength;
+            while (offset < (raws.length - 4))
+            {
+                buffer.position(offset);
+                signature = buffer.getShort();
+                type = (byte) ((signature >>> 8) & 0x007f);
+                elementLength = (buffer.getShort() * 2) + 4;
+                if (raws.length < (offset + elementLength))
+                {
+                    System.out.println("Length not match:" + offset + ":" + buffer.position() + ":" + buffer.limit());
+                    break;
+                }
+                recordType = ElementType.forID(type);
+                handler = recordType.getElementHandler();
+                if (handler != null)
+                {
+                    Element subElement = (Element) handler.read(buffer, signature, elementLength);
+                    ((ComplexElement) dgnElement).add(subElement);
+                    offset += elementLength;
+                } else
+                {
+                    byte[] remain = new byte[buffer.remaining()];
+                    System.arraycopy(raws, offset, remain, 0, buffer.remaining());
+                    for (int i = 0; i < remain.length; i++)
+                    {
+                        if (remain[i] != 0)
+                        {
+                            logger.info("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                            System.out.println("fetch element has some error. index=" + (offset + i) + ":value=" + remain[i]);
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        return dgnElement;
+    }
+
+    /**
+     * �����ഫ���޹��ɪ��u�@
+     *
+     * @param context �u�@��������
+     * @throws org.quartz.JobExecutionException exception
+     */
+    private void convertIndexDesignFile(JobExecutionContext context) throws JobExecutionException
+    {
+        File indexDir = new File(getDataPath(), "index");
+        if (!indexDir.exists())
+        {
+            logger.info("index dir=" + indexDir + " not exist.");
+            return;
+        }
+
+        if (!indexDir.isDirectory())
+        {
+            logger.info("index dir=" + indexDir + " is not a directory.");
+        }
+
+        File[] dgnFiles = indexDir.listFiles(new FilenameFilter()
+        {
+            public boolean accept(File dir, String name)
+            {
+                return name.toLowerCase().endsWith(".dgn");
+            }
+        });
+
+        for (File dgnFile : dgnFiles)
+        {
+            IndexDgnConvertJobContext convertContext = new IndexDgnConvertJobContext(getDataPath());
+            logger.debug("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+
+                FileInputStream fs = new FileInputStream(dgnFile);
+                FileChannel fc = fs.getChannel();
+                Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+                convertContext.setReader(reader);
+
+                scanIndexDgnElement(convertContext);
+
+                convertContext.commitTransaction();
+                convertContext.closeFeatureWriter();
+                System.gc();
+            } catch (FileNotFoundException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (Dgn7fileException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IOException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IllegalAttributeException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (SchemaException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            }
+        }
+    }
+
+    protected void scanIndexDgnElement(IndexDgnConvertJobContext convertContext)
+            throws Dgn7fileException, IOException, IllegalAttributeException, SchemaException
+    {
+        Dgn7fileReader reader = convertContext.getReader();
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    lastComplex = null;
+
+                    processIndexElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        processIndexElement(element, convertContext);
+                        lastComplex = element;
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processIndexElement(Element element, IndexDgnConvertJobContext convertContext) throws IllegalAttributeException, SchemaException
+    {
+        if (element instanceof TextElement)
+        {
+            convertContext.putFeatureCollection(element);
+        }
+    }
+
+
+    /**
+     * �����ഫ��L�]�p���ɪ��u�@
+     *
+     * @param context jobContext
+     * @throws org.quartz.JobExecutionException exception
+     */
+    private void convertOtherDesignFile(JobExecutionContext context) throws JobExecutionException
+    {
+        File otherDir = new File(getDataPath(), "other");
+        if (!otherDir.exists())
+        {
+            logger.info("other dir=" + otherDir + " not exist.");
+            return;
+        }
+
+        if (!otherDir.isDirectory())
+        {
+            logger.info("other dir=" + otherDir + " is not a directory.");
+        }
+
+        File[] dgnFiles = otherDir.listFiles(new FilenameFilter()
+        {
+            public boolean accept(File dir, String name)
+            {
+                return name.toLowerCase().endsWith(".dgn");
+            }
+        });
+
+        for (File dgnFile : dgnFiles)
+        {
+            GeneralDgnConvertJobContext convertContext = new GeneralDgnConvertJobContext(getDataPath());
+            logger.info("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+
+                FileInputStream fs = new FileInputStream(dgnFile);
+                FileChannel fc = fs.getChannel();
+                Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+                convertContext.setReader(reader);
+
+                scanOtherDgnElement(convertContext);
+
+                convertContext.commitTransaction();
+                convertContext.closeFeatureWriter();
+                System.gc();
+            } catch (FileNotFoundException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (Dgn7fileException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IOException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IllegalAttributeException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (SchemaException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            }
+        }
+    }
+
+    public void scanOtherDgnElement(GeneralDgnConvertJobContext convertContext)
+            throws Dgn7fileException, IOException, IllegalAttributeException, SchemaException
+    {
+        Dgn7fileReader reader = convertContext.getReader();
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    lastComplex = null;
+
+                    processOtherElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        processOtherElement(element, convertContext);
+                        lastComplex = element;
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processOtherElement(Element element, GeneralDgnConvertJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void clearOutputDirectory()
+    {
+        File outDataPath = new File(getDataPath(), OracleConvertJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), IndexDgnConvertJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), GeneralDgnConvertJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+    }
+
+    private void deleteFilesInPath(File outDataPath)
+    {
+        deleteFilesInPath(outDataPath, true);
+    }
+
+    private void deleteFilesInPath(File outDataPath, boolean removeSubDir)
+    {
+        if (!outDataPath.isDirectory())
+        {
+            return;
+        }
+        File[] files = outDataPath.listFiles();
+        for (File file : files)
+        {
+            if (file.isFile())
+            {
+                if (!file.delete())
+                {
+                    logger.info("Cannot delete file-" + file.toString());
+                }
+            } else if (file.isDirectory())
+            {
+                deleteFilesInPath(file, removeSubDir);
+                if (removeSubDir)
+                {
+                    if (file.delete())
+                    {
+                        logger.info("Cannot delete dir-" + file.toString());
+                    }
+                }
+            }
+        }
+    }
+
+    private void convertFeatureDesignFile(JobExecutionContext context) throws JobExecutionException
+    {
+        File elminDir = new File(getDataPath(), "elmin");
+        if (!elminDir.exists())
+        {
+            logger.info("elmin dir=" + elminDir + " not exist.");
+            return;
+        }
+
+        if (!elminDir.isDirectory())
+        {
+            logger.info("elmin dir=" + elminDir + " is not a directory.");
+        }
+
+        File[] dgnFiles = elminDir.listFiles(new FilenameFilter()
+        {
+            public boolean accept(File dir, String name)
+            {
+                return name.toLowerCase().endsWith(".dgn");
+            }
+        });
+
+        for (File dgnFile : dgnFiles)
+        {
+            FeatureDgnConvertJobContext convertContext = new FeatureDgnConvertJobContext(getDataPath(), _filterPath);
+            logger.info("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+
+                FileInputStream fs = new FileInputStream(dgnFile);
+                FileChannel fc = fs.getChannel();
+                Dgn7fileReader reader = new Dgn7fileReader(fc, new Lock());
+                convertContext.setReader(reader);
+
+                scanFeatureDgnElement(convertContext);
+
+                convertContext.commitTransaction();
+                convertContext.closeFeatureWriter();
+                System.gc();
+            } catch (FileNotFoundException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (Dgn7fileException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IOException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (IllegalAttributeException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            } catch (SchemaException e)
+            {
+                convertContext.rollbackTransaction();
+                logger.warn(e.getMessage(), e);
+                throw new JobExecutionException(e.getMessage(), e);
+            }
+        }
+    }
+
+    public void scanFeatureDgnElement(FeatureDgnConvertJobContext convertContext)
+            throws Dgn7fileException, IOException, IllegalAttributeException, SchemaException
+    {
+        Dgn7fileReader reader = convertContext.getReader();
+        int count = 0;
+        Element lastComplex = null;
+        while (reader.hasNext())
+        {
+            Dgn7fileReader.Record record = reader.nextElement();
+            if (record.element() != null)
+            {
+                Element element = (Element) record.element();
+                ElementType type = element.getElementType();
+
+                if ((!type.isComplexElement()) && (!element.isComponentElement()))
+                {
+                    lastComplex = null;
+
+                    processFeatureElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex == null)
+                    {
+                        lastComplex = element;
+                    } else
+                    {
+                        processFeatureElement(element, convertContext);
+                        lastComplex = element;
+                    }
+                }
+            }
+            count++;
+        }
+
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processFeatureElement(Element element, FeatureDgnConvertJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void createDummyFeatureFile(JobExecutionContext context) throws JobExecutionException
+    {
+        /*
+        DummyFeatureConvertJobContext convertContext = new DummyFeatureConvertJobContext(getDataPath(), _filterPath);
+        try {
+            convertContext.startTransaction();
+            convertContext.commitTransaction();
+            convertContext.closeFeatureWriter();
+        } catch (IOException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+        */
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
new file mode 100644
index 0000000..0ede50b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertJobContext.java
@@ -0,0 +1,347 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+import java.nio.charset.Charset;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SimpleFeature;
+import org.quartz.JobExecutionContext;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.util.Assert;
+
+import com.ximple.eofms.filter.AbstractFLinkageDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.ComplexElement;
+
+
+public class OracleConvertJobContext extends AbstractOracleJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    static final String SHPOUTPATH = "shpout";
+
+    private OracleElementLogger elmLogger = null;
+
+    static
+    {
+        try
+        {
+            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
+        } catch (SQLException e)
+        {
+            Assert.shouldNeverReachHere(e.getMessage());
+        }
+    }
+
+    private String _filterConfig;
+
+    private ElementDispatcher elementDispatcher;
+
+    private HashMap featuresContext = new HashMap();
+    private HashMap<String, FeatureWriter> featuresWriterContext = new HashMap<String, FeatureWriter>();
+    private PessimisticMapWrapper txFeaturesContext;
+
+    private JobExecutionContext executionContext;
+
+    private String dataOut = null;
+    private String _convertDB = null;
+    private String _convertFile = null;
+    private String currentSchema = null;
+    private boolean schemaChanged = false;
+    private String _convertElementIn = null;
+
+    public OracleConvertJobContext(String filterConfig)
+    {
+        properties = new Properties();
+        _filterConfig = filterConfig;
+        elementDispatcher = createElementDispatcher();
+        txFeaturesContext = new PessimisticMapWrapper(featuresContext, sLogger);
+    }
+
+    private ElementDispatcher createElementDispatcher()
+    {
+        try
+        {
+            URL rulesURL = ElementDispatcher.class.getResource("ElementDispatcherRules.xml");
+            assert rulesURL != null;
+            Digester digester = DigesterLoader.createDigester(rulesURL);
+            URL filterURL = null;
+            if (_filterConfig != null)
+            {
+                File config = new File(_filterConfig);
+                if (config.exists())
+                {
+                    filterURL = config.toURI().toURL();
+                }
+            }
+            if (filterURL == null)
+            {
+                // config = new File("conf/DefaultConvertShpFilter.xml");
+                filterURL = this.getClass().getResource("/conf/DefaultConvertShpFilter.xml");
+                // filterURL = this.getClass().getResource("/conf/ConvertShpFilterForLevel.xml");
+            }
+            assert filterURL != null;
+            return (ElementDispatcher) digester.parse(filterURL);
+        } catch (UnsupportedEncodingException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (MalformedURLException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (SAXException e)
+        {
+            logger.info(e.getMessage(), e);
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public void putFeatureCollection(Element element)
+    {
+        assert elementDispatcher != null;
+        // �P�_�O�_�ũM����
+        Feature feature = elementDispatcher.execute(element);
+        if (feature == null)
+        {
+            boolean isEmptySize = false;
+            FrammeAttributeData linkage =
+                    AbstractFLinkageDispatchableFilter.getFeatureLinkage(element);
+            logger.warn("Unknown Element:" + element.getElementType().toString() +
+                    ":type=" + element.getType() + ":lv=" + element.getLevelIndex() + ":id=" +
+                    (linkage == null ? "NULL" : (linkage.getFsc() + "|" + linkage.getComponentID())));
+
+            if (element instanceof ComplexElement)
+            {
+                ComplexElement complex = (ComplexElement) element;
+                logger.warn("----Complex Element size=" + complex.size() + ":" +
+                        (linkage == null ? "NULL" : (linkage.getUfid())));
+                isEmptySize = true;
+            }
+
+            if (getElementLogging() && (!isEmptySize))
+            {
+                getElementLogger().logElement(element, getCurrentSchema());
+            }
+            return;
+        }
+
+        if (!txFeaturesContext.containsKey(feature.getFeatureType()))
+        {
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList());
+        }
+        ArrayList arrayList = (ArrayList) txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+    }
+
+    public void startTransaction()
+    {
+        //txFeaturesContext.startTransaction();
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+
+        if (this.getElementLogger() != null)
+            this.getElementLogger().flashLogging();
+    }
+
+    public void rollbackTransaction()
+    {
+        //txFeaturesContext.rollbackTransaction();
+        if (!featuresContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    private void updateDataStore()
+    {
+        Iterator it = featuresContext.keySet().iterator();
+
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = (FeatureType) it.next();
+                File sfile = new File(getDataOutPath() + File.separator + featureType.getTypeName());
+                logger.debug("Begin Save shapefile:" + sfile.toURI());
+
+                FeatureWriter writer = null;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    /*
+                    ShapefileDataStore shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                            true, Charset.forName("UTF-8"));
+                    */
+                    if (!sfile.exists())
+                    {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else {
+                        ShapefileDataStore shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                        writer = shapefileDataStore.getFeatureWriterAppend(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    }
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList features = (ArrayList) featuresContext.get(featureType);
+                Iterator itFeature = features.iterator();
+                while (itFeature.hasNext())
+                {
+                    Feature feature = (Feature) itFeature.next();
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save shapefile:" + sfile.toURI());
+            }
+            featuresContext.clear();
+        } catch (MalformedURLException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IllegalAttributeException e)
+        {
+            logger.error(e.getMessage(), e);
+        } catch (IOException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+
+    }
+
+    public JobExecutionContext getExecutionContext()
+    {
+        return executionContext;
+    }
+
+    public void setExecutionContext(JobExecutionContext context)
+    {
+        executionContext = context;
+    }
+
+    /**
+     * �����]�Ƽg�J��
+     *
+     * @throws IOException IO�o�Ϳ��~
+     */
+    public void closeFeatureWriter() throws IOException
+    {
+
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+    }
+
+    /**
+     * ���o��ƿ�X���|
+     *
+     * @return ���|���r��
+     */
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), SHPOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public void setConvertDB(String convertDB)
+    {
+        _convertDB = convertDB;
+    }
+
+    public void setConvertFile(String convertFile)
+    {
+        _convertFile = convertFile;
+    }
+
+    protected OracleElementLogger getElementLogger()
+    {
+        if (elmLogger == null)
+        {
+            elmLogger = new OracleElementLogger(getOracleConnection());
+            elmLogger.setDataPath(this.getDataPath());
+        }
+        return elmLogger;
+    }
+
+    public String getCurrentSchema()
+    {
+        return currentSchema;
+    }
+
+    public void setCurrentSchema(String querySchema)
+    {
+        this.currentSchema = querySchema;
+        this.schemaChanged = true;
+    }
+
+    public void setConvertElementIn(String convertElementIn)
+    {
+        _convertElementIn = convertElementIn;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java
new file mode 100644
index 0000000..2ff257c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java
@@ -0,0 +1,314 @@
+package com.ximple.eofms.jobs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.vividsolutions.jts.util.Assert;
+
+import oracle.sql.BLOB;
+
+import com.ximple.eofms.util.PrintfFormat;
+import com.ximple.io.dgn7.ArcElement;
+import com.ximple.io.dgn7.ComplexChainElement;
+import com.ximple.io.dgn7.ComplexShapeElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.EllipseElement;
+import com.ximple.io.dgn7.LineElement;
+import com.ximple.io.dgn7.LineStringElement;
+import com.ximple.io.dgn7.ShapeElement;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.TextNodeElement;
+
+public class OracleElementLogger
+{
+    static Log logger = LogFactory.getLog(OracleElementLogger.class);
+    private static final String ELMOUTPATH = "elmout";
+    private static final String TAB_IGDSSEED = "SD$IGDSSET_SEED";
+
+    private Connection connection;
+    private String dataOut = null;
+    private String dataPath;
+    private String currentSchema;
+    private boolean schemaChanged;
+    private FileOutputStream fos = null;
+    private FileChannel fch = null;
+    private int logCount = 0;
+
+    public OracleElementLogger(Connection connection)
+    {
+        this.connection = connection;
+    }
+
+    public String getDataOutPath()
+    {
+        if (dataOut == null)
+        {
+            File outPath = new File(getDataPath(), ELMOUTPATH);
+            if (!outPath.exists())
+            {
+                outPath.mkdir();
+            } else if (!outPath.isDirectory())
+            {
+                outPath.mkdir();
+            }
+            dataOut = outPath.toString();
+        }
+        return dataOut;
+    }
+
+    public String getDataPath()
+    {
+        return dataPath;
+    }
+
+    public void setDataPath(String dataPath)
+    {
+        this.dataPath = dataPath;
+    }
+
+    public void logElement(Element element, String currentSchema)
+    {
+        if ((this.currentSchema == null) ||
+            (!this.currentSchema.equalsIgnoreCase(currentSchema)))
+        {
+            schemaChanged = true;
+            this.currentSchema = currentSchema;
+            try
+            {
+                createNewStream();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+                return;
+            } catch (SQLException e)
+            {
+                logger.warn(e.getMessage(), e);
+                return;
+            }
+        } else
+        {
+            if (fch == null)
+            {
+                try
+                {
+                    createNewStream();
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                    return;
+                } catch (SQLException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                    return;
+                }
+            }
+        }
+
+        if (fch != null)
+        {
+            ByteBuffer buf = null;
+            if (element instanceof LineElement)
+            {
+                int size = LineElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                LineElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof ShapeElement)
+            {
+                int size = ShapeElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                ShapeElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof LineStringElement)
+            {
+                int size = LineStringElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                LineStringElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof ComplexChainElement)
+            {
+                int size = ComplexChainElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                ComplexChainElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof ComplexShapeElement)
+            {
+                int size = ComplexShapeElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                ComplexShapeElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof ArcElement)
+            {
+                int size = ArcElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                ArcElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof EllipseElement)
+            {
+                int size = EllipseElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                EllipseElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof TextElement)
+            {
+                int size = TextElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                TextElement.ElementHandler.getInstance().write(buf, element);
+            } else if (element instanceof TextNodeElement)
+            {
+                int size = TextNodeElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                TextNodeElement.ElementHandler.getInstance().write(buf, element);
+            }
+
+            if ((buf != null) && (fch != null))
+            {
+                try
+                {
+                    buf.position(0);
+                    int size = fch.write(buf);
+                    if (size != buf.limit())
+                    {
+                        long position = fch.position();
+                        logger.info("Pos:" + position);
+                    }
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    private void createNewStream() throws IOException, SQLException
+    {
+        if (fos != null)
+        {
+            putEndOfFileElement();
+            fos.close();
+            fos = null;
+            fch = null;
+        }
+
+        File logFile = new File(getDataOutPath(), this.currentSchema + ".dgn");
+        while (logFile.exists())
+        {
+            logFile = new File(getDataOutPath(), this.currentSchema + "-"
+                    + (++logCount) +".dgn");
+        }
+
+        logger.warn("Create Dgn Logging File:" + logFile.toString());
+        fos = new FileOutputStream(logFile);
+        fch = fos.getChannel();
+
+        prepareOutputElementStream();
+        schemaChanged = false;
+    }
+
+    private void putEndOfFileElement() throws IOException
+    {
+        if (fch == null)
+            return;
+        ByteBuffer bf = ByteBuffer.allocate(4);
+        bf.putInt(-1);
+        fch.write(bf);
+    }
+
+    private void prepareOutputElementStream() throws SQLException, IOException
+    {
+        if (connection == null)
+        {
+            logger.warn("connection is null");
+            return;
+        }
+        String fetchSrcStmtFmt = "SELECT IGDSELM FROM \"%s\".\"%s\" ORDER BY ROWID";
+        PrintfFormat spf = new PrintfFormat(fetchSrcStmtFmt);
+        String fetchSrcStmt = spf.sprintf(new Object[]{currentSchema, TAB_IGDSSEED});
+        Statement stmtSrc = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+        ResultSet rsSrc = stmtSrc.executeQuery(fetchSrcStmt);
+
+        while (rsSrc.next())
+        {
+            byte[] raw;
+
+            if (rsSrc.getMetaData().getColumnType(1) == Types.BLOB)
+            {
+                BLOB blob = (BLOB) rsSrc.getBlob(1);
+
+                raw = getBytesFromBLOB(blob);
+                blob.close();
+            } else
+            {
+                raw = rsSrc.getBytes(1);
+            }
+
+            if (raw != null)
+            {
+                putElementIntoStream(raw);
+            }
+        }
+    }
+
+    private void putElementIntoStream(byte[] raw) throws IOException
+    {
+        if (fch != null)
+            fch.write(ByteBuffer.wrap(raw));
+    }
+
+    protected static byte[] getBytesFromBLOB(BLOB blob) throws SQLException
+    {
+        byte[] raw = null;
+
+        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)
+        {
+            logger.warn(e.getMessage(), e);
+            Assert.shouldNeverReachHere();
+        }
+        return raw;
+    }
+
+    public void flashLogging()
+    {
+        if (fos != null)
+        {
+            try
+            {
+                fos.close();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+            }
+            fos = null;
+            fch = null;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
new file mode 100644
index 0000000..1ba97a3
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
@@ -0,0 +1,60 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OracleDatabaseMetaData;
+import oracle.jdbc.OracleStatement;
+
+public class OracleUpgradeBlob2UDTJob extends AbstractOracleDatabaseJob
+{
+    static Log logger = LogFactory.getLog(OracleUpgradeBlob2UDTJob.class);
+
+    public void execute(JobExecutionContext context) throws JobExecutionException
+    {
+        // Every job has its own job detail
+        JobDetail jobDetail = context.getJobDetail();
+
+        // The name is defined in the job definition
+        String jobName = jobDetail.getName();
+
+        // Log the time the job started
+        logger.info(jobName + " fired at " + new Date());
+        extractJobConfiguration(jobDetail);
+
+        AbstractOracleJobContext jobContext = prepareJobContext(_filterPath);
+        jobContext.setConnectionInfo(_oracleHost, _oraclePort, _oracleInstance);
+        jobContext.setLogin(_username, _password);
+
+        try
+        {
+            for (String orgSchema : _orgSchema)
+            {
+                exetcuteConvert(jobContext, orgSchema, _dataPath);
+            }
+        } catch (SQLException e)
+        {
+            throw new JobExecutionException("Database error.", e);
+        }
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleUpgradeJobContext();
+    }
+
+    private void exetcuteConvert(AbstractOracleJobContext jobContext,
+                                 String orgSchema, String dataPath) throws SQLException
+    {
+        OracleConnection connection = jobContext.getOracleConnection();
+        OracleDatabaseMetaData metaData = (OracleDatabaseMetaData) connection.getMetaData();
+        OracleStatement statement = (OracleStatement) connection.createStatement();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java
new file mode 100644
index 0000000..54cf900
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeJobContext.java
@@ -0,0 +1,16 @@
+package com.ximple.eofms.jobs;
+
+public class OracleUpgradeJobContext extends AbstractOracleJobContext
+{
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java
new file mode 100644
index 0000000..3bf59cf
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/TWD97GeometryConverterDecorator.java
@@ -0,0 +1,69 @@
+package com.ximple.eofms.jobs;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.util.Assert;
+
+import com.ximple.eofms.util.TWDDatumConverter;
+import com.ximple.io.dgn7.GeometryConverter;
+
+public class TWD97GeometryConverterDecorator implements GeometryConverter
+{
+    private GeometryConverter converter;
+    private TWD97ConvertFilter coordinatesFilter = new TWD97ConvertFilter();
+
+    public TWD97GeometryConverterDecorator()
+    {
+    }
+
+    public GeometryConverter getConverter()
+    {
+        return converter;
+    }
+
+    public void setConverter(GeometryConverter converter)
+    {
+        this.converter = converter;
+    }
+
+    public Geometry toGeometry(GeometryFactory factory)
+    {
+        if (converter == null) Assert.shouldNeverReachHere();
+
+        coordinatesFilter.reset();
+
+        Geometry geom = converter.toGeometry(factory);
+        if (geom == null) return null;
+        geom.apply(coordinatesFilter);
+        return geom;
+    }
+
+    class TWD97ConvertFilter implements CoordinateSequenceFilter
+    {
+        public void filter(CoordinateSequence coordinateSequence, int i)
+        {
+            Coordinate pt =coordinateSequence.getCoordinate(i);
+            Coordinate pt97 = TWDDatumConverter.fromTM2ToTWD97(pt);
+            pt.x = pt97.x;
+            pt.y = pt97.y;
+            pt.z = pt97.z;
+        }
+
+        public boolean isDone()
+        {
+            return false;
+        }
+
+        public boolean isGeometryChanged()
+        {
+            return true;
+        }
+
+        public void reset()
+        {
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java
new file mode 100644
index 0000000..13d7c92
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java
@@ -0,0 +1,551 @@
+package com.ximple.eofms.util;
+
+import java.util.Arrays;
+
+public class Base64
+{
+    private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+    private static final int[] IA = new int[256];
+
+    static
+    {
+        Arrays.fill(IA, -1);
+        for (int i = 0, iS = CA.length; i < iS; i++)
+            IA[CA[i]] = i;
+        IA['='] = 0;
+    }
+
+    // ****************************************************************************************
+    // *  char[] version
+    // ****************************************************************************************
+
+    /**
+     * Encodes a raw byte array into a BASE64 <code>char[]</code> representation i accordance with RFC 2045.
+     *
+     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
+     * @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
+     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
+     *                little faster.
+     * @return A BASE64 encoded array. Never <code>null</code>.
+     */
+    public static char[] encodeToChar(byte[] sArr, boolean lineSep)
+    {
+        // Check special case
+        int sLen = sArr != null ? sArr.length : 0;
+        if (sLen == 0)
+            return new char[0];
+
+        int eLen = (sLen / 3) * 3;              // Length of even 24-bits.
+        int cCnt = ((sLen - 1) / 3 + 1) << 2;   // Returned character count
+        int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
+        char[] dArr = new char[dLen];
+
+        // Encode even 24-bits
+        for (int s = 0, d = 0, cc = 0; s < eLen;)
+        {
+            // Copy next three bytes into lower 24 bits of int, paying attension to sign.
+            int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
+
+            // Encode the int into four chars
+            dArr[d++] = CA[(i >>> 18) & 0x3f];
+            dArr[d++] = CA[(i >>> 12) & 0x3f];
+            dArr[d++] = CA[(i >>> 6) & 0x3f];
+            dArr[d++] = CA[i & 0x3f];
+
+            // Add optional line separator
+            if (lineSep && ++cc == 19 && d < dLen - 2)
+            {
+                dArr[d++] = '\r';
+                dArr[d++] = '\n';
+                cc = 0;
+            }
+        }
+
+        // Pad and encode last bits if source isn't even 24 bits.
+        int left = sLen - eLen; // 0 - 2.
+        if (left > 0)
+        {
+            // Prepare the int
+            int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
+
+            // Set last four chars
+            dArr[dLen - 4] = CA[i >> 12];
+            dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
+            dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
+            dArr[dLen - 1] = '=';
+        }
+        return dArr;
+    }
+
+    /**
+     * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
+     * and without line separators.
+     *
+     * @param sArr The source array. <code>null</code> or length 0 will return an empty array.
+     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
+     *         (including '=') isn't divideable by 4.  (I.e. definitely corrupted).
+     */
+    public static byte[] decode(char[] sArr)
+    {
+        // Check special case
+        int sLen = sArr != null ? sArr.length : 0;
+        if (sLen == 0)
+            return new byte[0];
+
+        // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+        // so we don't have to reallocate & copy it later.
+        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+        for (int i = 0; i < sLen; i++)  // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+            if (IA[sArr[i]] < 0)
+                sepCnt++;
+
+        // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+        if ((sLen - sepCnt) % 4 != 0)
+            return null;
+
+        int pad = 0;
+        for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;)
+            if (sArr[i] == '=')
+                pad++;
+
+        int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        for (int s = 0, d = 0; d < len;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = 0;
+            for (int j = 0; j < 4; j++)
+            {   // j only increased if a valid char was found.
+                int c = IA[sArr[s++]];
+                if (c >= 0)
+                    i |= c << (18 - j * 6);
+                else
+                    j--;
+            }
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            if (d < len)
+            {
+                dArr[d++] = (byte) (i >> 8);
+                if (d < len)
+                    dArr[d++] = (byte) i;
+            }
+        }
+        return dArr;
+    }
+
+    /**
+     * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
+     * fast as {@link #decode(char[])}. The preconditions are:<br>
+     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
+     * + Line separator must be "\r\n", as specified in RFC 2045
+     * + The array must not contain illegal characters within the encoded string<br>
+     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
+     *
+     * @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0.
+     */
+    public byte[] decodeFast(char[] sArr)
+    {
+        // Check special case
+        int sLen = sArr.length;
+        if (sLen == 0)
+            return new byte[0];
+
+        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[sArr[sIx]] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[sArr[eIx]] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0;  // Count '=' at end.
+        int cCnt = eIx - sIx + 1;   // Content count including possible separators
+        int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
+
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            dArr[d++] = (byte) (i >> 8);
+            dArr[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19)
+            {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len)
+        {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[sArr[sIx++]] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                dArr[d++] = (byte) (i >> r);
+        }
+
+        return dArr;
+    }
+
+    // ****************************************************************************************
+    // *  byte[] version
+    // ****************************************************************************************
+
+    /**
+     * Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
+     *
+     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
+     * @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
+     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
+     *                little faster.
+     * @return A BASE64 encoded array. Never <code>null</code>.
+     */
+    public static byte[] encodeToByte(byte[] sArr, boolean lineSep)
+    {
+        // Check special case
+        int sLen = sArr != null ? sArr.length : 0;
+        if (sLen == 0)
+            return new byte[0];
+
+        int eLen = (sLen / 3) * 3;                              // Length of even 24-bits.
+        int cCnt = ((sLen - 1) / 3 + 1) << 2;                   // Returned character count
+        int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
+        byte[] dArr = new byte[dLen];
+
+        // Encode even 24-bits
+        for (int s = 0, d = 0, cc = 0; s < eLen;)
+        {
+            // Copy next three bytes into lower 24 bits of int, paying attension to sign.
+            int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
+
+            // Encode the int into four chars
+            dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
+            dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
+            dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
+            dArr[d++] = (byte) CA[i & 0x3f];
+
+            // Add optional line separator
+            if (lineSep && ++cc == 19 && d < dLen - 2)
+            {
+                dArr[d++] = '\r';
+                dArr[d++] = '\n';
+                cc = 0;
+            }
+        }
+
+        // Pad and encode last bits if source isn't an even 24 bits.
+        int left = sLen - eLen; // 0 - 2.
+        if (left > 0)
+        {
+            // Prepare the int
+            int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
+
+            // Set last four chars
+            dArr[dLen - 4] = (byte) CA[i >> 12];
+            dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
+            dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
+            dArr[dLen - 1] = '=';
+        }
+        return dArr;
+    }
+
+    /**
+     * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
+     * and without line separators.
+     *
+     * @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
+     *         (including '=') isn't divideable by 4. (I.e. definitely corrupted).
+     */
+    public static byte[] decode(byte[] sArr)
+    {
+        // Check special case
+        int sLen = sArr.length;
+
+        // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+        // so we don't have to reallocate & copy it later.
+        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+        for (int i = 0; i < sLen; i++)      // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+            if (IA[sArr[i] & 0xff] < 0)
+                sepCnt++;
+
+        // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+        if ((sLen - sepCnt) % 4 != 0)
+            return null;
+
+        int pad = 0;
+        for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;)
+            if (sArr[i] == '=')
+                pad++;
+
+        int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        for (int s = 0, d = 0; d < len;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = 0;
+            for (int j = 0; j < 4; j++)
+            {   // j only increased if a valid char was found.
+                int c = IA[sArr[s++] & 0xff];
+                if (c >= 0)
+                    i |= c << (18 - j * 6);
+                else
+                    j--;
+            }
+
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            if (d < len)
+            {
+                dArr[d++] = (byte) (i >> 8);
+                if (d < len)
+                    dArr[d++] = (byte) i;
+            }
+        }
+
+        return dArr;
+    }
+
+
+    /**
+     * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
+     * fast as {@link #decode(byte[])}. The preconditions are:<br>
+     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
+     * + Line separator must be "\r\n", as specified in RFC 2045
+     * + The array must not contain illegal characters within the encoded string<br>
+     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
+     *
+     * @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0.
+     */
+    public static byte[] decodeFast(byte[] sArr)
+    {
+        // Check special case
+        int sLen = sArr.length;
+        if (sLen == 0)
+            return new byte[0];
+
+        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0;  // Count '=' at end.
+        int cCnt = eIx - sIx + 1;   // Content count including possible separators
+        int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
+
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            dArr[d++] = (byte) (i >> 8);
+            dArr[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19)
+            {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len)
+        {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[sArr[sIx++]] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                dArr[d++] = (byte) (i >> r);
+        }
+
+        return dArr;
+    }
+
+    // ****************************************************************************************
+    // * String version
+    // ****************************************************************************************
+
+    /**
+     * Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.
+     *
+     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
+     * @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
+     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
+     *                little faster.
+     * @return A BASE64 encoded array. Never <code>null</code>.
+     */
+    public static String encodeToString(byte[] sArr, boolean lineSep)
+    {
+        // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
+        return new String(encodeToChar(sArr, lineSep));
+    }
+
+    /**
+     * Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with
+     * and without line separators.<br>
+     * <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That
+     * will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.
+     *
+     * @param str The source string. <code>null</code> or length 0 will return an empty array.
+     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
+     *         (including '=') isn't divideable by 4.  (I.e. definitely corrupted).
+     */
+    public static byte[] decode(String str)
+    {
+        // Check special case
+        int sLen = str != null ? str.length() : 0;
+        if (sLen == 0)
+            return new byte[0];
+
+        // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+        // so we don't have to reallocate & copy it later.
+        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+        for (int i = 0; i < sLen; i++)  // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+            if (IA[str.charAt(i)] < 0)
+                sepCnt++;
+
+        // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+        if ((sLen - sepCnt) % 4 != 0)
+            return null;
+
+        // Count '=' at end
+        int pad = 0;
+        for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;)
+            if (str.charAt(i) == '=')
+                pad++;
+
+        int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        for (int s = 0, d = 0; d < len;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = 0;
+            for (int j = 0; j < 4; j++)
+            {   // j only increased if a valid char was found.
+                int c = IA[str.charAt(s++)];
+                if (c >= 0)
+                    i |= c << (18 - j * 6);
+                else
+                    j--;
+            }
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            if (d < len)
+            {
+                dArr[d++] = (byte) (i >> 8);
+                if (d < len)
+                    dArr[d++] = (byte) i;
+            }
+        }
+        return dArr;
+    }
+
+    /**
+     * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as
+     * fast as {@link #decode(String)}. The preconditions are:<br>
+     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
+     * + Line separator must be "\r\n", as specified in RFC 2045
+     * + The array must not contain illegal characters within the encoded string<br>
+     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
+     *
+     * @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
+     * @return The decoded array of bytes. May be of length 0.
+     */
+    public static byte[] decodeFast(String s)
+    {
+        // Check special case
+        int sLen = s.length();
+        if (sLen == 0)
+            return new byte[0];
+
+        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.
+
+        // Trim illegal chars from start
+        while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
+            sIx++;
+
+        // Trim illegal chars from end
+        while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
+            eIx--;
+
+        // get the padding count (=) (0, 1 or 2)
+        int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0;  // Count '=' at end.
+        int cCnt = eIx - sIx + 1;   // Content count including possible separators
+        int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length
+
+        // Decode all but the last 0 - 2 bytes.
+        int d = 0;
+        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;)
+        {
+            // Assemble three bytes into an int from four "valid" characters.
+            int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
+
+            // Add the bytes
+            dArr[d++] = (byte) (i >> 16);
+            dArr[d++] = (byte) (i >> 8);
+            dArr[d++] = (byte) i;
+
+            // If line separator, jump over it.
+            if (sepCnt > 0 && ++cc == 19)
+            {
+                sIx += 2;
+                cc = 0;
+            }
+        }
+
+        if (d < len)
+        {
+            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+            int i = 0;
+            for (int j = 0; sIx <= eIx - pad; j++)
+                i |= IA[s.charAt(sIx++)] << (18 - j * 6);
+
+            for (int r = 16; d < len; r -= 8)
+                dArr[d++] = (byte) (i >> r);
+        }
+
+        return dArr;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
new file mode 100644
index 0000000..2dd4c53
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/BinConverter.java
@@ -0,0 +1,363 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.LongBuffer;
+
+/**
+ * BinConverter
+ * User: Ulysses
+ * Date: 2007/9/17
+ * Time: �W�� 01:13:13
+ */
+public class BinConverter
+{
+    // our table for binhex conversion
+    final static char[] HEXTAB =
+            {
+                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+            };
+
+    /**
+     * gets bytes from an array into a long
+     *
+     * @param buffer      where to get the bytes
+     * @param nStartIndex index from where to read the data
+     * @return the 64bit integer
+     */
+    public static long byteArrayToLong(byte[] buffer, int nStartIndex)
+    {
+        return (((long) buffer[nStartIndex]) << 56) | (((long) buffer[nStartIndex + 1] & 0x0ffL) << 48)
+                | (((long) buffer[nStartIndex + 2] & 0x0ffL) << 40) | (((long) buffer[nStartIndex + 3] & 0x0ffL) << 32)
+                | (((long) buffer[nStartIndex + 4] & 0x0ffL) << 24) | (((long) buffer[nStartIndex + 5] & 0x0ffL) << 16)
+                | (((long) buffer[nStartIndex + 6] & 0x0ffL) << 8) | ((long) buffer[nStartIndex + 7] & 0x0ff);
+    }
+
+    /**
+     * converts a long o bytes which are put into a given array
+     *
+     * @param lValue      the 64bit integer to convert
+     * @param buffer      the target buffer
+     * @param nStartIndex where to place the bytes in the buffer
+     */
+    public static void longToByteArray(long lValue, byte[] buffer, int nStartIndex)
+    {
+        buffer[nStartIndex] = (byte) (lValue >>> 56);
+        buffer[nStartIndex + 1] = (byte) ((lValue >>> 48) & 0x0ff);
+        buffer[nStartIndex + 2] = (byte) ((lValue >>> 40) & 0x0ff);
+        buffer[nStartIndex + 3] = (byte) ((lValue >>> 32) & 0x0ff);
+        buffer[nStartIndex + 4] = (byte) ((lValue >>> 24) & 0x0ff);
+        buffer[nStartIndex + 5] = (byte) ((lValue >>> 16) & 0x0ff);
+        buffer[nStartIndex + 6] = (byte) ((lValue >>> 8) & 0x0ff);
+        buffer[nStartIndex + 7] = (byte) lValue;
+    }
+
+    /**
+     * converts values from an integer array to a long
+     *
+     * @param buffer      where to get the bytes
+     * @param nStartIndex index from where to read the data
+     * @return the 64bit integer
+     */
+    public static long intArrayToLong(int[] buffer, int nStartIndex)
+    {
+        return (((long) buffer[nStartIndex]) << 32) | (((long) buffer[nStartIndex + 1]) & 0x0ffffffffL);
+    }
+
+    /**
+     * converts a long to integers which are put into a given array
+     *
+     * @param lValue      the 64bit integer to convert
+     * @param buffer      the target buffer
+     * @param nStartIndex where to place the bytes in the buffer
+     */
+    public static void longToIntArray(long lValue, int[] buffer, int nStartIndex)
+    {
+        buffer[nStartIndex] = (int) (lValue >>> 32);
+        buffer[nStartIndex + 1] = (int) lValue;
+    }
+
+    /**
+     * makes a long from two integers (treated unsigned)
+     *
+     * @param nLo lower 32bits
+     * @param nHi higher 32bits
+     * @return the built long
+     */
+    public static long makeLong(int nLo, int nHi)
+    {
+        return (((long) nHi << 32) | ((long) nLo & 0x00000000ffffffffL));
+    }
+
+    /**
+     * gets the lower 32 bits of a long
+     *
+     * @param lVal the long integer
+     * @return lower 32 bits
+     */
+    public static int longLo32(long lVal)
+    {
+        return (int) lVal;
+    }
+
+    /**
+     * gets the higher 32 bits of a long
+     *
+     * @param lVal the long integer
+     * @return higher 32 bits
+     */
+    public static int longHi32(long lVal)
+    {
+        return (int) ((long) (lVal >>> 32));
+    }
+
+    /**
+     * converts a byte array to a binhex string
+     *
+     * @param data the byte array
+     * @return the binhex string
+     */
+    public static String bytesToBinHex(byte[] data)
+    {
+        // just map the call
+        return bytesToBinHex(data, 0, data.length);
+    }
+
+    /**
+     * converts a byte array to a binhex string
+     *
+     * @param data        the byte array
+     * @param nStartPos   start index where to get the bytes
+     * @param nNumOfBytes number of bytes to convert
+     * @return the binhex string
+     */
+    public static String bytesToBinHex(byte[] data, int nStartPos, int nNumOfBytes)
+    {
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.setLength(nNumOfBytes << 1);
+
+        int nPos = 0;
+
+        for (int nI = 0; nI < nNumOfBytes; nI++)
+        {
+            sbuf.setCharAt(nPos++, HEXTAB[(data[nI + nStartPos] >> 4) & 0x0f]);
+            sbuf.setCharAt(nPos++, HEXTAB[data[nI + nStartPos] & 0x0f]);
+        }
+
+        return sbuf.toString();
+    }
+
+    /**
+     * converts a binhex string back into a byte array (invalid codes will be skipped)
+     *
+     * @param sBinHex     binhex string
+     * @param data        the target array
+     * @param nSrcPos     from which character in the string the conversion should begin,
+     *                    remember that (nSrcPos modulo 2) should equals 0 normally
+     * @param nDstPos     to store the bytes from which position in the array
+     * @param nNumOfBytes number of bytes to extract
+     * @return number of extracted bytes
+     */
+    public static int binHexToBytes(String sBinHex, byte[] data, int nSrcPos, int nDstPos, int nNumOfBytes)
+    {
+        // check for correct ranges
+        int nStrLen = sBinHex.length();
+        int nAvailBytes = (nStrLen - nSrcPos) >> 1;
+
+        if (nAvailBytes < nNumOfBytes)
+        {
+            nNumOfBytes = nAvailBytes;
+        }
+
+        int nOutputCapacity = data.length - nDstPos;
+
+        if (nNumOfBytes > nOutputCapacity)
+        {
+            nNumOfBytes = nOutputCapacity;
+        }
+
+        // convert now
+        int nResult = 0;
+
+        for (int nI = 0; nI < nNumOfBytes; nI++)
+        {
+            byte bActByte = 0;
+            boolean blConvertOK = true;
+
+            for (int nJ = 0; nJ < 2; nJ++)
+            {
+                bActByte <<= 4;
+
+                char cActChar = sBinHex.charAt(nSrcPos++);
+
+                if ((cActChar >= 'a') && (cActChar <= 'f'))
+                {
+                    bActByte |= (byte) (cActChar - 'a') + 10;
+                } else if ((cActChar >= '0') && (cActChar <= '9'))
+                {
+                    bActByte |= (byte) (cActChar - '0');
+                } else
+                {
+                    blConvertOK = false;
+                }
+            }
+
+            if (blConvertOK)
+            {
+                data[nDstPos++] = bActByte;
+                nResult++;
+            }
+        }
+
+        return nResult;
+    }
+
+    /**
+     * converts a byte array into an UNICODE string
+     *
+     * @param data        the byte array
+     * @param nStartPos   where to begin the conversion
+     * @param nNumOfBytes number of bytes to handle
+     * @return the string
+     */
+    public static String byteArrayToUNCString(byte[] data, int nStartPos, int nNumOfBytes)
+    {
+        // we need two bytes for every character
+        nNumOfBytes &= ~1;
+
+        // enough bytes in the buffer?
+        int nAvailCapacity = data.length - nStartPos;
+
+        if (nAvailCapacity < nNumOfBytes)
+        {
+            nNumOfBytes = nAvailCapacity;
+        }
+
+        StringBuffer sbuf = new StringBuffer();
+
+        sbuf.setLength(nNumOfBytes >> 1);
+
+        int nSBufPos = 0;
+
+        while (nNumOfBytes > 0)
+        {
+            sbuf.setCharAt(nSBufPos++, (char) (((int) data[nStartPos] << 8) | ((int) data[nStartPos + 1] & 0x0ff)));
+            nStartPos += 2;
+            nNumOfBytes -= 2;
+        }
+
+        return sbuf.toString();
+    }
+
+    public static long[] marshalByteArray(byte[] raws, boolean hasSignature)
+    {
+        int remainder = raws.length % 8;
+        ByteBuffer rawData = ByteBuffer.wrap(raws);
+
+        rawData.rewind();
+        rawData.order(ByteOrder.LITTLE_ENDIAN);
+
+        LongBuffer longBuffer = ((ByteBuffer) rawData.rewind()).asLongBuffer();
+        int resultSize = longBuffer.limit() + ((remainder != 0)
+                ? 1
+                : 0) + (hasSignature
+                ? 1
+                : 0);
+        long[] result = new long[resultSize];
+        int i = 0;
+
+        if (hasSignature)
+        {
+            result[i] = raws.length;
+            i++;
+        }
+
+        while (longBuffer.hasRemaining())
+        {
+            result[i] = longBuffer.get();
+            i++;
+        }
+
+        if (remainder != 0)
+        {
+            int pos = (i - (hasSignature
+                    ? 1
+                    : 0)) * 8;
+
+            // int pos = rawData.position();
+            byte[] temp = new byte[8];
+
+            for (int j = 0; j < remainder; j++)
+            {
+                temp[7 - j] = raws[pos + j];
+            }
+
+            // System.arraycopy(raws, pos, temp, 0, remainder);
+            result[i] = BinConverter.byteArrayToLong(temp, 0);
+        }
+
+        return result;
+    }
+
+    public static byte[] unmarshalByteArray(long[] raws, boolean hasSignature)
+    {
+        LongBuffer longBuffer = LongBuffer.wrap(raws);
+        int resultBufferSize = (raws.length - (hasSignature
+                ? 1
+                : 0)) * 8;
+        int resultSize = resultBufferSize;
+
+        if (hasSignature)
+        {
+            resultSize = (int) longBuffer.get();
+        }
+
+        ByteBuffer result = ByteBuffer.allocate(resultBufferSize);
+
+        result.order(ByteOrder.LITTLE_ENDIAN);
+
+        while (longBuffer.hasRemaining())
+        {
+            result.putLong(longBuffer.get());
+        }
+
+        if (resultSize == resultBufferSize)
+        {
+            return result.array();
+        }
+
+        byte[] resultData = new byte[resultSize];
+
+        result.position(0);
+        result.get(resultData, 0, resultSize);
+
+        return resultData;
+    }
+
+    public static long[] marshalCompactByteArray(byte[] raws)
+    {
+        byte[] compactRaws = new byte[raws.length + 2];
+        ByteBuffer bbCompact = ByteBuffer.wrap(compactRaws);
+        bbCompact.order(ByteOrder.LITTLE_ENDIAN);
+        bbCompact.putShort((short) raws.length);
+        bbCompact.put(raws);
+        long[] longData = BinConverter.marshalByteArray(compactRaws, false);
+        return longData;
+    }
+
+    public static byte[] unmarshalCompactByteArray(long[] raws)
+    {
+        byte[] rawData = BinConverter.unmarshalByteArray(raws, false);
+
+        ByteBuffer bbCompact = ByteBuffer.wrap(rawData);
+        bbCompact.order(ByteOrder.LITTLE_ENDIAN);
+        short originSize = bbCompact.getShort();
+
+        byte[] rawOriginData = new byte[originSize];
+        bbCompact.get(rawOriginData, 0, originSize);
+        return rawOriginData;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java
new file mode 100644
index 0000000..5e529ac
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Bits.java
@@ -0,0 +1,323 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.ByteOrder;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.misc.VM;
+
+/**
+ * Bits
+ * User: Ulysses
+ * Date: 2007/6/17
+ * Time: �W�� 01:16:39
+ */
+public class Bits
+{
+    // -- Unsafe access --
+    // private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    // -- Processor and memory-system properties --
+    private static ByteOrder byteOrder = null;
+    private static int pageSize = -1;
+    private static boolean unaligned;
+    private static boolean unalignedKnown = false;
+
+    // -- Direct memory management --
+    // A user-settable upper limit on the maximum amount of allocatable
+    // direct buffer memory. This value may be changed during VM
+    // initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
+    private static volatile long maxMemory = VM.maxDirectMemory();
+    private static volatile long reservedMemory = 0;
+    private static boolean memoryLimitSet = false;
+
+    private Bits()
+    {
+    }
+
+    // -- Swapping --
+    public static short swap(short x)
+    {
+        return (short) ((x << 8) | ((x >> 8) & 0xff));
+    }
+
+    public static char swap(char x)
+    {
+        return (char) ((x << 8) | ((x >> 8) & 0xff));
+    }
+
+    public static int swap(int x)
+    {
+        return (int) ((swap((short) x) << 16) | (swap((short) (x >> 16)) & 0xffff));
+    }
+
+    public static long swap(long x)
+    {
+        return (long) (((long) swap((int) (x)) << 32) | ((long) swap((int) (x >> 32)) & 0xffffffffL));
+    }
+
+    // -- get/put char --
+    static private char makeChar(byte b1, byte b0)
+    {
+        return (char) ((b1 << 8) | (b0 & 0xff));
+    }
+
+    private static byte char1(char x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte char0(char x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // --get/put short--
+    public static short makeShort(byte b1, byte b0)
+    {
+        return (short) ((b1 << 8) | (b0 & 0xff));
+    }
+
+    private static byte short1(short x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    public static byte short0(short x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put int --
+    public static int makeInt(byte b3, byte b2, byte b1, byte b0)
+    {
+        return (int) ((((b3 & 0xff) << 24) | ((b2 & 0xff) << 16) | ((b1 & 0xff) << 8) | ((b0 & 0xff) << 0)));
+    }
+
+    public static int makeInt(short hiword, short loword)
+    {
+        return ((hiword & 0xffff) << 16) + (loword & 0xffff);
+    }
+
+    public static short getHiShort(int qwValue)
+    {
+        return ((short) (qwValue >>> 16));
+    }
+
+    public static short getLoShort(int qwValue)
+    {
+        return ((short) (qwValue & 0xFFFF));
+    }
+
+    public static byte int3(int x)
+    {
+        return (byte) (x >> 24);
+    }
+
+    public static byte int2(int x)
+    {
+        return (byte) (x >> 16);
+    }
+
+    private static byte int1(int x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte int0(int x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put long --
+    public static long makeLong(byte b7, byte b6, byte b5, byte b4, byte b3, byte b2, byte b1, byte b0)
+    {
+        return ((((long) b7 & 0xff) << 56) | (((long) b6 & 0xff) << 48) | (((long) b5 & 0xff) << 40) | (((long) b4 & 0xff) << 32)
+                | (((long) b3 & 0xff) << 24) | (((long) b2 & 0xff) << 16) | (((long) b1 & 0xff) << 8)
+                | (((long) b0 & 0xff) << 0));
+    }
+
+    public static long makeLong(int LoValue, int HiValue)
+    {
+        return (((long) HiValue & 0xFFFFFFFF) << 32) + (((long) LoValue) & 0xFFFFFFFF);
+    }
+
+    public static int getHiInt(long qwValue)
+    {
+        return ((int) (qwValue >>> 32));
+    }
+
+    public static int getLoInt(long qwValue)
+    {
+        return ((int) (qwValue & 0xFFFFFFFF));
+    }
+
+    private static byte long7(long x)
+    {
+        return (byte) (x >> 56);
+    }
+
+    private static byte long6(long x)
+    {
+        return (byte) (x >> 48);
+    }
+
+    private static byte long5(long x)
+    {
+        return (byte) (x >> 40);
+    }
+
+    private static byte long4(long x)
+    {
+        return (byte) (x >> 32);
+    }
+
+    private static byte long3(long x)
+    {
+        return (byte) (x >> 24);
+    }
+
+    private static byte long2(long x)
+    {
+        return (byte) (x >> 16);
+    }
+
+    private static byte long1(long x)
+    {
+        return (byte) (x >> 8);
+    }
+
+    private static byte long0(long x)
+    {
+        return (byte) (x >> 0);
+    }
+
+    // -- get/put float --
+
+    // -- get/put double --
+
+    /*
+    private static byte _get(long a)
+    {
+        return unsafe.getByte(a);
+    }
+
+    private static void _put(long a, byte b)
+    {
+        unsafe.putByte(a, b);
+    }
+
+    static Unsafe unsafe()
+    {
+        return unsafe;
+    }
+
+    static ByteOrder byteOrder()
+    {
+        if (byteOrder != null)
+        {
+            return byteOrder;
+        }
+
+        long a = unsafe.allocateMemory(8);
+
+        try
+        {
+            unsafe.putLong(a, 0x0102030405060708L);
+
+            byte b = unsafe.getByte(a);
+
+            switch (b)
+            {
+            case 0x01 :
+                byteOrder = ByteOrder.BIG_ENDIAN;
+
+                break;
+
+            case 0x08 :
+                byteOrder = ByteOrder.LITTLE_ENDIAN;
+
+                break;
+
+            default :
+                throw new Error("Unknown byte order");
+            }
+        } finally
+        {
+            unsafe.freeMemory(a);
+        }
+
+        return byteOrder;
+    }
+    */
+
+    static boolean unaligned()
+    {
+        if (unalignedKnown)
+        {
+            return unaligned;
+        }
+
+        PrivilegedAction pa = new sun.security.action.GetPropertyAction("os.arch");
+        String arch = (String) AccessController.doPrivileged(pa);
+
+        unaligned = arch.equals("i386") || arch.equals("x86");
+        unalignedKnown = true;
+
+        return unaligned;
+    }
+
+    // These methods should be called whenever direct memory is allocated or
+    //  freed. They allow the user to control the amount of direct memory
+    // which a process may access. All sizes are specified in bytes.
+    static void reserveMemory(long size)
+    {
+        synchronized (Bits.class)
+        {
+            if (!memoryLimitSet && VM.isBooted())
+            {
+                maxMemory = VM.maxDirectMemory();
+                memoryLimitSet = true;
+            }
+
+            if (size <= maxMemory - reservedMemory)
+            {
+                reservedMemory += size;
+
+                return;
+            }
+        }
+
+        System.gc();
+
+        try
+        {
+            Thread.sleep(100);
+        } catch (InterruptedException x)
+        {
+            // Restore interrupt status
+            Thread.currentThread().interrupt();
+        }
+
+        synchronized (Bits.class)
+        {
+            if (reservedMemory + size > maxMemory)
+            {
+                throw new OutOfMemoryError("Direct buffer memory");
+            }
+
+            reservedMemory += size;
+        }
+    }
+
+    static synchronized void unreserveMemory(long size)
+    {
+        if (reservedMemory > 0)
+        {
+            reservedMemory -= size;
+            assert (reservedMemory > -1);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java
new file mode 100644
index 0000000..f22eaec
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java
@@ -0,0 +1,97 @@
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+/**
+ * ByteArrayCompressor
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �U�� 02:21:00
+ * To change this template use File | Settings | File Templates.
+ */
+public final class ByteArrayCompressor
+{
+    public static byte[] decompressByteArray(byte[] raw)
+    {
+        // Create the decompressor and give it the data to compress
+        Inflater decompressor = new Inflater();
+
+        decompressor.setInput(raw);
+
+        // Create an expandable byte array to hold the decompressed data
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(raw.length);
+
+        // Decompress the data
+        byte[] buf = new byte[1024];
+
+        while (!decompressor.finished())
+        {
+            try
+            {
+                int count = decompressor.inflate(buf);
+
+                bos.write(buf, 0, count);
+            } catch (DataFormatException e)
+            {
+            }
+        }
+
+        try
+        {
+            bos.close();
+        } catch (IOException e)
+        {
+        }
+
+        // Get the decompressed data
+        byte[] decompressedData = bos.toByteArray();
+
+        return decompressedData;
+    }
+
+    public static byte[] compressByteArray(byte[] raw)
+    {
+        // Create the compressor with highest level of compression
+        Deflater compressor = new Deflater();
+
+        compressor.setLevel(Deflater.BEST_SPEED);
+
+        // Give the compressor the data to compress
+        compressor.setInput(raw);
+        compressor.finish();
+
+        // Create an expandable byte array to hold the compressed data.
+        // You cannot use an array that's the same size as the orginal because
+        // there is no guarantee that the compressed data will be smaller than
+        // the uncompressed data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(raw.length);
+
+        // Compress the data
+        byte[] buf = new byte[1024];
+
+        while (!compressor.finished())
+        {
+            int count = compressor.deflate(buf);
+
+            bos.write(buf, 0, count);
+        }
+
+        try
+        {
+            bos.close();
+        } catch (IOException e)
+        {
+        }
+
+        // Get the compressed data
+        byte[] compressedData = bos.toByteArray();
+
+        return compressedData;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java
new file mode 100644
index 0000000..b0e3715
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java
@@ -0,0 +1,12 @@
+package com.ximple.eofms.util;
+
+import java.awt.Color;
+import java.util.List;
+
+public interface ColorTableMapping
+{
+    boolean contain(Color color);
+    List findId(Color color);
+    Color getColor(int value);
+    String getColorCode(int i);
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java
new file mode 100644
index 0000000..b050fb0
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java
@@ -0,0 +1,363 @@
+package com.ximple.eofms.util;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultColorTable implements ColorTableMapping
+{
+    private static DefaultColorTable _instance  = null;
+
+    public static ColorTableMapping getInstance()
+    {
+        if (_instance == null)
+        {
+            _instance = new DefaultColorTable();
+        }
+
+        return _instance;
+    }
+
+    private ArrayList<Color> colortable = null;
+    private DefaultColorTable()
+    {
+        initializeColorTable();
+    }
+
+    private void initializeColorTable()
+    {
+        if (colortable != null)
+        {
+            return;
+        }
+
+        colortable = new ArrayList<Color>(255);
+        colortable.add(0, new Color(255, 255, 255));
+        colortable.add(1, new Color(0, 0, 255));
+        colortable.add(2, new Color(0, 255, 0));
+        colortable.add(3, new Color(255, 0, 0));
+        colortable.add(4, new Color(255, 255, 0));
+        colortable.add(5, new Color(255, 0, 255));
+        colortable.add(6, new Color(255, 127, 0));
+        colortable.add(7, new Color(0, 255, 255));
+        colortable.add(8, new Color(64, 64, 64));
+        colortable.add(9, new Color(192, 192, 192));
+        colortable.add(10, new Color(254, 0, 96));
+        colortable.add(11, new Color(160, 224, 0));
+        colortable.add(12, new Color(0, 254, 160));
+        colortable.add(13, new Color(128, 0, 160));
+        colortable.add(14, new Color(176, 176, 176));
+        colortable.add(15, new Color(0, 240, 240));
+        colortable.add(16, new Color(240, 240, 240));
+        colortable.add(17, new Color(0, 0, 240));
+        colortable.add(18, new Color(0, 240, 0));
+        colortable.add(19, new Color(240, 0, 0));
+        colortable.add(20, new Color(240, 240, 0));
+        colortable.add(21, new Color(240, 0, 240));
+        colortable.add(22, new Color(240, 122, 0));
+        colortable.add(23, new Color(0, 240, 240));
+        colortable.add(24, new Color(240, 240, 240));
+        colortable.add(25, new Color(0, 0, 240));
+        colortable.add(26, new Color(0, 240, 0));
+        colortable.add(27, new Color(240, 0, 0));
+        colortable.add(28, new Color(240, 240, 0));
+        colortable.add(29, new Color(240, 0, 240));
+        colortable.add(30, new Color(240, 122, 0));
+        colortable.add(31, new Color(0, 225, 225));
+        colortable.add(32, new Color(225, 225, 225));
+        colortable.add(33, new Color(0, 0, 225));
+        colortable.add(34, new Color(0, 225, 0));
+        colortable.add(35, new Color(225, 0, 0));
+        colortable.add(36, new Color(225, 225, 0));
+        colortable.add(37, new Color(225, 0, 225));
+        colortable.add(38, new Color(225, 117, 0));
+        colortable.add(39, new Color(0, 225, 225));
+        colortable.add(40, new Color(225, 225, 225));
+        colortable.add(41, new Color(0, 0, 225));
+        colortable.add(42, new Color(0, 225, 0));
+        colortable.add(43, new Color(225, 0, 0));
+        colortable.add(44, new Color(225, 225, 0));
+        colortable.add(45, new Color(225, 0, 225));
+        colortable.add(46, new Color(225, 117, 0));
+        colortable.add(47, new Color(0, 210, 210));
+        colortable.add(48, new Color(210, 210, 210));
+        colortable.add(49, new Color(0, 0, 210));
+        colortable.add(50, new Color(0, 210, 0));
+        colortable.add(51, new Color(210, 0, 0));
+        colortable.add(52, new Color(210, 210, 0));
+        colortable.add(53, new Color(210, 0, 210));
+        colortable.add(54, new Color(210, 112, 0));
+        colortable.add(55, new Color(0, 210, 210));
+        colortable.add(56, new Color(210, 210, 210));
+        colortable.add(57, new Color(0, 0, 210));
+        colortable.add(58, new Color(0, 210, 0));
+        colortable.add(59, new Color(210, 0, 0));
+        colortable.add(60, new Color(210, 210, 0));
+        colortable.add(61, new Color(210, 0, 210));
+        colortable.add(62, new Color(210, 112, 0));
+        colortable.add(63, new Color(0, 195, 195));
+        colortable.add(64, new Color(195, 195, 195));
+        colortable.add(65, new Color(0, 0, 195));
+        colortable.add(66, new Color(0, 195, 0));
+        colortable.add(67, new Color(195, 0, 0));
+        colortable.add(68, new Color(195, 195, 0));
+        colortable.add(69, new Color(195, 0, 195));
+        colortable.add(70, new Color(195, 107, 0));
+        colortable.add(71, new Color(0, 195, 195));
+        colortable.add(72, new Color(195, 195, 195));
+        colortable.add(73, new Color(0, 0, 195));
+        colortable.add(74, new Color(0, 195, 0));
+        colortable.add(75, new Color(195, 0, 0));
+        colortable.add(76, new Color(195, 195, 0));
+        colortable.add(77, new Color(195, 0, 195));
+        colortable.add(78, new Color(195, 107, 0));
+        colortable.add(79, new Color(0, 180, 180));
+        colortable.add(80, new Color(180, 180, 180));
+        colortable.add(81, new Color(0, 0, 180));
+        colortable.add(82, new Color(0, 180, 0));
+        colortable.add(83, new Color(180, 0, 0));
+        colortable.add(84, new Color(180, 180, 0));
+        colortable.add(85, new Color(180, 0, 180));
+        colortable.add(86, new Color(180, 102, 0));
+        colortable.add(87, new Color(0, 180, 180));
+        colortable.add(88, new Color(180, 180, 180));
+        colortable.add(89, new Color(0, 0, 180));
+        colortable.add(90, new Color(0, 180, 0));
+        colortable.add(91, new Color(180, 0, 0));
+        colortable.add(92, new Color(180, 180, 0));
+        colortable.add(93, new Color(180, 0, 180));
+        colortable.add(94, new Color(180, 102, 0));
+        colortable.add(95, new Color(0, 165, 165));
+        colortable.add(96, new Color(165, 165, 165));
+        colortable.add(97, new Color(0, 0, 165));
+        colortable.add(98, new Color(0, 165, 0));
+        colortable.add(99, new Color(165, 0, 0));
+        colortable.add(100, new Color(165, 165, 0));
+        colortable.add(101, new Color(165, 0, 165));
+        colortable.add(102, new Color(165, 97, 0));
+        colortable.add(103, new Color(0, 165, 165));
+        colortable.add(104, new Color(165, 165, 165));
+        colortable.add(105, new Color(0, 0, 165));
+        colortable.add(106, new Color(0, 165, 0));
+        colortable.add(107, new Color(165, 0, 0));
+        colortable.add(108, new Color(165, 165, 0));
+        colortable.add(109, new Color(165, 0, 165));
+        colortable.add(110, new Color(165, 97, 0));
+        colortable.add(111, new Color(0, 150, 150));
+        colortable.add(112, new Color(150, 150, 150));
+        colortable.add(113, new Color(0, 0, 150));
+        colortable.add(114, new Color(0, 150, 0));
+        colortable.add(115, new Color(150, 0, 0));
+        colortable.add(116, new Color(150, 150, 0));
+        colortable.add(117, new Color(150, 0, 150));
+        colortable.add(118, new Color(150, 92, 0));
+        colortable.add(119, new Color(0, 150, 150));
+        colortable.add(120, new Color(150, 150, 150));
+        colortable.add(121, new Color(0, 0, 150));
+        colortable.add(122, new Color(0, 150, 0));
+        colortable.add(123, new Color(150, 0, 0));
+        colortable.add(124, new Color(150, 150, 0));
+        colortable.add(125, new Color(150, 0, 150));
+        colortable.add(126, new Color(150, 92, 0));
+        colortable.add(127, new Color(0, 135, 135));
+        colortable.add(128, new Color(135, 135, 135));
+        colortable.add(129, new Color(0, 0, 135));
+        colortable.add(130, new Color(0, 135, 0));
+        colortable.add(131, new Color(135, 0, 0));
+        colortable.add(132, new Color(135, 135, 0));
+        colortable.add(133, new Color(135, 0, 135));
+        colortable.add(134, new Color(135, 87, 0));
+        colortable.add(135, new Color(0, 135, 135));
+        colortable.add(136, new Color(135, 135, 135));
+        colortable.add(137, new Color(0, 0, 135));
+        colortable.add(138, new Color(0, 135, 0));
+        colortable.add(139, new Color(135, 0, 0));
+        colortable.add(140, new Color(135, 135, 0));
+        colortable.add(141, new Color(135, 0, 135));
+        colortable.add(142, new Color(135, 87, 0));
+        colortable.add(143, new Color(0, 120, 120));
+        colortable.add(144, new Color(120, 120, 120));
+        colortable.add(145, new Color(0, 0, 120));
+        colortable.add(146, new Color(0, 120, 0));
+        colortable.add(147, new Color(120, 0, 0));
+        colortable.add(148, new Color(120, 120, 0));
+        colortable.add(149, new Color(120, 0, 120));
+        colortable.add(150, new Color(120, 82, 0));
+        colortable.add(151, new Color(0, 120, 120));
+        colortable.add(152, new Color(120, 120, 120));
+        colortable.add(153, new Color(0, 0, 120));
+        colortable.add(154, new Color(0, 120, 0));
+        colortable.add(155, new Color(120, 0, 0));
+        colortable.add(156, new Color(120, 120, 0));
+        colortable.add(157, new Color(120, 0, 120));
+        colortable.add(158, new Color(120, 82, 0));
+        colortable.add(159, new Color(0, 105, 105));
+        colortable.add(160, new Color(105, 105, 105));
+        colortable.add(161, new Color(0, 0, 105));
+        colortable.add(162, new Color(0, 105, 0));
+        colortable.add(163, new Color(105, 0, 0));
+        colortable.add(164, new Color(105, 105, 0));
+        colortable.add(165, new Color(105, 0, 105));
+        colortable.add(166, new Color(105, 77, 0));
+        colortable.add(167, new Color(0, 105, 105));
+        colortable.add(168, new Color(105, 105, 105));
+        colortable.add(169, new Color(0, 0, 105));
+        colortable.add(170, new Color(0, 105, 0));
+        colortable.add(171, new Color(105, 0, 0));
+        colortable.add(172, new Color(105, 105, 0));
+        colortable.add(173, new Color(105, 0, 105));
+        colortable.add(174, new Color(105, 77, 0));
+        colortable.add(175, new Color(0, 90, 90));
+        colortable.add(176, new Color(90, 90, 90));
+        colortable.add(177, new Color(0, 0, 90));
+        colortable.add(178, new Color(0, 90, 0));
+        colortable.add(179, new Color(90, 0, 0));
+        colortable.add(180, new Color(90, 90, 0));
+        colortable.add(181, new Color(90, 0, 90));
+        colortable.add(182, new Color(90, 72, 0));
+        colortable.add(183, new Color(0, 90, 90));
+        colortable.add(184, new Color(90, 90, 90));
+        colortable.add(185, new Color(0, 0, 90));
+        colortable.add(186, new Color(0, 90, 0));
+        colortable.add(187, new Color(90, 0, 0));
+        colortable.add(188, new Color(90, 90, 0));
+        colortable.add(189, new Color(90, 0, 90));
+        colortable.add(190, new Color(90, 72, 0));
+        colortable.add(191, new Color(0, 75, 75));
+        colortable.add(192, new Color(75, 75, 75));
+        colortable.add(193, new Color(0, 0, 75));
+        colortable.add(194, new Color(0, 75, 0));
+        colortable.add(195, new Color(75, 0, 0));
+        colortable.add(196, new Color(75, 75, 0));
+        colortable.add(197, new Color(75, 0, 75));
+        colortable.add(198, new Color(75, 67, 0));
+        colortable.add(199, new Color(0, 75, 75));
+        colortable.add(200, new Color(75, 75, 75));
+        colortable.add(201, new Color(0, 0, 75));
+        colortable.add(202, new Color(0, 75, 0));
+        colortable.add(203, new Color(75, 0, 0));
+        colortable.add(204, new Color(75, 75, 0));
+        colortable.add(205, new Color(75, 0, 75));
+        colortable.add(206, new Color(75, 67, 0));
+        colortable.add(207, new Color(0, 60, 60));
+        colortable.add(208, new Color(60, 60, 60));
+        colortable.add(209, new Color(0, 0, 60));
+        colortable.add(210, new Color(0, 60, 0));
+        colortable.add(211, new Color(60, 0, 0));
+        colortable.add(212, new Color(60, 60, 0));
+        colortable.add(213, new Color(60, 0, 60));
+        colortable.add(214, new Color(60, 62, 0));
+        colortable.add(215, new Color(0, 60, 60));
+        colortable.add(216, new Color(60, 60, 60));
+        colortable.add(217, new Color(0, 0, 60));
+        colortable.add(218, new Color(0, 60, 0));
+        colortable.add(219, new Color(60, 0, 0));
+        colortable.add(220, new Color(60, 60, 0));
+        colortable.add(221, new Color(60, 0, 60));
+        colortable.add(222, new Color(60, 62, 0));
+        colortable.add(223, new Color(0, 45, 45));
+        colortable.add(224, new Color(45, 45, 45));
+        colortable.add(225, new Color(0, 0, 45));
+        colortable.add(226, new Color(0, 45, 0));
+        colortable.add(227, new Color(45, 0, 0));
+        colortable.add(228, new Color(45, 45, 0));
+        colortable.add(229, new Color(45, 0, 45));
+        colortable.add(230, new Color(45, 57, 0));
+        colortable.add(231, new Color(0, 45, 45));
+        colortable.add(232, new Color(45, 45, 45));
+        colortable.add(233, new Color(0, 0, 45));
+        colortable.add(234, new Color(0, 45, 0));
+        colortable.add(235, new Color(45, 0, 0));
+        colortable.add(236, new Color(45, 45, 0));
+        colortable.add(237, new Color(45, 0, 45));
+        colortable.add(238, new Color(45, 57, 0));
+        colortable.add(239, new Color(0, 30, 30));
+        colortable.add(240, new Color(30, 30, 30));
+        colortable.add(241, new Color(0, 0, 30));
+        colortable.add(242, new Color(0, 30, 0));
+        colortable.add(243, new Color(30, 0, 0));
+        colortable.add(244, new Color(30, 30, 0));
+        colortable.add(245, new Color(30, 0, 30));
+        colortable.add(246, new Color(30, 52, 0));
+        colortable.add(247, new Color(0, 30, 30));
+        colortable.add(248, new Color(30, 30, 30));
+        colortable.add(249, new Color(0, 0, 30));
+        colortable.add(250, new Color(0, 30, 0));
+        colortable.add(251, new Color(30, 0, 0));
+        colortable.add(252, new Color(30, 30, 0));
+        colortable.add(253, new Color(30, 0, 30));
+        colortable.add(254, new Color(30, 52, 0));
+    }
+
+    public List findId(Color color)
+    {
+        ArrayList<Integer> codelist = new ArrayList<Integer>();
+        for (int i = 0; i < colortable.size(); i++)
+        {
+            Color colorDef = colortable.get(i);
+            if (colorDef.equals(color))
+            {
+                codelist.add(i);
+            }
+        }
+        return codelist;
+    }
+
+    public Color getColor(int i)
+    {
+        return colortable.get(i);
+    }
+
+    public String getColorCode(int i)
+    {
+        Color color = colortable.get(i);
+        if (!color.equals(Color.WHITE))
+            return colorToString(colortable.get(i));
+        return colorToString(Color.GRAY);
+    }
+
+    public boolean contain(Color color)
+    {
+        for (Color colorDef : colortable)
+        {
+            if (colorDef.equals(color))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String colorToString(Color c) {
+		char[] buf = new char[7];
+		buf[0] = '#';
+		String s = Integer.toHexString(c.getRed());
+		if (s.length() == 1) {
+			buf[1] = '0';
+			buf[2] = s.charAt(0);
+		}
+		else {
+			buf[1] = s.charAt(0);
+			buf[2] = s.charAt(1);
+		}
+		s = Integer.toHexString(c.getGreen());
+		if (s.length() == 1) {
+			buf[3] = '0';
+			buf[4] = s.charAt(0);
+		}
+		else {
+			buf[3] = s.charAt(0);
+			buf[4] = s.charAt(1);
+		}
+		s = Integer.toHexString(c.getBlue());
+		if (s.length() == 1) {
+			buf[5] = '0';
+			buf[6] = s.charAt(0);
+		}
+		else {
+			buf[5] = s.charAt(0);
+			buf[6] = s.charAt(1);
+		}
+		return String.valueOf(buf);
+	}
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java
new file mode 100644
index 0000000..ebdf0d2
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java
@@ -0,0 +1,30 @@
+package com.ximple.eofms.util;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �W�� 01:20:20
+ * To change this template use File | Settings | File Templates.
+ */
+public final class GeomUtil
+{
+    public static double convertLogicalValue(double value)
+    {
+        return value / 1000.0 + 2147483.648;
+    }
+
+    public static Coordinate convertLogicalCooridate(Coordinate value)
+    {
+        return new Coordinate(convertLogicalValue(value.x), convertLogicalValue(value.y), convertLogicalValue(value.z));
+    }
+
+    public static Envelope convertLogicalEnvelope(Envelope value)
+    {
+        return new Envelope(convertLogicalValue(value.getMinX()), convertLogicalValue(value.getMaxX()),
+                            convertLogicalValue(value.getMinY()), convertLogicalValue(value.getMaxY()));
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java
new file mode 100644
index 0000000..c8c933d
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/LangUtil.java
@@ -0,0 +1,104 @@
+package com.ximple.eofms.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Ulysses
+ * Date: 2007/6/15
+ * Time: �W�� 01:21:25
+ * To change this template use File | Settings | File Templates.
+ */
+public class LangUtil
+{
+    private static Map primitiveToWrapperMap = new HashMap()
+    {
+
+        {
+            put(byte.class, Byte.class);
+            put(char.class, Character.class);
+            put(short.class, Short.class);
+            put(int.class, Integer.class);
+            put(long.class, Long.class);
+            put(float.class, Float.class);
+            put(double.class, Double.class);
+            put(boolean.class, Boolean.class);
+        }
+    };
+
+    public static String emptyStringIfNull(String s)
+    {
+        return (s == null) ? "" : s;
+    }
+
+    /**
+     * Useful because an expression used to generate o need only be
+     * evaluated once.
+     */
+    public static Object ifNull(Object o, Object alternative)
+    {
+        return (o == null) ? alternative : o;
+    }
+
+    public static Object ifNotNull(Object o, Object alternative)
+    {
+        return (o != null) ? alternative : o;
+    }
+
+    public static Class toPrimitiveWrapperClass(Class primitiveClass)
+    {
+        return (Class) primitiveToWrapperMap.get(primitiveClass);
+    }
+
+    public static boolean isPrimitive(Class c)
+    {
+        return primitiveToWrapperMap.containsKey(c);
+    }
+
+    public static boolean bothNullOrEqual(Object a, Object b)
+    {
+        return (a == null && b == null) || (a != null && b != null && a.equals(b));
+    }
+
+    public static Object newInstance(Class c)
+    {
+        try
+        {
+            return c.newInstance();
+        } catch (Exception e)
+        {
+            Assert.shouldNeverReachHere(e.toString());
+            return null;
+        }
+    }
+
+    public static Collection classesAndInterfaces(Class c)
+    {
+        ArrayList classesAndInterfaces = new ArrayList();
+        classesAndInterfaces.add(c);
+        superclasses(c, classesAndInterfaces);
+        for (Iterator i = new ArrayList(classesAndInterfaces).iterator(); i.hasNext();)
+        {
+            Class x = (Class) i.next();
+            classesAndInterfaces.addAll(Arrays.asList(x.getInterfaces()));
+        }
+        return classesAndInterfaces;
+    }
+
+    private static void superclasses(Class c, Collection results)
+    {
+        if (c.getSuperclass() == null)
+        {
+            return;
+        }
+        results.add(c.getSuperclass());
+        superclasses(c.getSuperclass(), results);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
new file mode 100644
index 0000000..b7a6ade
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
@@ -0,0 +1,4774 @@
+//
+//(c) 2000 Sun Microsystems, Inc.
+//ALL RIGHTS RESERVED
+//
+//License Grant-
+//
+//
+//Permission to use, copy, modify, and distribute this Software and its
+//documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
+//hereby granted.
+//
+//This Software is provided "AS IS".  All express warranties, including any
+//implied warranty of merchantability, satisfactory quality, fitness for a
+//particular purpose, or non-infringement, are disclaimed, except to the extent
+//that such disclaimers are held to be legally invalid.
+//
+//You acknowledge that Software is not designed, licensed or intended for use in
+//the design, construction, operation or maintenance of any nuclear facility
+//("High Risk Activities").  Sun disclaims any express or implied warranty of
+//fitness for such uses.
+//
+//Please refer to the file http://www.sun.com/policies/trademarks/ for further
+//important trademark information and to
+//http://java.sun.com/nav/business/index.html for further important licensing
+//information for the Java Technology.
+//
+
+package com.ximple.eofms.util;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.text.DecimalFormatSymbols;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Vector;
+
+/**
+ * PrintfFormat allows the formatting of an array of
+ * objects embedded within a string.  Primitive types
+ * must be passed using wrapper types.  The formatting
+ * is controlled by a control string.
+ * <p>
+ * A control string is a Java string that contains a
+ * control specification.  The control specification
+ * starts at the first percent sign (%) in the string,
+ * provided that this percent sign
+ * <ol>
+ * <li>is not escaped protected by a matching % or is
+ * not an escape % character,
+ * <li>is not at the end of the format string, and
+ * <li>precedes a sequence of characters that parses as
+ * a valid control specification.
+ * </ol>
+ * </p><p>
+ * A control specification usually takes the form:
+ * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+ *                { [hlL] }+ [idfgGoxXeEcs]
+ * </pre>
+ * There are variants of this basic form that are
+ * discussed below.</p>
+ * <p>
+ * The format is composed of zero or more directives
+ * defined as follows:
+ * <ul>
+ * <li>ordinary characters, which are simply copied to
+ * the output stream;
+ * <li>escape sequences, which represent non-graphic
+ * characters; and
+ * <li>conversion specifications,  each of which
+ * results in the fetching of zero or more arguments.
+ * </ul></p>
+ * <p>
+ * The results are undefined if there are insufficient
+ * arguments for the format.  Usually an unchecked
+ * exception will be thrown.  If the format is
+ * exhausted while arguments remain, the excess
+ * arguments are evaluated but are otherwise ignored.
+ * In format strings containing the % form of
+ * conversion specifications, each argument in the
+ * argument list is used exactly once.</p>
+ * <p>
+ * Conversions can be applied to the <code>n</code>th
+ * argument after the format in the argument list,
+ * rather than to the next unused argument.  In this
+ * case, the conversion characer % is replaced by the
+ * sequence %<code>n</code>$, where <code>n</code> is
+ * a decimal integer giving the position of the
+ * argument in the argument list.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of conversion specifications, each argument
+ * in the argument list is used exactly once.</p>
+ *
+ * <h4>Escape Sequences</h4>
+ * <p>
+ * The following table lists escape sequences and
+ * associated actions on display devices capable of
+ * the action.
+ * <table>
+ * <tr><th align=left>Sequence</th>
+ *    <th align=left>Name</th>
+ *    <th align=left>Description</th></tr>
+ * <tr><td>\\</td><td>backlash</td><td>None.
+ * </td></tr>
+ * <tr><td>\a</td><td>alert</td><td>Attempts to alert
+ *          the user through audible or visible
+ *          notification.
+ * </td></tr>
+ * <tr><td>\b</td><td>backspace</td><td>Moves the
+ *          printing position to one column before
+ *          the current position, unless the
+ *          current position is the start of a line.
+ * </td></tr>
+ * <tr><td>\f</td><td>form-feed</td><td>Moves the
+ *          printing position to the initial
+ *          printing position of the next logical
+ *          page.
+ * </td></tr>
+ * <tr><td>\n</td><td>newline</td><td>Moves the
+ *          printing position to the start of the
+ *          next line.
+ * </td></tr>
+ * <tr><td>\r</td><td>carriage-return</td><td>Moves
+ *          the printing position to the start of
+ *          the current line.
+ * </td></tr>
+ * <tr><td>\t</td><td>tab</td><td>Moves the printing
+ *          position to the next implementation-
+ *          defined horizontal tab position.
+ * </td></tr>
+ * <tr><td>\v</td><td>vertical-tab</td><td>Moves the
+ *          printing position to the start of the
+ *          next implementation-defined vertical
+ *          tab position.
+ * </td></tr>
+ * </table></p>
+ * <h4>Conversion Specifications</h4>
+ * <p>
+ * Each conversion specification is introduced by
+ * the percent sign character (%).  After the character
+ * %, the following appear in sequence:</p>
+ * <p>
+ * Zero or more flags (in any order), which modify the
+ * meaning of the conversion specification.</p>
+ * <p>
+ * An optional minimum field width.  If the converted
+ * value has fewer characters than the field width, it
+ * will be padded with spaces by default on the left;
+ * t will be padded on the right, if the left-
+ * adjustment flag (-), described below, is given to
+ * the field width.  The field width takes the form
+ * of a decimal integer.  If the conversion character
+ * is s, the field width is the the minimum number of
+ * characters to be printed.</p>
+ * <p>
+ * An optional precision that gives the minumum number
+ * of digits to appear for the d, i, o, x or X
+ * conversions (the field is padded with leading
+ * zeros); the number of digits to appear after the
+ * radix character for the e, E, and f conversions,
+ * the maximum number of significant digits for the g
+ * and G conversions; or the maximum number of
+ * characters to be written from a string is s and S
+ * conversions.  The precision takes the form of an
+ * optional decimal digit string, where a null digit
+ * string is treated as 0.  If a precision appears
+ * with a c conversion character the precision is
+ * ignored.
+ * </p>
+ * <p>
+ * An optional h specifies that a following d, i, o,
+ * x, or X conversion character applies to a type
+ * short argument (the argument will be promoted
+ * according to the integral promotions and its value
+ * converted to type short before printing).</p>
+ * <p>
+ * An optional l (ell) specifies that a following
+ * d, i, o, x, or X conversion character applies to a
+ * type long argument.</p>
+ * <p>
+ * A field width or precision may be indicated by an
+ * asterisk (*) instead of a digit string.  In this
+ * case, an integer argument supplised the field width
+ * precision.  The argument that is actually converted
+ * is not fetched until the conversion letter is seen,
+ * so the the arguments specifying field width or
+ * precision must appear before the argument (if any)
+ * to be converted.  If the precision argument is
+ * negative, it will be changed to zero.  A negative
+ * field width argument is taken as a - flag, followed
+ * by a positive field width.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of a conversion specification, a field width
+ * or precision may be indicated by the sequence
+ * *<code>m</code>$, where m is a decimal integer
+ * giving the position in the argument list (after the
+ * format argument) of an integer argument containing
+ * the field width or precision.</p>
+ * <p>
+ * The format can contain either numbered argument
+ * specifications (that is, %<code>n</code>$ and
+ * *<code>m</code>$), or unnumbered argument
+ * specifications (that is % and *), but normally not
+ * both.  The only exception to this is that %% can
+ * be mixed with the %<code>n</code>$ form.  The
+ * results of mixing numbered and unnumbered argument
+ * specifications in a format string are undefined.</p>
+ *
+ * <h4>Flag Characters</h4>
+ * <p>
+ * The flags and their meanings are:</p>
+ * <dl>
+ * <dt>'<dd> integer portion of the result of a
+ *      decimal conversion (%i, %d, %f, %g, or %G) will
+ *      be formatted with thousands' grouping
+ *      characters.  For other conversions the flag
+ *      is ignored.  The non-monetary grouping
+ *      character is used.
+ * <dt>-<dd> result of the conversion is left-justified
+ *      within the field.  (It will be right-justified
+ *      if this flag is not specified).</td></tr>
+ * <dt>+<dd> result of a signed conversion always
+ *      begins with a sign (+ or -).  (It will begin
+ *      with a sign only when a negative value is
+ *      converted if this flag is not specified.)
+ * <dt>&lt;space&gt;<dd> If the first character of a
+ *      signed conversion is not a sign, a space
+ *      character will be placed before the result.
+ *      This means that if the space character and +
+ *      flags both appear, the space flag will be
+ *      ignored.
+ * <dt>#<dd> value is to be converted to an alternative
+ *      form.  For c, d, i, and s conversions, the flag
+ *      has no effect.  For o conversion, it increases
+ *      the precision to force the first digit of the
+ *      result to be a zero.  For x or X conversion, a
+ *      non-zero result has 0x or 0X prefixed to it,
+ *      respectively.  For e, E, f, g, and G
+ *      conversions, the result always contains a radix
+ *      character, even if no digits follow the radix
+ *      character (normally, a decimal point appears in
+ *      the result of these conversions only if a digit
+ *      follows it).  For g and G conversions, trailing
+ *      zeros will not be removed from the result as
+ *      they normally are.
+ * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G
+ *      conversions, leading zeros (following any
+ *      indication of sign or base) are used to pad to
+ *      the field width;  no space padding is
+ *      performed.  If the 0 and - flags both appear,
+ *      the 0 flag is ignored.  For d, i, o, x, and X
+ *      conversions, if a precision is specified, the
+ *      0 flag will be ignored. For c conversions,
+ *      the flag is ignored.
+ * </dl>
+ *
+ * <h4>Conversion Characters</h4>
+ * <p>
+ * Each conversion character results in fetching zero
+ * or more arguments.  The results are undefined if
+ * there are insufficient arguments for the format.
+ * Usually, an unchecked exception will be thrown.
+ * If the format is exhausted while arguments remain,
+ * the excess arguments are ignored.</p>
+ *
+ * <p>
+ * The conversion characters and their meanings are:
+ * </p>
+ * <dl>
+ * <dt>d,i<dd>The int argument is converted to a
+ *        signed decimal in the style [-]dddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>o<dd> The int argument is converted to unsigned
+ *        octal format in the style ddddd.  The
+ *        precision specifies the minimum number of
+ *        digits to appear;  if the value being
+ *        converted can be represented in fewer
+ *        digits, it will be expanded with leading
+ *        zeros.  The default precision is 1.  The
+ *        result of converting 0 with an explicit
+ *        precision of 0 is no characters.
+ * <dt>x<dd> The int argument is converted to unsigned
+ *        hexadecimal format in the style dddd;  the
+ *        letters abcdef are used.  The precision
+ *        specifies the minimum numberof digits to
+ *        appear; if the value being converted can be
+ *        represented in fewer digits, it will be
+ *        expanded with leading zeros.  The default
+ *        precision is 1.  The result of converting 0
+ *        with an explicit precision of 0 is no
+ *        characters.
+ * <dt>X<dd> Behaves the same as the x conversion
+ *        character except that letters ABCDEF are
+ *        used instead of abcdef.
+ * <dt>f<dd> The floating point number argument is
+ *        written in decimal notation in the style
+ *        [-]ddd.ddd, where the number of digits after
+ *        the radix character (shown here as a decimal
+ *        point) is equal to the precision
+ *        specification.  A Locale is used to determine
+ *        the radix character to use in this format.
+ *        If the precision is omitted from the
+ *        argument, six digits are written after the
+ *        radix character;  if the precision is
+ *        explicitly 0 and the # flag is not specified,
+ *        no radix character appears.  If a radix
+ *        character appears, at least 1 digit appears
+ *        before it.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>e,E<dd>The floating point number argument is
+ *        written in the style [-]d.ddde{+-}dd
+ *        (the symbols {+-} indicate either a plus or
+ *        minus sign), where there is one digit before
+ *        the radix character (shown here as a decimal
+ *        point) and the number of digits after it is
+ *        equal to the precision.  A Locale is used to
+ *        determine the radix character to use in this
+ *        format.  When the precision is missing, six
+ *        digits are written after the radix character;
+ *        if the precision is 0 and the # flag is not
+ *        specified, no radix character appears.  The
+ *        E conversion will produce a number with E
+ *        instead of e introducing the exponent.  The
+ *        exponent always contains at least two digits.
+ *        However, if the value to be written requires
+ *        an exponent greater than two digits,
+ *        additional exponent digits are written as
+ *        necessary.  The value is rounded to the
+ *        appropriate number of digits.
+ * <dt>g,G<dd>The floating point number argument is
+ *        written in style f or e (or in sytle E in the
+ *        case of a G conversion character), with the
+ *        precision specifying the number of
+ *        significant digits.  If the precision is
+ *        zero, it is taken as one.  The style used
+ *        depends on the value converted:  style e
+ *        (or E) will be used only if the exponent
+ *        resulting from the conversion is less than
+ *        -4 or greater than or equal to the precision.
+ *        Trailing zeros are removed from the result.
+ *        A radix character appears only if it is
+ *        followed by a digit.
+ * <dt>c,C<dd>The integer argument is converted to a
+ *        char and the result is written.
+ *
+ * <dt>s,S<dd>The argument is taken to be a string and
+ *        bytes from the string are written until the
+ *        end of the string or the number of bytes
+ *        indicated by the precision specification of
+ *        the argument is reached.  If the precision
+ *        is omitted from the argument, it is taken to
+ *        be infinite, so all characters up to the end
+ *        of the string are written.
+ * <dt>%<dd>Write a % character;  no argument is
+ *        converted.
+ * </dl>
+ * <p>
+ * If a conversion specification does not match one of
+ * the above forms, an IllegalArgumentException is
+ * thrown and the instance of PrintfFormat is not
+ * created.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for infinity, the output is
+ * [+]Infinity, where Infinity is either Infinity or
+ * Inf, depending on the desired output string length.
+ * Printing of the sign follows the rules described
+ * above.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for "not-a-number," the output is
+ * [+]NaN.  Printing of the sign follows the rules
+ * described above.</p>
+ * <p>
+ * In no case does a non-existent or small field width
+ * cause truncation of a field;  if the result of a
+ * conversion is wider than the field width, the field
+ * is simply expanded to contain the conversion result.
+ * </p>
+ * <p>
+ * The behavior is like printf.  One exception is that
+ * the minimum number of exponent digits is 3 instead
+ * of 2 for e and E formats when the optional L is used
+ * before the e, E, g, or G conversion character.  The
+ * optional L does not imply conversion to a long long
+ * double. </p>
+ * <p>
+ * The biggest divergence from the C printf
+ * specification is in the use of 16 bit characters.
+ * This allows the handling of characters beyond the
+ * small ASCII character set and allows the utility to
+ * interoperate correctly with the rest of the Java
+ * runtime environment.</p>
+ * <p>
+ * Omissions from the C printf specification are
+ * numerous.  All the known omissions are present
+ * because Java never uses bytes to represent
+ * characters and does not have pointers:</p>
+ * <ul>
+ * <li>%c is the same as %C.
+ * <li>%s is the same as %S.
+ * <li>u, p, and n conversion characters.
+ * <li>%ws format.
+ * <li>h modifier applied to an n conversion character.
+ * <li>l (ell) modifier applied to the c, n, or s
+ * conversion characters.
+ * <li>ll (ell ell) modifier to d, i, o, u, x, or X
+ * conversion characters.
+ * <li>ll (ell ell) modifier to an n conversion
+ * character.
+ * <li>c, C, d,i,o,u,x, and X conversion characters
+ * apply to Byte, Character, Short, Integer, Long
+ * types.
+ * <li>f, e, E, g, and G conversion characters apply
+ * to Float and Double types.
+ * <li>s and S conversion characters apply to String
+ * types.
+ * <li>All other reference types can be formatted
+ * using the s or S conversion characters only.
+ * </ul>
+ * <p>
+ * Most of this specification is quoted from the Unix
+ * man page for the sprintf utility.</p>
+ *
+ * @author Allan Jacobs
+ * @version 1
+ * Release 1: Initial release.
+ * Release 2: Asterisk field widths and precisions
+ *            %n$ and *m$
+ *            Bug fixes
+ *              g format fix (2 digits in e form corrupt)
+ *              rounding in f format implemented
+ *              round up when digit not printed is 5
+ *              formatting of -0.0f
+ *              round up/down when last digits are 50000...
+ */
+public final class PrintfFormat
+{
+    /** Vector of control strings and format literals. */
+    private Vector vFmt = new Vector();
+
+    /** Character position.  Used by the constructor. */
+    private int cPos = 0;
+
+    /** Character position.  Used by the constructor. */
+    private DecimalFormatSymbols dfs = null;
+
+    /**
+     * Constructs an array of control specifications
+     * possibly preceded, separated, or followed by
+     * ordinary strings.  Control strings begin with
+     * unpaired percent signs.  A pair of successive
+     * percent signs designates a single percent sign in
+     * the format.
+     * @param fmtArg  Control string.
+     * @exception IllegalArgumentException if the control
+     * string is null, zero length, or otherwise
+     * malformed.
+     */
+    public PrintfFormat(String fmtArg) throws IllegalArgumentException
+    {
+        this(Locale.getDefault(), fmtArg);
+    }
+
+    /**
+     * Constructs an array of control specifications
+     * possibly preceded, separated, or followed by
+     * ordinary strings.  Control strings begin with
+     * unpaired percent signs.  A pair of successive
+     * percent signs designates a single percent sign in
+     * the format.
+     * @param fmtArg  Control string.
+     * @exception IllegalArgumentException if the control
+     * string is null, zero length, or otherwise
+     * malformed.
+     */
+    public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException
+    {
+        dfs = new DecimalFormatSymbols(locale);
+
+        int                     ePos = 0;
+        ConversionSpecification sFmt = null;
+        String                  unCS = this.nonControl(fmtArg, 0);
+
+        if (unCS != null)
+        {
+            sFmt = new ConversionSpecification();
+            sFmt.setLiteral(unCS);
+            vFmt.addElement(sFmt);
+        }
+
+        while ((cPos != -1) && (cPos < fmtArg.length()))
+        {
+            for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++)
+            {
+                char c = 0;
+
+                c = fmtArg.charAt(ePos);
+
+                if (c == 'i')
+                {
+                    break;
+                }
+
+                if (c == 'd')
+                {
+                    break;
+                }
+
+                if (c == 'f')
+                {
+                    break;
+                }
+
+                if (c == 'g')
+                {
+                    break;
+                }
+
+                if (c == 'G')
+                {
+                    break;
+                }
+
+                if (c == 'o')
+                {
+                    break;
+                }
+
+                if (c == 'x')
+                {
+                    break;
+                }
+
+                if (c == 'X')
+                {
+                    break;
+                }
+
+                if (c == 'e')
+                {
+                    break;
+                }
+
+                if (c == 'E')
+                {
+                    break;
+                }
+
+                if (c == 'c')
+                {
+                    break;
+                }
+
+                if (c == 's')
+                {
+                    break;
+                }
+
+                if (c == '%')
+                {
+                    break;
+                }
+            }
+
+            ePos = Math.min(ePos + 1, fmtArg.length());
+            sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos));
+            vFmt.addElement(sFmt);
+            unCS = this.nonControl(fmtArg, ePos);
+
+            if (unCS != null)
+            {
+                sFmt = new ConversionSpecification();
+                sFmt.setLiteral(unCS);
+                vFmt.addElement(sFmt);
+            }
+        }
+    }
+
+    /**
+     * Return a substring starting at
+     * <code>start</code> and ending at either the end
+     * of the String <code>s</code>, the next unpaired
+     * percent sign, or at the end of the String if the
+     * last character is a percent sign.
+     * @param s  Control string.
+     * @param start Position in the string
+     *     <code>s</code> to begin looking for the start
+     *     of a control string.
+     * @return the substring from the start position
+     *     to the beginning of the control string.
+     */
+    private String nonControl(String s, int start)
+    {
+        String ret = "";
+
+        cPos = s.indexOf("%", start);
+
+        if (cPos == -1)
+        {
+            cPos = s.length();
+        }
+
+        return s.substring(start, cPos);
+    }
+
+    /**
+     * Format an array of objects.  Byte, Short,
+     * Integer, Long, Float, Double, and Character
+     * arguments are treated as wrappers for primitive
+     * types.
+     * @param o The array of objects to format.
+     * @return  The formatted String.
+     */
+    public String sprintf(Object[] o)
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        int                     i  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                if (cs.isPositionalSpecification())
+                {
+                    i = cs.getArgumentPosition() - 1;
+
+                    if (cs.isPositionalFieldWidth())
+                    {
+                        int ifw = cs.getArgumentPositionForFieldWidth() - 1;
+
+                        cs.setFieldWidthWithArg(((Integer) o[ifw]).intValue());
+                    }
+
+                    if (cs.isPositionalPrecision())
+                    {
+                        int ipr = cs.getArgumentPositionForPrecision() - 1;
+
+                        cs.setPrecisionWithArg(((Integer) o[ipr]).intValue());
+                    }
+                } else
+                {
+                    if (cs.isVariableFieldWidth())
+                    {
+                        cs.setFieldWidthWithArg(((Integer) o[i]).intValue());
+                        i++;
+                    }
+
+                    if (cs.isVariablePrecision())
+                    {
+                        cs.setPrecisionWithArg(((Integer) o[i]).intValue());
+                        i++;
+                    }
+                }
+
+                if (o[i] instanceof Byte)
+                {
+                    sb.append(cs.internalsprintf(((Byte) o[i]).byteValue()));
+                } else if (o[i] instanceof Short)
+                {
+                    sb.append(cs.internalsprintf(((Short) o[i]).shortValue()));
+                } else if (o[i] instanceof Integer)
+                {
+                    sb.append(cs.internalsprintf(((Integer) o[i]).intValue()));
+                } else if (o[i] instanceof Long)
+                {
+                    sb.append(cs.internalsprintf(((Long) o[i]).longValue()));
+                } else if (o[i] instanceof Float)
+                {
+                    sb.append(cs.internalsprintf(((Float) o[i]).floatValue()));
+                } else if (o[i] instanceof Double)
+                {
+                    sb.append(cs.internalsprintf(((Double) o[i]).doubleValue()));
+                } else if (o[i] instanceof Character)
+                {
+                    sb.append(cs.internalsprintf(((Character) o[i]).charValue()));
+                } else if (o[i] instanceof String)
+                {
+                    sb.append(cs.internalsprintf((String) o[i]));
+                } else
+                {
+                    sb.append(cs.internalsprintf(o[i]));
+                }
+
+                if (!cs.isPositionalSpecification())
+                {
+                    i++;
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format nothing.  Just use the control string.
+     * @return  the formatted String.
+     */
+    public String sprintf()
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an int.
+     * @param x The int to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, G, s,
+     *     or S.
+     */
+    public String sprintf(int x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an long.
+     * @param x The long to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is f, e, E, g, G, s,
+     *     or S.
+     */
+    public String sprintf(long x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format a double.
+     * @param x The double to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *     conversion character is c, C, s, S,
+     *     d, d, x, X, or o.
+     */
+    public String sprintf(double x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format a String.
+     * @param x The String to format.
+     * @return  The formatted String.
+     * @exception IllegalArgumentException if the
+     *   conversion character is neither s nor S.
+     */
+    public String sprintf(String x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                sb.append(cs.internalsprintf(x));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Format an Object.  Convert wrapper types to
+     * their primitive equivalents and call the
+     * appropriate internal formatting method. Convert
+     * Strings using an internal formatting method for
+     * Strings. Otherwise use the default formatter
+     * (use toString).
+     * @param x the Object to format.
+     * @return  the formatted String.
+     * @exception IllegalArgumentException if the
+     *    conversion character is inappropriate for
+     *    formatting an unwrapped value.
+     */
+    public String sprintf(Object x) throws IllegalArgumentException
+    {
+        Enumeration             e  = vFmt.elements();
+        ConversionSpecification cs = null;
+        char                    c  = 0;
+        StringBuffer            sb = new StringBuffer();
+
+        while (e.hasMoreElements())
+        {
+            cs = (ConversionSpecification) e.nextElement();
+            c  = cs.getConversionCharacter();
+
+            if (c == '\0')
+            {
+                sb.append(cs.getLiteral());
+            } else if (c == '%')
+            {
+                sb.append("%");
+            } else
+            {
+                if (x instanceof Byte)
+                {
+                    sb.append(cs.internalsprintf(((Byte) x).byteValue()));
+                } else if (x instanceof Short)
+                {
+                    sb.append(cs.internalsprintf(((Short) x).shortValue()));
+                } else if (x instanceof Integer)
+                {
+                    sb.append(cs.internalsprintf(((Integer) x).intValue()));
+                } else if (x instanceof Long)
+                {
+                    sb.append(cs.internalsprintf(((Long) x).longValue()));
+                } else if (x instanceof Float)
+                {
+                    sb.append(cs.internalsprintf(((Float) x).floatValue()));
+                } else if (x instanceof Double)
+                {
+                    sb.append(cs.internalsprintf(((Double) x).doubleValue()));
+                } else if (x instanceof Character)
+                {
+                    sb.append(cs.internalsprintf(((Character) x).charValue()));
+                } else if (x instanceof String)
+                {
+                    sb.append(cs.internalsprintf((String) x));
+                } else
+                {
+                    sb.append(cs.internalsprintf(x));
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * <p>
+     * ConversionSpecification allows the formatting of
+     * a single primitive or object embedded within a
+     * string.  The formatting is controlled by a
+     * format string.  Only one Java primitive or
+     * object can be formatted at a time.
+     * <p>
+     * A format string is a Java string that contains
+     * a control string.  The control string starts at
+     * the first percent sign (%) in the string,
+     * provided that this percent sign
+     * <ol>
+     * <li>is not escaped protected by a matching % or
+     *     is not an escape % character,
+     * <li>is not at the end of the format string, and
+     * <li>precedes a sequence of characters that parses
+     *     as a valid control string.
+     * </ol>
+     * <p>
+     * A control string takes the form:
+     * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+     *                { [hlL] }+ [idfgGoxXeEcs]
+     * </pre>
+     * <p>
+     * The behavior is like printf.  One (hopefully the
+     * only) exception is that the minimum number of
+     * exponent digits is 3 instead of 2 for e and E
+     * formats when the optional L is used before the
+     * e, E, g, or G conversion character.  The
+     * optional L does not imply conversion to a long
+     * long double.
+     */
+    private class ConversionSpecification
+    {
+        /** Default precision. */
+        private final static int defaultDigits = 6;
+
+        /**
+         * The integer portion of the result of a decimal
+         * conversion (i, d, u, f, g, or G) will be
+         * formatted with thousands' grouping characters.
+         * For other conversions the flag is ignored.
+         */
+        private boolean thousands = false;
+
+        /**
+         * The result of the conversion will be
+         * left-justified within the field.
+         */
+        private boolean leftJustify = false;
+
+        /**
+         * The result of a signed conversion will always
+         * begin with a sign (+ or -).
+         */
+        private boolean leadingSign = false;
+
+        /**
+         * Flag indicating that left padding with spaces is
+         * specified.
+         */
+        private boolean leadingSpace = false;
+
+        /**
+         * For an o conversion, increase the precision to
+         * force the first digit of the result to be a
+         * zero.  For x (or X) conversions, a non-zero
+         * result will have 0x (or 0X) prepended to it.
+         * For e, E, f, g, or G conversions, the result
+         * will always contain a radix character, even if
+         * no digits follow the point.  For g and G
+         * conversions, trailing zeros will not be removed
+         * from the result.
+         */
+        private boolean alternateForm = false;
+
+        /**
+         * Flag indicating that left padding with zeroes is
+         * specified.
+         */
+        private boolean leadingZeros = false;
+
+        /**
+         * Flag indicating that the field width is *.
+         */
+        private boolean variableFieldWidth = false;
+
+        /**
+         * If the converted value has fewer bytes than the
+         * field width, it will be padded with spaces or
+         * zeroes.
+         */
+        private int fieldWidth = 0;
+
+        /**
+         * Flag indicating whether or not the field width
+         * has been set.
+         */
+        private boolean fieldWidthSet = false;
+
+        /**
+         * The minimum number of digits to appear for the
+         * d, i, o, u, x, or X conversions.  The number of
+         * digits to appear after the radix character for
+         * the e, E, and f conversions.  The maximum number
+         *  of significant digits for the g and G
+         * conversions.  The maximum number of bytes to be
+         * printed from a string in s and S conversions.
+         */
+        private int precision = 0;
+
+        /**
+         * Flag indicating that the precision is *.
+         */
+        private boolean variablePrecision = false;
+
+        /**
+         * Flag indicating whether or not the precision has
+         * been set.
+         */
+        private boolean precisionSet = false;
+
+        /*
+         */
+        private boolean positionalSpecification       = false;
+        private int     argumentPosition              = 0;
+        private boolean positionalFieldWidth          = false;
+        private int     argumentPositionForFieldWidth = 0;
+        private boolean positionalPrecision           = false;
+        private int     argumentPositionForPrecision  = 0;
+
+        /**
+         * Flag specifying that a following d, i, o, u, x,
+         * or X conversion character applies to a type
+         * short int.
+         */
+        private boolean optionalh = false;
+
+        /**
+         * Flag specifying that a following d, i, o, u, x,
+         * or X conversion character applies to a type lont
+         * int argument.
+         */
+        private boolean optionall = false;
+
+        /**
+         * Flag specifying that a following e, E, f, g, or
+         * G conversion character applies to a type double
+         * argument.  This is a noop in Java.
+         */
+        private boolean optionalL = false;
+
+        /** Control string type. */
+        private char conversionCharacter = '\0';
+
+        /**
+         * Position within the control string.  Used by
+         * the constructor.
+         */
+        private int pos = 0;
+
+        /** Literal or control format string. */
+        private String fmt;
+
+        /**
+         * Constructor.  Used to prepare an instance
+         * to hold a literal, not a control string.
+         */
+        ConversionSpecification()
+        {
+        }
+
+        /**
+         * Constructor for a conversion specification.
+         * The argument must begin with a % and end
+         * with the conversion character for the
+         * conversion specification.
+         *  @param fmtArg  String specifying the
+         *     conversion specification.
+         *  @exception IllegalArgumentException if the
+         *     input string is null, zero length, or
+         *     otherwise malformed.
+         */
+        ConversionSpecification(String fmtArg) throws IllegalArgumentException
+        {
+            if (fmtArg == null)
+            {
+                throw new NullPointerException();
+            }
+
+            if (fmtArg.length() == 0)
+            {
+                throw new IllegalArgumentException("Control strings must have positive" + " lengths.");
+            }
+
+            if (fmtArg.charAt(0) == '%')
+            {
+                fmt = fmtArg;
+                pos = 1;
+                setArgPosition();
+                setFlagCharacters();
+                setFieldWidth();
+                setPrecision();
+                setOptionalHL();
+
+                if (setConversionCharacter())
+                {
+                    if (pos == fmtArg.length())
+                    {
+                        if (leadingZeros && leftJustify)
+                        {
+                            leadingZeros = false;
+                        }
+
+                        if (precisionSet && leadingZeros)
+                        {
+                            if ((conversionCharacter == 'd') || (conversionCharacter == 'i') || (conversionCharacter == 'o')
+                                || (conversionCharacter == 'x'))
+                            {
+                                leadingZeros = false;
+                            }
+                        }
+                    } else
+                    {
+                        throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+                    }
+                } else
+                {
+                    throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+                }
+            } else
+            {
+                throw new IllegalArgumentException("Control strings must begin with %.");
+            }
+        }
+
+        /**
+         * Set the String for this instance.
+         * @param s the String to store.
+         */
+        void setLiteral(String s)
+        {
+            fmt = s;
+        }
+
+        /**
+         * Get the String for this instance.  Translate
+         * any escape sequences.
+         *
+         * @return s the stored String.
+         */
+        String getLiteral()
+        {
+            StringBuffer sb = new StringBuffer();
+            int          i  = 0;
+
+            while (i < fmt.length())
+            {
+                if (fmt.charAt(i) == '\\')
+                {
+                    i++;
+
+                    if (i < fmt.length())
+                    {
+                        char c = fmt.charAt(i);
+
+                        switch (c)
+                        {
+                        case 'a' :
+                            sb.append((char) 0x07);
+
+                            break;
+
+                        case 'b' :
+                            sb.append('\b');
+
+                            break;
+
+                        case 'f' :
+                            sb.append('\f');
+
+                            break;
+
+                        case 'n' :
+                            sb.append(System.getProperty("line.separator"));
+
+                            break;
+
+                        case 'r' :
+                            sb.append('\r');
+
+                            break;
+
+                        case 't' :
+                            sb.append('\t');
+
+                            break;
+
+                        case 'v' :
+                            sb.append((char) 0x0b);
+
+                            break;
+
+                        case '\\' :
+                            sb.append('\\');
+
+                            break;
+                        }
+
+                        i++;
+                    } else
+                    {
+                        sb.append('\\');
+                    }
+                } else
+                {
+                    i++;
+                }
+            }
+
+            return fmt;
+        }
+
+        /**
+         * Get the conversion character that tells what
+         * type of control character this instance has.
+         *
+         * @return the conversion character.
+         */
+        char getConversionCharacter()
+        {
+            return conversionCharacter;
+        }
+
+        /**
+         * Check whether the specifier has a variable
+         * field width that is going to be set by an
+         * argument.
+         * @return <code>true</code> if the conversion
+         *   uses an * field width; otherwise
+         *   <code>false</code>.
+         */
+        boolean isVariableFieldWidth()
+        {
+            return variableFieldWidth;
+        }
+
+        /**
+         * Set the field width with an argument.  A
+         * negative field width is taken as a - flag
+         * followed by a positive field width.
+         * @param fw the field width.
+         */
+        void setFieldWidthWithArg(int fw)
+        {
+            if (fw < 0)
+            {
+                leftJustify = true;
+            }
+
+            fieldWidthSet = true;
+            fieldWidth    = Math.abs(fw);
+        }
+
+        /**
+         * Check whether the specifier has a variable
+         * precision that is going to be set by an
+         * argument.
+         * @return <code>true</code> if the conversion
+         *   uses an * precision; otherwise
+         *   <code>false</code>.
+         */
+        boolean isVariablePrecision()
+        {
+            return variablePrecision;
+        }
+
+        /**
+         * Set the precision with an argument.  A
+         * negative precision will be changed to zero.
+         * @param pr the precision.
+         */
+        void setPrecisionWithArg(int pr)
+        {
+            precisionSet = true;
+            precision    = Math.max(pr, 0);
+        }
+
+        /**
+         * Format an int argument using this conversion
+         *  specification.
+         * @param s the int to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is f, e, E, g, or G.
+         */
+        String internalsprintf(int s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'd' :
+            case 'i' :
+                if (optionalh)
+                {
+                    s2 = printDFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printDFormat((long) s);
+                } else
+                {
+                    s2 = printDFormat(s);
+                }
+
+                break;
+
+            case 'x' :
+            case 'X' :
+                if (optionalh)
+                {
+                    s2 = printXFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printXFormat((long) s);
+                } else
+                {
+                    s2 = printXFormat(s);
+                }
+
+                break;
+
+            case 'o' :
+                if (optionalh)
+                {
+                    s2 = printOFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printOFormat((long) s);
+                } else
+                {
+                    s2 = printOFormat(s);
+                }
+
+                break;
+
+            case 'c' :
+            case 'C' :
+                s2 = printCFormat((char) s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot format a int with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a long argument using this conversion
+         * specification.
+         * @param s the long to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is f, e, E, g, or G.
+         */
+        String internalsprintf(long s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'd' :
+            case 'i' :
+                if (optionalh)
+                {
+                    s2 = printDFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printDFormat(s);
+                } else
+                {
+                    s2 = printDFormat((int) s);
+                }
+
+                break;
+
+            case 'x' :
+            case 'X' :
+                if (optionalh)
+                {
+                    s2 = printXFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printXFormat(s);
+                } else
+                {
+                    s2 = printXFormat((int) s);
+                }
+
+                break;
+
+            case 'o' :
+                if (optionalh)
+                {
+                    s2 = printOFormat((short) s);
+                } else if (optionall)
+                {
+                    s2 = printOFormat(s);
+                } else
+                {
+                    s2 = printOFormat((int) s);
+                }
+
+                break;
+
+            case 'c' :
+            case 'C' :
+                s2 = printCFormat((char) s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot format a long with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a double argument using this conversion
+         * specification.
+         * @param s the double to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is c, C, s, S, i, d,
+         *     x, X, or o.
+         */
+        String internalsprintf(double s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            switch (conversionCharacter)
+            {
+            case 'f' :
+                s2 = printFFormat(s);
+
+                break;
+
+            case 'E' :
+            case 'e' :
+                s2 = printEFormat(s);
+
+                break;
+
+            case 'G' :
+            case 'g' :
+                s2 = printGFormat(s);
+
+                break;
+
+            default :
+                throw new IllegalArgumentException("Cannot " + "format a double with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format a String argument using this conversion
+         * specification.
+         * @param s the String to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *   conversion character is neither s nor S.
+         */
+        String internalsprintf(String s) throws IllegalArgumentException
+        {
+            String s2 = "";
+
+            if ((conversionCharacter == 's') || (conversionCharacter == 'S'))
+            {
+                s2 = printSFormat(s);
+            } else
+            {
+                throw new IllegalArgumentException("Cannot " + "format a String with a format using a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * Format an Object argument using this conversion
+         * specification.
+         * @param s the Object to format.
+         * @return the formatted String.
+         * @exception IllegalArgumentException if the
+         *     conversion character is neither s nor S.
+         */
+        String internalsprintf(Object s)
+        {
+            String s2 = "";
+
+            if ((conversionCharacter == 's') || (conversionCharacter == 'S'))
+            {
+                s2 = printSFormat(s.toString());
+            } else
+            {
+                throw new IllegalArgumentException("Cannot format a String with a format using" + " a " + conversionCharacter
+                                                   + " conversion character.");
+            }
+
+            return s2;
+        }
+
+        /**
+         * For f format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both
+         * a '+' and a ' ' are specified, the blank flag
+         * is ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the number of digits
+         * to appear after the radix character.  Padding is
+         * with trailing 0s.
+         */
+        private char[] fFormatDigits(double x)
+        {
+            // int defaultDigits=6;
+            String  sx, sxOut;
+            int     i, j, k;
+            int     n1In, n2In;
+            int     expon     = 0;
+            boolean minusSign = false;
+
+            if (x > 0.0)
+            {
+                sx = Double.toString(x);
+            } else if (x < 0.0)
+            {
+                sx        = Double.toString(-x);
+                minusSign = true;
+            } else
+            {
+                sx = Double.toString(x);
+
+                if (sx.charAt(0) == '-')
+                {
+                    minusSign = true;
+                    sx        = sx.substring(1);
+                }
+            }
+
+            int ePos = sx.indexOf('E');
+            int rPos = sx.indexOf('.');
+
+            if (rPos != -1)
+            {
+                n1In = rPos;
+            } else if (ePos != -1)
+            {
+                n1In = ePos;
+            } else
+            {
+                n1In = sx.length();
+            }
+
+            if (rPos != -1)
+            {
+                if (ePos != -1)
+                {
+                    n2In = ePos - rPos - 1;
+                } else
+                {
+                    n2In = sx.length() - rPos - 1;
+                }
+            } else
+            {
+                n2In = 0;
+            }
+
+            if (ePos != -1)
+            {
+                int ie = ePos + 1;
+
+                expon = 0;
+
+                if (sx.charAt(ie) == '-')
+                {
+                    for (++ie; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(ie));
+                    }
+                } else
+                {
+                    if (sx.charAt(ie) == '+')
+                    {
+                        ++ie;
+                    }
+
+                    for (; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(ie));
+                    }
+                }
+            }
+
+            int p;
+
+            if (precisionSet)
+            {
+                p = precision;
+            } else
+            {
+                p = defaultDigits - 1;
+            }
+
+            char[] ca1 = sx.toCharArray();
+            char[] ca2 = new char[n1In + n2In];
+            char[] ca3, ca4, ca5;
+
+            for (j = 0; j < n1In; j++)
+            {
+                ca2[j] = ca1[j];
+            }
+
+            i = j + 1;
+
+            for (k = 0; k < n2In; j++, i++, k++)
+            {
+                ca2[j] = ca1[i];
+            }
+
+            if (n1In + expon <= 0)
+            {
+                ca3 = new char[-expon + n2In];
+
+                for (j = 0, k = 0; k < (-n1In - expon); k++, j++)
+                {
+                    ca3[j] = '0';
+                }
+
+                for (i = 0; i < (n1In + n2In); i++, j++)
+                {
+                    ca3[j] = ca2[i];
+                }
+            } else
+            {
+                ca3 = ca2;
+            }
+
+            boolean carry = false;
+
+            if (p < -expon + n2In)
+            {
+                if (expon < 0)
+                {
+                    i = p;
+                } else
+                {
+                    i = p + n1In;
+                }
+
+                carry = checkForCarry(ca3, i);
+
+                if (carry)
+                {
+                    carry = startSymbolicCarry(ca3, i - 1, 0);
+                }
+            }
+
+            if (n1In + expon <= 0)
+            {
+                ca4 = new char[2 + p];
+
+                if (!carry)
+                {
+                    ca4[0] = '0';
+                } else
+                {
+                    ca4[0] = '1';
+                }
+
+                if (alternateForm ||!precisionSet || (precision != 0))
+                {
+                    ca4[1] = '.';
+
+                    for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++)
+                    {
+                        ca4[j] = ca3[i];
+                    }
+
+                    for (; j < ca4.length; j++)
+                    {
+                        ca4[j] = '0';
+                    }
+                }
+            } else
+            {
+                if (!carry)
+                {
+                    if (alternateForm ||!precisionSet || (precision != 0))
+                    {
+                        ca4 = new char[n1In + expon + p + 1];
+                    } else
+                    {
+                        ca4 = new char[n1In + expon];
+                    }
+
+                    j = 0;
+                } else
+                {
+                    if (alternateForm ||!precisionSet || (precision != 0))
+                    {
+                        ca4 = new char[n1In + expon + p + 2];
+                    } else
+                    {
+                        ca4 = new char[n1In + expon + 1];
+                    }
+
+                    ca4[0] = '1';
+                    j      = 1;
+                }
+
+                for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++)
+                {
+                    ca4[j] = ca3[i];
+                }
+
+                for (; i < n1In + expon; i++, j++)
+                {
+                    ca4[j] = '0';
+                }
+
+                if (alternateForm ||!precisionSet || (precision != 0))
+                {
+                    ca4[j] = '.';
+                    j++;
+
+                    for (k = 0; (i < ca3.length) && (k < p); i++, j++, k++)
+                    {
+                        ca4[j] = ca3[i];
+                    }
+
+                    for (; j < ca4.length; j++)
+                    {
+                        ca4[j] = '0';
+                    }
+                }
+            }
+
+            int nZeros = 0;
+
+            if (!leftJustify && leadingZeros)
+            {
+                int xThousands = 0;
+
+                if (thousands)
+                {
+                    int xlead = 0;
+
+                    if ((ca4[0] == '+') || (ca4[0] == '-') || (ca4[0] == ' '))
+                    {
+                        xlead = 1;
+                    }
+
+                    int xdp = xlead;
+
+                    for (; xdp < ca4.length; xdp++)
+                    {
+                        if (ca4[xdp] == '.')
+                        {
+                            break;
+                        }
+                    }
+
+                    xThousands = (xdp - xlead) / 3;
+                }
+
+                if (fieldWidthSet)
+                {
+                    nZeros = fieldWidth - ca4.length;
+                }
+
+                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+                {
+                    nZeros--;
+                }
+
+                nZeros -= xThousands;
+
+                if (nZeros < 0)
+                {
+                    nZeros = 0;
+                }
+            }
+
+            j = 0;
+
+            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+            {
+                ca5 = new char[ca4.length + nZeros + 1];
+                j++;
+            } else
+            {
+                ca5 = new char[ca4.length + nZeros];
+            }
+
+            if (!minusSign)
+            {
+                if (leadingSign)
+                {
+                    ca5[0] = '+';
+                }
+
+                if (leadingSpace)
+                {
+                    ca5[0] = ' ';
+                }
+            } else
+            {
+                ca5[0] = '-';
+            }
+
+            for (i = 0; i < nZeros; i++, j++)
+            {
+                ca5[j] = '0';
+            }
+
+            for (i = 0; i < ca4.length; i++, j++)
+            {
+                ca5[j] = ca4[i];
+            }
+
+            int lead = 0;
+
+            if ((ca5[0] == '+') || (ca5[0] == '-') || (ca5[0] == ' '))
+            {
+                lead = 1;
+            }
+
+            int dp = lead;
+
+            for (; dp < ca5.length; dp++)
+            {
+                if (ca5[dp] == '.')
+                {
+                    break;
+                }
+            }
+
+            int nThousands = (dp - lead) / 3;
+
+            // Localize the decimal point.
+            if (dp < ca5.length)
+            {
+                ca5[dp] = dfs.getDecimalSeparator();
+            }
+
+            char[] ca6 = ca5;
+
+            if (thousands && (nThousands > 0))
+            {
+                ca6    = new char[ca5.length + nThousands + lead];
+                ca6[0] = ca5[0];
+
+                for (i = lead, k = lead; i < dp; i++)
+                {
+                    if ((i > 0) && (dp - i) % 3 == 0)
+                    {
+                        // ca6[k]=',';
+                        ca6[k]     = dfs.getGroupingSeparator();
+                        ca6[k + 1] = ca5[i];
+                        k          += 2;
+                    } else
+                    {
+                        ca6[k] = ca5[i];
+                        k++;
+                    }
+                }
+
+                for (; i < ca5.length; i++, k++)
+                {
+                    ca6[k] = ca5[i];
+                }
+            }
+
+            return ca6;
+        }
+
+        /**
+         * An intermediate routine on the way to creating
+         * an f format String.  The method decides whether
+         * the input double value is an infinity,
+         * not-a-number, or a finite double and formats
+         * each type of input appropriately.
+         * @param x the double value to be formatted.
+         * @return the converted double value.
+         */
+        private String fFormatString(double x)
+        {
+            boolean noDigits = false;
+            char[]  ca6, ca7;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca6 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca6 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca6 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca6 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca6 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca6 = " NaN".toCharArray();
+                } else
+                {
+                    ca6 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                ca6 = fFormatDigits(x);
+            }
+
+            ca7 = applyFloatPadding(ca6, false);
+
+            return new String(ca7);
+        }
+
+        /**
+         * For e format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         *
+         * The behavior is like printf.  One (hopefully the
+         * only) exception is that the minimum number of
+         * exponent digits is 3 instead of 2 for e and E
+         * formats when the optional L is used before the
+         * e, E, g, or G conversion character. The optional
+         * L does not imply conversion to a long long
+         * double.
+         */
+        private char[] eFormatDigits(double x, char eChar)
+        {
+            char[] ca1, ca2, ca3;
+
+            // int defaultDigits=6;
+            String  sx, sxOut;
+            int     i, j, k, p;
+            int     n1In, n2In;
+            int     expon = 0;
+            int     ePos, rPos, eSize;
+            boolean minusSign = false;
+
+            if (x > 0.0)
+            {
+                sx = Double.toString(x);
+            } else if (x < 0.0)
+            {
+                sx        = Double.toString(-x);
+                minusSign = true;
+            } else
+            {
+                sx = Double.toString(x);
+
+                if (sx.charAt(0) == '-')
+                {
+                    minusSign = true;
+                    sx        = sx.substring(1);
+                }
+            }
+
+            ePos = sx.indexOf('E');
+
+            if (ePos == -1)
+            {
+                ePos = sx.indexOf('e');
+            }
+
+            rPos = sx.indexOf('.');
+
+            if (rPos != -1)
+            {
+                n1In = rPos;
+            } else if (ePos != -1)
+            {
+                n1In = ePos;
+            } else
+            {
+                n1In = sx.length();
+            }
+
+            if (rPos != -1)
+            {
+                if (ePos != -1)
+                {
+                    n2In = ePos - rPos - 1;
+                } else
+                {
+                    n2In = sx.length() - rPos - 1;
+                }
+            } else
+            {
+                n2In = 0;
+            }
+
+            if (ePos != -1)
+            {
+                int ie = ePos + 1;
+
+                expon = 0;
+
+                if (sx.charAt(ie) == '-')
+                {
+                    for (++ie; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(ie));
+                    }
+                } else
+                {
+                    if (sx.charAt(ie) == '+')
+                    {
+                        ++ie;
+                    }
+
+                    for (; ie < sx.length(); ie++)
+                    {
+                        if (sx.charAt(ie) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (ie < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(ie));
+                    }
+                }
+            }
+
+            if (rPos != -1)
+            {
+                expon += rPos - 1;
+            }
+
+            if (precisionSet)
+            {
+                p = precision;
+            } else
+            {
+                p = defaultDigits - 1;
+            }
+
+            if ((rPos != -1) && (ePos != -1))
+            {
+                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
+            } else if (rPos != -1)
+            {
+                ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
+            } else if (ePos != -1)
+            {
+                ca1 = sx.substring(0, ePos).toCharArray();
+            } else
+            {
+                ca1 = sx.toCharArray();
+            }
+
+            boolean carry = false;
+            int     i0    = 0;
+
+            if (ca1[0] != '0')
+            {
+                i0 = 0;
+            } else
+            {
+                for (i0 = 0; i0 < ca1.length; i0++)
+                {
+                    if (ca1[i0] != '0')
+                    {
+                        break;
+                    }
+                }
+            }
+
+            if (i0 + p < ca1.length - 1)
+            {
+                carry = checkForCarry(ca1, i0 + p + 1);
+
+                if (carry)
+                {
+                    carry = startSymbolicCarry(ca1, i0 + p, i0);
+                }
+
+                if (carry)
+                {
+                    ca2     = new char[i0 + p + 1];
+                    ca2[i0] = '1';
+
+                    for (j = 0; j < i0; j++)
+                    {
+                        ca2[j] = '0';
+                    }
+
+                    for (i = i0, j = i0 + 1; j < p + 1; i++, j++)
+                    {
+                        ca2[j] = ca1[i];
+                    }
+
+                    expon++;
+                    ca1 = ca2;
+                }
+            }
+
+            if ((Math.abs(expon) < 100) &&!optionalL)
+            {
+                eSize = 4;
+            } else
+            {
+                eSize = 5;
+            }
+
+            if (alternateForm ||!precisionSet || (precision != 0))
+            {
+                ca2 = new char[2 + p + eSize];
+            } else
+            {
+                ca2 = new char[1 + eSize];
+            }
+
+            if (ca1[0] != '0')
+            {
+                ca2[0] = ca1[0];
+                j      = 1;
+            } else
+            {
+                for (j = 1; j < ((ePos == -1)
+                                 ? ca1.length
+                                 : ePos); j++)
+                {
+                    if (ca1[j] != '0')
+                    {
+                        break;
+                    }
+                }
+
+                if (((ePos != -1) && (j < ePos)) || ((ePos == -1) && (j < ca1.length)))
+                {
+                    ca2[0] = ca1[j];
+                    expon  -= j;
+                    j++;
+                } else
+                {
+                    ca2[0] = '0';
+                    j      = 2;
+                }
+            }
+
+            if (alternateForm ||!precisionSet || (precision != 0))
+            {
+                ca2[1] = '.';
+                i      = 2;
+            } else
+            {
+                i = 1;
+            }
+
+            for (k = 0; (k < p) && (j < ca1.length); j++, i++, k++)
+            {
+                ca2[i] = ca1[j];
+            }
+
+            for (; i < ca2.length - eSize; i++)
+            {
+                ca2[i] = '0';
+            }
+
+            ca2[i++] = eChar;
+
+            if (expon < 0)
+            {
+                ca2[i++] = '-';
+            } else
+            {
+                ca2[i++] = '+';
+            }
+
+            expon = Math.abs(expon);
+
+            if (expon >= 100)
+            {
+                switch (expon / 100)
+                {
+                case 1 :
+                    ca2[i] = '1';
+
+                    break;
+
+                case 2 :
+                    ca2[i] = '2';
+
+                    break;
+
+                case 3 :
+                    ca2[i] = '3';
+
+                    break;
+
+                case 4 :
+                    ca2[i] = '4';
+
+                    break;
+
+                case 5 :
+                    ca2[i] = '5';
+
+                    break;
+
+                case 6 :
+                    ca2[i] = '6';
+
+                    break;
+
+                case 7 :
+                    ca2[i] = '7';
+
+                    break;
+
+                case 8 :
+                    ca2[i] = '8';
+
+                    break;
+
+                case 9 :
+                    ca2[i] = '9';
+
+                    break;
+                }
+
+                i++;
+            }
+
+            switch ((expon % 100) / 10)
+            {
+            case 0 :
+                ca2[i] = '0';
+
+                break;
+
+            case 1 :
+                ca2[i] = '1';
+
+                break;
+
+            case 2 :
+                ca2[i] = '2';
+
+                break;
+
+            case 3 :
+                ca2[i] = '3';
+
+                break;
+
+            case 4 :
+                ca2[i] = '4';
+
+                break;
+
+            case 5 :
+                ca2[i] = '5';
+
+                break;
+
+            case 6 :
+                ca2[i] = '6';
+
+                break;
+
+            case 7 :
+                ca2[i] = '7';
+
+                break;
+
+            case 8 :
+                ca2[i] = '8';
+
+                break;
+
+            case 9 :
+                ca2[i] = '9';
+
+                break;
+            }
+
+            i++;
+
+            switch (expon % 10)
+            {
+            case 0 :
+                ca2[i] = '0';
+
+                break;
+
+            case 1 :
+                ca2[i] = '1';
+
+                break;
+
+            case 2 :
+                ca2[i] = '2';
+
+                break;
+
+            case 3 :
+                ca2[i] = '3';
+
+                break;
+
+            case 4 :
+                ca2[i] = '4';
+
+                break;
+
+            case 5 :
+                ca2[i] = '5';
+
+                break;
+
+            case 6 :
+                ca2[i] = '6';
+
+                break;
+
+            case 7 :
+                ca2[i] = '7';
+
+                break;
+
+            case 8 :
+                ca2[i] = '8';
+
+                break;
+
+            case 9 :
+                ca2[i] = '9';
+
+                break;
+            }
+
+            int nZeros = 0;
+
+            if (!leftJustify && leadingZeros)
+            {
+                int xThousands = 0;
+
+                if (thousands)
+                {
+                    int xlead = 0;
+
+                    if ((ca2[0] == '+') || (ca2[0] == '-') || (ca2[0] == ' '))
+                    {
+                        xlead = 1;
+                    }
+
+                    int xdp = xlead;
+
+                    for (; xdp < ca2.length; xdp++)
+                    {
+                        if (ca2[xdp] == '.')
+                        {
+                            break;
+                        }
+                    }
+
+                    xThousands = (xdp - xlead) / 3;
+                }
+
+                if (fieldWidthSet)
+                {
+                    nZeros = fieldWidth - ca2.length;
+                }
+
+                if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+                {
+                    nZeros--;
+                }
+
+                nZeros -= xThousands;
+
+                if (nZeros < 0)
+                {
+                    nZeros = 0;
+                }
+            }
+
+            j = 0;
+
+            if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+            {
+                ca3 = new char[ca2.length + nZeros + 1];
+                j++;
+            } else
+            {
+                ca3 = new char[ca2.length + nZeros];
+            }
+
+            if (!minusSign)
+            {
+                if (leadingSign)
+                {
+                    ca3[0] = '+';
+                }
+
+                if (leadingSpace)
+                {
+                    ca3[0] = ' ';
+                }
+            } else
+            {
+                ca3[0] = '-';
+            }
+
+            for (k = 0; k < nZeros; j++, k++)
+            {
+                ca3[j] = '0';
+            }
+
+            for (i = 0; (i < ca2.length) && (j < ca3.length); i++, j++)
+            {
+                ca3[j] = ca2[i];
+            }
+
+            int lead = 0;
+
+            if ((ca3[0] == '+') || (ca3[0] == '-') || (ca3[0] == ' '))
+            {
+                lead = 1;
+            }
+
+            int dp = lead;
+
+            for (; dp < ca3.length; dp++)
+            {
+                if (ca3[dp] == '.')
+                {
+                    break;
+                }
+            }
+
+            int nThousands = dp / 3;
+
+            // Localize the decimal point.
+            if (dp < ca3.length)
+            {
+                ca3[dp] = dfs.getDecimalSeparator();
+            }
+
+            char[] ca4 = ca3;
+
+            if (thousands && (nThousands > 0))
+            {
+                ca4    = new char[ca3.length + nThousands + lead];
+                ca4[0] = ca3[0];
+
+                for (i = lead, k = lead; i < dp; i++)
+                {
+                    if ((i > 0) && (dp - i) % 3 == 0)
+                    {
+                        // ca4[k]=',';
+                        ca4[k]     = dfs.getGroupingSeparator();
+                        ca4[k + 1] = ca3[i];
+                        k          += 2;
+                    } else
+                    {
+                        ca4[k] = ca3[i];
+                        k++;
+                    }
+                }
+
+                for (; i < ca3.length; i++, k++)
+                {
+                    ca4[k] = ca3[i];
+                }
+            }
+
+            return ca4;
+        }
+
+        /**
+         * Check to see if the digits that are going to
+         * be truncated because of the precision should
+         * force a round in the preceding digits.
+         * @param ca1 the array of digits
+         * @param icarry the index of the first digit that
+         *     is to be truncated from the print
+         * @return <code>true</code> if the truncation forces
+         *     a round that will change the print
+         */
+        private boolean checkForCarry(char[] ca1, int icarry)
+        {
+            boolean carry = false;
+
+            if (icarry < ca1.length)
+            {
+                if ((ca1[icarry] == '6') || (ca1[icarry] == '7') || (ca1[icarry] == '8') || (ca1[icarry] == '9'))
+                {
+                    carry = true;
+                } else if (ca1[icarry] == '5')
+                {
+                    int ii = icarry + 1;
+
+                    for (; ii < ca1.length; ii++)
+                    {
+                        if (ca1[ii] != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    carry = ii < ca1.length;
+
+                    if (!carry && (icarry > 0))
+                    {
+                        carry = ((ca1[icarry - 1] == '1') || (ca1[icarry - 1] == '3') || (ca1[icarry - 1] == '5')
+                                 || (ca1[icarry - 1] == '7') || (ca1[icarry - 1] == '9'));
+                    }
+                }
+            }
+
+            return carry;
+        }
+
+        /**
+         * Start the symbolic carry process.  The process
+         * is not quite finished because the symbolic
+         * carry may change the length of the string and
+         * change the exponent (in e format).
+         * @param cLast index of the last digit changed
+         *     by the round
+         * @param cFirst index of the first digit allowed
+         *     to be changed by this phase of the round
+         * @return <code>true</code> if the carry forces
+         *     a round that will change the print still
+         *     more
+         */
+        private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst)
+        {
+            boolean carry = true;
+
+            for (int i = cLast; carry && (i >= cFirst); i--)
+            {
+                carry = false;
+
+                switch (ca[i])
+                {
+                case '0' :
+                    ca[i] = '1';
+
+                    break;
+
+                case '1' :
+                    ca[i] = '2';
+
+                    break;
+
+                case '2' :
+                    ca[i] = '3';
+
+                    break;
+
+                case '3' :
+                    ca[i] = '4';
+
+                    break;
+
+                case '4' :
+                    ca[i] = '5';
+
+                    break;
+
+                case '5' :
+                    ca[i] = '6';
+
+                    break;
+
+                case '6' :
+                    ca[i] = '7';
+
+                    break;
+
+                case '7' :
+                    ca[i] = '8';
+
+                    break;
+
+                case '8' :
+                    ca[i] = '9';
+
+                    break;
+
+                case '9' :
+                    ca[i] = '0';
+                    carry = true;
+
+                    break;
+                }
+            }
+
+            return carry;
+        }
+
+        /**
+         * An intermediate routine on the way to creating
+         * an e format String.  The method decides whether
+         * the input double value is an infinity,
+         * not-a-number, or a finite double and formats
+         * each type of input appropriately.
+         * @param x the double value to be formatted.
+         * @param eChar an 'e' or 'E' to use in the
+         *     converted double value.
+         * @return the converted double value.
+         */
+        private String eFormatString(double x, char eChar)
+        {
+            boolean noDigits = false;
+            char[]  ca4, ca5;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca4 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca4 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca4 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca4 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca4 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca4 = " NaN".toCharArray();
+                } else
+                {
+                    ca4 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                ca4 = eFormatDigits(x, eChar);
+            }
+
+            ca5 = applyFloatPadding(ca4, false);
+
+            return new String(ca5);
+        }
+
+        /**
+         * Apply zero or blank, left or right padding.
+         * @param ca4 array of characters before padding is
+         *     finished
+         * @param noDigits NaN or signed Inf
+         * @return a padded array of characters
+         */
+        private char[] applyFloatPadding(char[] ca4, boolean noDigits)
+        {
+            char[] ca5 = ca4;
+
+            if (fieldWidthSet)
+            {
+                int i, j, nBlanks;
+
+                if (leftJustify)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+
+                        for (i = 0; i < ca4.length; i++)
+                        {
+                            ca5[i] = ca4[i];
+                        }
+
+                        for (j = 0; j < nBlanks; j++, i++)
+                        {
+                            ca5[i] = ' ';
+                        }
+                    }
+                } else if (!leadingZeros || noDigits)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+
+                        for (i = 0; i < nBlanks; i++)
+                        {
+                            ca5[i] = ' ';
+                        }
+
+                        for (j = 0; j < ca4.length; i++, j++)
+                        {
+                            ca5[i] = ca4[j];
+                        }
+                    }
+                } else if (leadingZeros)
+                {
+                    nBlanks = fieldWidth - ca4.length;
+
+                    if (nBlanks > 0)
+                    {
+                        ca5 = new char[ca4.length + nBlanks];
+                        i   = 0;
+                        j   = 0;
+
+                        if (ca4[0] == '-')
+                        {
+                            ca5[0] = '-';
+                            i++;
+                            j++;
+                        }
+
+                        for (int k = 0; k < nBlanks; i++, k++)
+                        {
+                            ca5[i] = '0';
+                        }
+
+                        for (; j < ca4.length; i++, j++)
+                        {
+                            ca5[i] = ca4[j];
+                        }
+                    }
+                }
+            }
+
+            return ca5;
+        }
+
+        /**
+         * Format method for the f conversion character.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printFFormat(double x)
+        {
+            return fFormatString(x);
+        }
+
+        /**
+         * Format method for the e or E conversion
+         * character.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printEFormat(double x)
+        {
+            if (conversionCharacter == 'e')
+            {
+                return eFormatString(x, 'e');
+            } else
+            {
+                return eFormatString(x, 'E');
+            }
+        }
+
+        /**
+         * Format method for the g conversion character.
+         *
+         * For g format, the flag character '-', means that
+         *  the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         * @param x the double to format.
+         * @return the formatted String.
+         */
+        private String printGFormat(double x)
+        {
+            String  sx, sy, sz, ret;
+            int     savePrecision = precision;
+            int     i;
+            char[]  ca4, ca5;
+            boolean noDigits = false;
+
+            if (Double.isInfinite(x))
+            {
+                if (x == Double.POSITIVE_INFINITY)
+                {
+                    if (leadingSign)
+                    {
+                        ca4 = "+Inf".toCharArray();
+                    } else if (leadingSpace)
+                    {
+                        ca4 = " Inf".toCharArray();
+                    } else
+                    {
+                        ca4 = "Inf".toCharArray();
+                    }
+                } else
+                {
+                    ca4 = "-Inf".toCharArray();
+                }
+
+                noDigits = true;
+            } else if (Double.isNaN(x))
+            {
+                if (leadingSign)
+                {
+                    ca4 = "+NaN".toCharArray();
+                } else if (leadingSpace)
+                {
+                    ca4 = " NaN".toCharArray();
+                } else
+                {
+                    ca4 = "NaN".toCharArray();
+                }
+
+                noDigits = true;
+            } else
+            {
+                if (!precisionSet)
+                {
+                    precision = defaultDigits;
+                }
+
+                if (precision == 0)
+                {
+                    precision = 1;
+                }
+
+                int ePos = -1;
+
+                if (conversionCharacter == 'g')
+                {
+                    sx   = eFormatString(x, 'e').trim();
+                    ePos = sx.indexOf('e');
+                } else
+                {
+                    sx   = eFormatString(x, 'E').trim();
+                    ePos = sx.indexOf('E');
+                }
+
+                i = ePos + 1;
+
+                int expon = 0;
+
+                if (sx.charAt(i) == '-')
+                {
+                    for (++i; i < sx.length(); i++)
+                    {
+                        if (sx.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (i < sx.length())
+                    {
+                        expon = -Integer.parseInt(sx.substring(i));
+                    }
+                } else
+                {
+                    if (sx.charAt(i) == '+')
+                    {
+                        ++i;
+                    }
+
+                    for (; i < sx.length(); i++)
+                    {
+                        if (sx.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if (i < sx.length())
+                    {
+                        expon = Integer.parseInt(sx.substring(i));
+                    }
+                }
+
+                // Trim trailing zeros.
+                // If the radix character is not followed by
+                // a digit, trim it, too.
+                if (!alternateForm)
+                {
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        sy = fFormatString(x).trim();
+                    } else
+                    {
+                        sy = sx.substring(0, ePos);
+                    }
+
+                    i = sy.length() - 1;
+
+                    for (; i >= 0; i--)
+                    {
+                        if (sy.charAt(i) != '0')
+                        {
+                            break;
+                        }
+                    }
+
+                    if ((i >= 0) && (sy.charAt(i) == '.'))
+                    {
+                        i--;
+                    }
+
+                    if (i == -1)
+                    {
+                        sz = "0";
+                    } else if (!Character.isDigit(sy.charAt(i)))
+                    {
+                        sz = sy.substring(0, i + 1) + "0";
+                    } else
+                    {
+                        sz = sy.substring(0, i + 1);
+                    }
+
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        ret = sz;
+                    } else
+                    {
+                        ret = sz + sx.substring(ePos);
+                    }
+                } else
+                {
+                    if ((expon >= -4) && (expon < precision))
+                    {
+                        ret = fFormatString(x).trim();
+                    } else
+                    {
+                        ret = sx;
+                    }
+                }
+
+                // leading space was trimmed off during
+                // construction
+                if (leadingSpace)
+                {
+                    if (x >= 0)
+                    {
+                        ret = " " + ret;
+                    }
+                }
+
+                ca4 = ret.toCharArray();
+            }
+
+            // Pad with blanks or zeros.
+            ca5       = applyFloatPadding(ca4, false);
+            precision = savePrecision;
+
+            return new String(ca5);
+        }
+
+        /**
+         * Format method for the d conversion specifer and
+         * short argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(short x)
+        {
+            return printDFormat(Short.toString(x));
+        }
+
+        /**
+         * Format method for the d conversion character and
+         * long argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(long x)
+        {
+            return printDFormat(Long.toString(x));
+        }
+
+        /**
+         * Format method for the d conversion character and
+         * int argument.
+         *
+         * For d format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  A '+' character means that the conversion
+         * will always begin with a sign (+ or -).  The
+         * blank flag character means that a non-negative
+         * input will be preceded with a blank.  If both a
+         * '+' and a ' ' are specified, the blank flag is
+         * ignored.  The '0' flag character implies that
+         * padding to the field width will be done with
+         * zeros instead of blanks.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printDFormat(int x)
+        {
+            return printDFormat(Integer.toString(x));
+        }
+
+        /**
+         * Utility method for formatting using the d
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printDFormat(String sx)
+        {
+            int     nLeadingZeros = 0;
+            int     nBlanks       = 0,
+                    n             = 0;
+            int     i             = 0,
+                    jFirst        = 0;
+            boolean neg           = sx.charAt(0) == '-';
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (!neg)
+            {
+                if (precisionSet && (sx.length() < precision))
+                {
+                    nLeadingZeros = precision - sx.length();
+                }
+            } else
+            {
+                if (precisionSet && (sx.length() - 1) < precision)
+                {
+                    nLeadingZeros = precision - sx.length() + 1;
+                }
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+
+                if (!neg && (leadingSign || leadingSpace))
+                {
+                    nBlanks--;
+                }
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            if (leadingSign)
+            {
+                n++;
+            } else if (leadingSpace)
+            {
+                n++;
+            }
+
+            n += nBlanks;
+            n += nLeadingZeros;
+            n += sx.length();
+
+            char[] ca = new char[n];
+
+            if (leftJustify)
+            {
+                if (neg)
+                {
+                    ca[i++] = '-';
+                } else if (leadingSign)
+                {
+                    ca[i++] = '+';
+                } else if (leadingSpace)
+                {
+                    ca[i++] = ' ';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                jFirst = neg
+                         ? 1
+                         : 0;
+
+                for (int j = 0; j < nLeadingZeros; i++, j++)
+                {
+                    ca[i] = '0';
+                }
+
+                for (int j = jFirst; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; i++, j++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (!leadingZeros)
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = ' ';
+                    }
+
+                    if (neg)
+                    {
+                        ca[i++] = '-';
+                    } else if (leadingSign)
+                    {
+                        ca[i++] = '+';
+                    } else if (leadingSpace)
+                    {
+                        ca[i++] = ' ';
+                    }
+                } else
+                {
+                    if (neg)
+                    {
+                        ca[i++] = '-';
+                    } else if (leadingSign)
+                    {
+                        ca[i++] = '+';
+                    } else if (leadingSpace)
+                    {
+                        ca[i++] = ' ';
+                    }
+
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = '0';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                jFirst = neg
+                         ? 1
+                         : 0;
+
+                for (int j = jFirst; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * short argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(short x)
+        {
+            String sx = null;
+
+            if (x == Short.MIN_VALUE)
+            {
+                sx = "8000";
+            } else if (x < 0)
+            {
+                String t;
+
+                if (x == Short.MIN_VALUE)
+                {
+                    t = "0";
+                } else
+                {
+                    t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
+
+                    if ((t.charAt(0) == 'F') || (t.charAt(0) == 'f'))
+                    {
+                        t = t.substring(16, 32);
+                    }
+                }
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 4 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 4);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 4);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 4);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 4);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 4);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 4);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 4);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString((int) x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * long argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(long x)
+        {
+            String sx = null;
+
+            if (x == Long.MIN_VALUE)
+            {
+                sx = "8000000000000000";
+            } else if (x < 0)
+            {
+                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "800000000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "80000000000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "8000000000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "800000000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "80000000000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "8000000000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "800000000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "80000000" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "8000000" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "800000" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "80000" + t;
+
+                    break;
+
+                case 12 :
+                    sx = "8000" + t;
+
+                    break;
+
+                case 13 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 14 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 15 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 16 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 16);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 16);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 16);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 16);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 16);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 16);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 16);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Long.toString(x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Format method for the x conversion character and
+         * int argument.
+         *
+         * For x format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means to lead with
+         * '0x'.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printXFormat(int x)
+        {
+            String sx = null;
+
+            if (x == Integer.MIN_VALUE)
+            {
+                sx = "80000000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "8000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "800000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "80000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "8000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "800" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "80" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "8" + t;
+
+                    break;
+
+                case 8 :
+                    switch (t.charAt(0))
+                    {
+                    case '1' :
+                        sx = "9" + t.substring(1, 8);
+
+                        break;
+
+                    case '2' :
+                        sx = "a" + t.substring(1, 8);
+
+                        break;
+
+                    case '3' :
+                        sx = "b" + t.substring(1, 8);
+
+                        break;
+
+                    case '4' :
+                        sx = "c" + t.substring(1, 8);
+
+                        break;
+
+                    case '5' :
+                        sx = "d" + t.substring(1, 8);
+
+                        break;
+
+                    case '6' :
+                        sx = "e" + t.substring(1, 8);
+
+                        break;
+
+                    case '7' :
+                        sx = "f" + t.substring(1, 8);
+
+                        break;
+                    }
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString(x, 16);
+            }
+
+            return printXFormat(sx);
+        }
+
+        /**
+         * Utility method for formatting using the x
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printXFormat(String sx)
+        {
+            int nLeadingZeros = 0;
+            int nBlanks       = 0;
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (precisionSet)
+            {
+                nLeadingZeros = precision - sx.length();
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+
+                if (alternateForm)
+                {
+                    nBlanks = nBlanks - 2;
+                }
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            int n = 0;
+
+            if (alternateForm)
+            {
+                n += 2;
+            }
+
+            n += nLeadingZeros;
+            n += sx.length();
+            n += nBlanks;
+
+            char[] ca = new char[n];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                if (alternateForm)
+                {
+                    ca[i++] = '0';
+                    ca[i++] = 'x';
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (!leadingZeros)
+                {
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = ' ';
+                    }
+                }
+
+                if (alternateForm)
+                {
+                    ca[i++] = '0';
+                    ca[i++] = 'x';
+                }
+
+                if (leadingZeros)
+                {
+                    for (int j = 0; j < nBlanks; j++, i++)
+                    {
+                        ca[i] = '0';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            String caReturn = new String(ca);
+
+            if (conversionCharacter == 'X')
+            {
+                caReturn = caReturn.toUpperCase();
+            }
+
+            return caReturn;
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * short argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the short to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(short x)
+        {
+            String sx = null;
+
+            if (x == Short.MIN_VALUE)
+            {
+                sx = "100000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "10000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "1000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "100" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "10" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "1" + t;
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString((int) x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * long argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the long to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(long x)
+        {
+            String sx = null;
+
+            if (x == Long.MIN_VALUE)
+            {
+                sx = "1000000000000000000000";
+            } else if (x < 0)
+            {
+                String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "100000000000000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "10000000000000000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "1000000000000000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "100000000000000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "10000000000000000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "1000000000000000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "100000000000000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "10000000000000" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "1000000000000" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "100000000000" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "10000000000" + t;
+
+                    break;
+
+                case 12 :
+                    sx = "1000000000" + t;
+
+                    break;
+
+                case 13 :
+                    sx = "100000000" + t;
+
+                    break;
+
+                case 14 :
+                    sx = "10000000" + t;
+
+                    break;
+
+                case 15 :
+                    sx = "1000000" + t;
+
+                    break;
+
+                case 16 :
+                    sx = "100000" + t;
+
+                    break;
+
+                case 17 :
+                    sx = "10000" + t;
+
+                    break;
+
+                case 18 :
+                    sx = "1000" + t;
+
+                    break;
+
+                case 19 :
+                    sx = "100" + t;
+
+                    break;
+
+                case 20 :
+                    sx = "10" + t;
+
+                    break;
+
+                case 21 :
+                    sx = "1" + t;
+
+                    break;
+                }
+            } else
+            {
+                sx = Long.toString(x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Format method for the o conversion character and
+         * int argument.
+         *
+         * For o format, the flag character '-', means that
+         * the output should be left justified within the
+         * field.  The default is to pad with blanks on the
+         * left.  The '#' flag character means that the
+         * output begins with a leading 0 and the precision
+         * is increased by 1.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is to
+         * add no padding.  Padding is with blanks by
+         * default.
+         *
+         * The precision, if set, is the minimum number of
+         * digits to appear.  Padding is with leading 0s.
+         * @param x the int to format.
+         * @return the formatted String.
+         */
+        private String printOFormat(int x)
+        {
+            String sx = null;
+
+            if (x == Integer.MIN_VALUE)
+            {
+                sx = "20000000000";
+            } else if (x < 0)
+            {
+                String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
+
+                switch (t.length())
+                {
+                case 1 :
+                    sx = "2000000000" + t;
+
+                    break;
+
+                case 2 :
+                    sx = "200000000" + t;
+
+                    break;
+
+                case 3 :
+                    sx = "20000000" + t;
+
+                    break;
+
+                case 4 :
+                    sx = "2000000" + t;
+
+                    break;
+
+                case 5 :
+                    sx = "200000" + t;
+
+                    break;
+
+                case 6 :
+                    sx = "20000" + t;
+
+                    break;
+
+                case 7 :
+                    sx = "2000" + t;
+
+                    break;
+
+                case 8 :
+                    sx = "200" + t;
+
+                    break;
+
+                case 9 :
+                    sx = "20" + t;
+
+                    break;
+
+                case 10 :
+                    sx = "2" + t;
+
+                    break;
+
+                case 11 :
+                    sx = "3" + t.substring(1);
+
+                    break;
+                }
+            } else
+            {
+                sx = Integer.toString(x, 8);
+            }
+
+            return printOFormat(sx);
+        }
+
+        /**
+         * Utility method for formatting using the o
+         * conversion character.
+         * @param sx the String to format, the result of
+         *     converting a short, int, or long to a
+         *     String.
+         * @return the formatted String.
+         */
+        private String printOFormat(String sx)
+        {
+            int nLeadingZeros = 0;
+            int nBlanks       = 0;
+
+            if (sx.equals("0") && precisionSet && (precision == 0))
+            {
+                sx = "";
+            }
+
+            if (precisionSet)
+            {
+                nLeadingZeros = precision - sx.length();
+            }
+
+            if (alternateForm)
+            {
+                nLeadingZeros++;
+            }
+
+            if (nLeadingZeros < 0)
+            {
+                nLeadingZeros = 0;
+            }
+
+            if (fieldWidthSet)
+            {
+                nBlanks = fieldWidth - nLeadingZeros - sx.length();
+            }
+
+            if (nBlanks < 0)
+            {
+                nBlanks = 0;
+            }
+
+            int    n  = nLeadingZeros + sx.length() + nBlanks;
+            char[] ca = new char[n];
+            int    i;
+
+            if (leftJustify)
+            {
+                for (i = 0; i < nLeadingZeros; i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+
+                for (int j = 0; j < nBlanks; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                if (leadingZeros)
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = '0';
+                    }
+                } else
+                {
+                    for (i = 0; i < nBlanks; i++)
+                    {
+                        ca[i] = ' ';
+                    }
+                }
+
+                for (int j = 0; j < nLeadingZeros; j++, i++)
+                {
+                    ca[i] = '0';
+                }
+
+                char[] csx = sx.toCharArray();
+
+                for (int j = 0; j < csx.length; j++, i++)
+                {
+                    ca[i] = csx[j];
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the c conversion character and
+         * char argument.
+         *
+         * The only flag character that affects c format is
+         * the '-', meaning that the output should be left
+         * justified within the field.  The default is to
+         * pad with blanks on the left.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  Padding is with
+         * blanks by default.  The default width is 1.
+         *
+         * The precision, if set, is ignored.
+         * @param x the char to format.
+         * @return the formatted String.
+         */
+        private String printCFormat(char x)
+        {
+            int nPrint = 1;
+            int width  = fieldWidth;
+
+            if (!fieldWidthSet)
+            {
+                width = nPrint;
+            }
+
+            char[] ca = new char[width];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                ca[0] = x;
+
+                for (i = 1; i <= width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                for (i = 0; i < width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+
+                ca[i] = x;
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Format method for the s conversion character and
+         * String argument.
+         *
+         * The only flag character that affects s format is
+         * the '-', meaning that the output should be left
+         * justified within the field.  The default is to
+         * pad with blanks on the left.
+         *
+         * The field width is treated as the minimum number
+         * of characters to be printed.  The default is the
+         * smaller of the number of characters in the the
+         * input and the precision.  Padding is with blanks
+         * by default.
+         *
+         * The precision, if set, specifies the maximum
+         * number of characters to be printed from the
+         * string.  A null digit string is treated
+         * as a 0.  The default is not to set a maximum
+         * number of characters to be printed.
+         * @param x the String to format.
+         * @return the formatted String.
+         */
+        private String printSFormat(String x)
+        {
+            int nPrint = x.length();
+            int width  = fieldWidth;
+
+            if (precisionSet && (nPrint > precision))
+            {
+                nPrint = precision;
+            }
+
+            if (!fieldWidthSet)
+            {
+                width = nPrint;
+            }
+
+            int n = 0;
+
+            if (width > nPrint)
+            {
+                n += width - nPrint;
+            }
+
+            if (nPrint >= x.length())
+            {
+                n += x.length();
+            } else
+            {
+                n += nPrint;
+            }
+
+            char[] ca = new char[n];
+            int    i  = 0;
+
+            if (leftJustify)
+            {
+                if (nPrint >= x.length())
+                {
+                    char[] csx = x.toCharArray();
+
+                    for (i = 0; i < x.length(); i++)
+                    {
+                        ca[i] = csx[i];
+                    }
+                } else
+                {
+                    char[] csx = x.substring(0, nPrint).toCharArray();
+
+                    for (i = 0; i < nPrint; i++)
+                    {
+                        ca[i] = csx[i];
+                    }
+                }
+
+                for (int j = 0; j < width - nPrint; j++, i++)
+                {
+                    ca[i] = ' ';
+                }
+            } else
+            {
+                for (i = 0; i < width - nPrint; i++)
+                {
+                    ca[i] = ' ';
+                }
+
+                if (nPrint >= x.length())
+                {
+                    char[] csx = x.toCharArray();
+
+                    for (int j = 0; j < x.length(); i++, j++)
+                    {
+                        ca[i] = csx[j];
+                    }
+                } else
+                {
+                    char[] csx = x.substring(0, nPrint).toCharArray();
+
+                    for (int j = 0; j < nPrint; i++, j++)
+                    {
+                        ca[i] = csx[j];
+                    }
+                }
+            }
+
+            return new String(ca);
+        }
+
+        /**
+         * Check for a conversion character.  If it is
+         * there, store it.
+         * @return <code>true</code> if the conversion
+         *     character is there, and
+         *     <code>false</code> otherwise.
+         */
+        private boolean setConversionCharacter()
+        {
+            /* idfgGoxXeEcs */
+            boolean ret = false;
+
+            conversionCharacter = '\0';
+
+            if (pos < fmt.length())
+            {
+                char c = fmt.charAt(pos);
+
+                if ((c == 'i') || (c == 'd') || (c == 'f') || (c == 'g') || (c == 'G') || (c == 'o') || (c == 'x') || (c == 'X')
+                    || (c == 'e') || (c == 'E') || (c == 'c') || (c == 's') || (c == '%'))
+                {
+                    conversionCharacter = c;
+                    pos++;
+                    ret = true;
+                }
+            }
+
+            return ret;
+        }
+
+        /**
+         * Check for an h, l, or L in a format.  An L is
+         * used to control the minimum number of digits
+         * in an exponent when using floating point
+         * formats.  An l or h is used to control
+         * conversion of the input to a long or short,
+         * respectively, before formatting.  If any of
+         * these is present, store them.
+         */
+        private void setOptionalHL()
+        {
+            optionalh = false;
+            optionall = false;
+            optionalL = false;
+
+            if (pos < fmt.length())
+            {
+                char c = fmt.charAt(pos);
+
+                if (c == 'h')
+                {
+                    optionalh = true;
+                    pos++;
+                } else if (c == 'l')
+                {
+                    optionall = true;
+                    pos++;
+                } else if (c == 'L')
+                {
+                    optionalL = true;
+                    pos++;
+                }
+            }
+        }
+
+        /**
+         * Set the precision.
+         */
+        private void setPrecision()
+        {
+            int firstPos = pos;
+
+            precisionSet = false;
+
+            if ((pos < fmt.length()) && (fmt.charAt(pos) == '.'))
+            {
+                pos++;
+
+                if ((pos < fmt.length()) && (fmt.charAt(pos) == '*'))
+                {
+                    pos++;
+
+                    if (!setPrecisionArgPosition())
+                    {
+                        variablePrecision = true;
+                        precisionSet      = true;
+                    }
+
+                    return;
+                } else
+                {
+                    while (pos < fmt.length())
+                    {
+                        char c = fmt.charAt(pos);
+
+                        if (Character.isDigit(c))
+                        {
+                            pos++;
+                        } else
+                        {
+                            break;
+                        }
+                    }
+
+                    if (pos > firstPos + 1)
+                    {
+                        String sz = fmt.substring(firstPos + 1, pos);
+
+                        precision    = Integer.parseInt(sz);
+                        precisionSet = true;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Set the field width.
+         */
+        private void setFieldWidth()
+        {
+            int firstPos = pos;
+
+            fieldWidth    = 0;
+            fieldWidthSet = false;
+
+            if ((pos < fmt.length()) && (fmt.charAt(pos) == '*'))
+            {
+                pos++;
+
+                if (!setFieldWidthArgPosition())
+                {
+                    variableFieldWidth = true;
+                    fieldWidthSet      = true;
+                }
+            } else
+            {
+                while (pos < fmt.length())
+                {
+                    char c = fmt.charAt(pos);
+
+                    if (Character.isDigit(c))
+                    {
+                        pos++;
+                    } else
+                    {
+                        break;
+                    }
+                }
+
+                if ((firstPos < pos) && (firstPos < fmt.length()))
+                {
+                    String sz = fmt.substring(firstPos, pos);
+
+                    fieldWidth    = Integer.parseInt(sz);
+                    fieldWidthSet = true;
+                }
+            }
+        }
+
+        /**
+         * Store the digits <code>n</code> in %n$ forms.
+         */
+        private void setArgPosition()
+        {
+            int xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalSpecification = true;
+                    argumentPosition        = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                     = xPos + 1;
+                }
+            }
+        }
+
+        /**
+         * Store the digits <code>n</code> in *n$ forms.
+         */
+        private boolean setFieldWidthArgPosition()
+        {
+            boolean ret = false;
+            int     xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalFieldWidth          = true;
+                    argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                           = xPos + 1;
+                    ret                           = true;
+                }
+            }
+
+            return ret;
+        }
+
+        /**
+         * Store the digits <code>n</code> in *n$ forms.
+         */
+        private boolean setPrecisionArgPosition()
+        {
+            boolean ret = false;
+            int     xPos;
+
+            for (xPos = pos; xPos < fmt.length(); xPos++)
+            {
+                if (!Character.isDigit(fmt.charAt(xPos)))
+                {
+                    break;
+                }
+            }
+
+            if ((xPos > pos) && (xPos < fmt.length()))
+            {
+                if (fmt.charAt(xPos) == '$')
+                {
+                    positionalPrecision          = true;
+                    argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos));
+                    pos                          = xPos + 1;
+                    ret                          = true;
+                }
+            }
+
+            return ret;
+        }
+
+        boolean isPositionalSpecification()
+        {
+            return positionalSpecification;
+        }
+
+        int getArgumentPosition()
+        {
+            return argumentPosition;
+        }
+
+        boolean isPositionalFieldWidth()
+        {
+            return positionalFieldWidth;
+        }
+
+        int getArgumentPositionForFieldWidth()
+        {
+            return argumentPositionForFieldWidth;
+        }
+
+        boolean isPositionalPrecision()
+        {
+            return positionalPrecision;
+        }
+
+        int getArgumentPositionForPrecision()
+        {
+            return argumentPositionForPrecision;
+        }
+
+        /**
+         * Set flag characters, one of '-+#0 or a space.
+         */
+        private void setFlagCharacters()
+        {
+            /* '-+ #0 */
+            thousands     = false;
+            leftJustify   = false;
+            leadingSign   = false;
+            leadingSpace  = false;
+            alternateForm = false;
+            leadingZeros  = false;
+
+            for (; pos < fmt.length(); pos++)
+            {
+                char c = fmt.charAt(pos);
+
+                if (c == '\'')
+                {
+                    thousands = true;
+                } else if (c == '-')
+                {
+                    leftJustify  = true;
+                    leadingZeros = false;
+                } else if (c == '+')
+                {
+                    leadingSign  = true;
+                    leadingSpace = false;
+                } else if (c == ' ')
+                {
+                    if (!leadingSign)
+                    {
+                        leadingSpace = true;
+                    }
+                } else if (c == '#')
+                {
+                    alternateForm = true;
+                } else if (c == '0')
+                {
+                    if (!leftJustify)
+                    {
+                        leadingZeros = true;
+                    }
+                } else
+                {
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java
new file mode 100644
index 0000000..06e869a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java
@@ -0,0 +1,2923 @@
+package com.ximple.eofms.util;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CharacterCodingException;
+import java.nio.CharBuffer;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.lang.reflect.Array;
+import java.text.BreakIterator;
+
+public abstract class StringUtils
+{
+	public static String ENCODING_US_ASCII = "US-ASCII";
+	public static String ENCODING_ISO_8859_1 = "ISO-8859-1";
+	public static String ENCODING_ISO_8859_2 = "ISO-8859-2";
+	public static String ENCODING_ISO_8859_5 = "ISO-8859-5";
+	public static String ENCODING_UTF_8 = "UTF-8";
+	public static String ENCODING_UTF_16BE = "UTF-16BE";
+	public static String ENCODING_UTF_16LE = "UTF-16LE";
+	public static String ENCODING_UTF_16 = "UTF-16";
+
+	public static Charset CHARSET_US_ASCII = Charset.forName(StringUtils.ENCODING_US_ASCII);
+
+	public static final Pattern BBCODE_COLOR = Pattern.compile("\\[color\\s*=\\s*([#\\w]*)\\s*\\]", Pattern.CASE_INSENSITIVE);
+	public static final Pattern BBCODE_SIZE = Pattern.compile("\\[size\\s*=\\s*([+\\-]?[0-9]*)\\s*\\]", Pattern.CASE_INSENSITIVE);
+	public static final Pattern BBCODE_URL_SHORT = Pattern.compile("\\[url\\]\\s*([^\\s]*)\\s*\\[\\/url\\]", Pattern.CASE_INSENSITIVE);
+	public static final Pattern BBCODE_URL_LONG = Pattern.compile("\\[url=([^\\[]*)\\]([^\\[]*)\\[/url\\]", Pattern.CASE_INSENSITIVE);
+	public static final Pattern BBCODE_IMG = Pattern.compile("\\[img\\]\\s*([^\\s]*)\\s*\\[\\/img\\]", Pattern.CASE_INSENSITIVE);
+	public static final Pattern BBCODE_QUOTE_LONG = Pattern.compile("\\[quote=([^\\]]+\\]*)\\]", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+	public static final Pattern BBCODE_BAREURL = Pattern.compile("(?:[^\"'=>\\]]|^)((?:http|ftp)s?://(?:%[\\p{Digit}A-Fa-f][\\p{Digit}A-Fa-f]|[\\-_\\.!~*';\\|/?:@#&=\\+$,\\p{Alnum}])+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
+
+	private static final Map<Character, String> AGGRESSIVE_HTML_ENCODE_MAP = new HashMap<Character, String>();
+	private static final Map<Character, String> DEFENSIVE_HTML_ENCODE_MAP = new HashMap<Character, String>();
+	private static final Map<Character, String> XML_ENCODE_MAP = new HashMap<Character, String>();
+	private static final Map<Character, String> STRING_ENCODE_MAP = new HashMap<Character, String>();
+	private static final Map<Character, String> SQL_ENCODE_MAP = new HashMap<Character, String>();
+	private static final Map<Character, String> LATEX_ENCODE_MAP = new HashMap<Character, String>();
+
+	private static final Map<String, Character>		HTML_DECODE_MAP = new HashMap<String, Character>();
+
+	private static final HtmlEncoderFallbackHandler HTML_ENCODER_FALLBACK = new HtmlEncoderFallbackHandler();
+
+	static
+	{
+		// Html encoding mapping according to the HTML 4.0 spec
+		// http://www.w3.org/TR/REC-html40/sgml/entities.html
+
+		// Special characters for HTML
+		AGGRESSIVE_HTML_ENCODE_MAP.put('\u0026',"&amp;");
+		AGGRESSIVE_HTML_ENCODE_MAP.put('\u003C',"&lt;");
+		AGGRESSIVE_HTML_ENCODE_MAP.put('\u003E',"&gt;");
+		AGGRESSIVE_HTML_ENCODE_MAP.put('\u0022',"&quot;");
+
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0152',"&OElig;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0153',"&oelig;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0160',"&Scaron;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0161',"&scaron;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0178',"&Yuml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u02C6',"&circ;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u02DC',"&tilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2002',"&ensp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2003',"&emsp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2009',"&thinsp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u200C',"&zwnj;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u200D',"&zwj;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u200E',"&lrm;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u200F',"&rlm;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2013',"&ndash;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2014',"&mdash;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2018',"&lsquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2019',"&rsquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u201A',"&sbquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u201C',"&ldquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u201D',"&rdquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u201E',"&bdquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2020',"&dagger;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2021',"&Dagger;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2030',"&permil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2039',"&lsaquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u203A',"&rsaquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u20AC',"&euro;");
+
+		// Character entity references for ISO 8859-1 characters
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A0',"&nbsp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A1',"&iexcl;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A2',"&cent;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A3',"&pound;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A4',"&curren;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A5',"&yen;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A6',"&brvbar;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A7',"&sect;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A8',"&uml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00A9',"&copy;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AA',"&ordf;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AB',"&laquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AC',"&not;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AD',"&shy;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AE',"&reg;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00AF',"&macr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B0',"&deg;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B1',"&plusmn;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B2',"&sup2;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B3',"&sup3;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B4',"&acute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B5',"&micro;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B6',"&para;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B7',"&middot;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B8',"&cedil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00B9',"&sup1;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BA',"&ordm;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BB',"&raquo;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BC',"&frac14;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BD',"&frac12;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BE',"&frac34;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00BF',"&iquest;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C0',"&Agrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C1',"&Aacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C2',"&Acirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C3',"&Atilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C4',"&Auml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C5',"&Aring;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C6',"&AElig;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C7',"&Ccedil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C8',"&Egrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00C9',"&Eacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CA',"&Ecirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CB',"&Euml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CC',"&Igrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CD',"&Iacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CE',"&Icirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00CF',"&Iuml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D0',"&ETH;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D1',"&Ntilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D2',"&Ograve;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D3',"&Oacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D4',"&Ocirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D5',"&Otilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D6',"&Ouml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D7',"&times;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D8',"&Oslash;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00D9',"&Ugrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DA',"&Uacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DB',"&Ucirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DC',"&Uuml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DD',"&Yacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DE',"&THORN;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00DF',"&szlig;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E0',"&agrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E1',"&aacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E2',"&acirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E3',"&atilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E4',"&auml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E5',"&aring;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E6',"&aelig;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E7',"&ccedil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E8',"&egrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00E9',"&eacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00EA',"&ecirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00EB',"&euml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00EC',"&igrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00ED',"&iacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00EE',"&icirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00EF',"&iuml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F0',"&eth;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F1',"&ntilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F2',"&ograve;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F3',"&oacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F4',"&ocirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F5',"&otilde;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F6',"&ouml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F7',"&divide;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F8',"&oslash;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00F9',"&ugrave;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FA',"&uacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FB',"&ucirc;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FC',"&uuml;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FD',"&yacute;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FE',"&thorn;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u00FF',"&yuml;");
+
+		// Mathematical, Greek and Symbolic characters for HTML
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0192',"&fnof;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0391',"&Alpha;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0392',"&Beta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0393',"&Gamma;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0394',"&Delta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0395',"&Epsilon;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0396',"&Zeta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0397',"&Eta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0398',"&Theta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u0399',"&Iota;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039A',"&Kappa;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039B',"&Lambda;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039C',"&Mu;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039D',"&Nu;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039E',"&Xi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u039F',"&Omicron;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A0',"&Pi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A1',"&Rho;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A3',"&Sigma;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A4',"&Tau;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A5',"&Upsilon;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A6',"&Phi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A7',"&Chi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A8',"&Psi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03A9',"&Omega;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B1',"&alpha;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B2',"&beta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B3',"&gamma;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B4',"&delta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B5',"&epsilon;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B6',"&zeta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B7',"&eta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B8',"&theta;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03B9',"&iota;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BA',"&kappa;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BB',"&lambda;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BC',"&mu;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BD',"&nu;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BE',"&xi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03BF',"&omicron;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C0',"&pi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C1',"&rho;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C2',"&sigmaf;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C3',"&sigma;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C4',"&tau;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C5',"&upsilon;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C6',"&phi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C7',"&chi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C8',"&psi;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03C9',"&omega;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03D1',"&thetasym;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03D2',"&upsih;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u03D6',"&piv;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2022',"&bull;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2026',"&hellip;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2032',"&prime;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2033',"&Prime;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u203E',"&oline;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2044',"&frasl;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2118',"&weierp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2111',"&image;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u211C',"&real;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2122',"&trade;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2135',"&alefsym;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2190',"&larr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2191',"&uarr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2192',"&rarr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2193',"&darr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2194',"&harr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21B5',"&crarr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21D0',"&lArr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21D1',"&uArr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21D2',"&rArr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21D3',"&dArr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u21D4',"&hArr;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2200',"&forall;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2202',"&part;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2203',"&exist;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2205',"&empty;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2207',"&nabla;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2208',"&isin;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2209',"&notin;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u220B',"&ni;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u220F',"&prod;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2211',"&sum;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2212',"&minus;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2217',"&lowast;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u221A',"&radic;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u221D',"&prop;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u221E',"&infin;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2220',"&ang;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2227',"&and;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2228',"&or;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2229',"&cap;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u222A',"&cup;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u222B',"&int;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2234',"&there4;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u223C',"&sim;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2245',"&cong;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2248',"&asymp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2260',"&ne;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2261',"&equiv;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2264',"&le;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2265',"&ge;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2282',"&sub;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2283',"&sup;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2284',"&nsub;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2286',"&sube;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2287',"&supe;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2295',"&oplus;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2297',"&otimes;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u22A5',"&perp;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u22C5',"&sdot;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2308',"&lceil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2309',"&rceil;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u230A',"&lfloor;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u230B',"&rfloor;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2329',"&lang;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u232A',"&rang;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u25CA',"&loz;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2660',"&spades;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2663',"&clubs;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2665',"&hearts;");
+		DEFENSIVE_HTML_ENCODE_MAP.put('\u2666',"&diams;");
+
+		Set<Map.Entry<Character, String>> aggresive_entries = AGGRESSIVE_HTML_ENCODE_MAP.entrySet();
+		for (Map.Entry<Character, String> entry : aggresive_entries)
+		{
+			HTML_DECODE_MAP.put(entry.getValue(), entry.getKey());
+		}
+
+		Set<Map.Entry<Character, String>> defensive_entries = DEFENSIVE_HTML_ENCODE_MAP.entrySet();
+		for (Map.Entry<Character, String> entry : defensive_entries)
+		{
+			HTML_DECODE_MAP.put(entry.getValue(), entry.getKey());
+		}
+
+		XML_ENCODE_MAP.put('\u0026',"&amp;");
+		XML_ENCODE_MAP.put('\'',"&apos;");
+		XML_ENCODE_MAP.put('\u0022',"&quot;");
+		XML_ENCODE_MAP.put('\u003C',"&lt;");
+		XML_ENCODE_MAP.put('\u003E',"&gt;");
+
+		SQL_ENCODE_MAP.put('\'',"''");
+
+		STRING_ENCODE_MAP.put('\\',"\\\\");
+		STRING_ENCODE_MAP.put('\n',"\\n");
+		STRING_ENCODE_MAP.put('\r',"\\r");
+		STRING_ENCODE_MAP.put('\t',"\\t");
+		STRING_ENCODE_MAP.put('"',"\\\"");
+
+		LATEX_ENCODE_MAP.put('\\',"\\\\");
+		LATEX_ENCODE_MAP.put('#',"\\#");
+		LATEX_ENCODE_MAP.put('$',"\\$");
+		LATEX_ENCODE_MAP.put('%',"\\%");
+		LATEX_ENCODE_MAP.put('&',"\\&");
+		LATEX_ENCODE_MAP.put('~',"\\~");
+		LATEX_ENCODE_MAP.put('_',"\\_");
+		LATEX_ENCODE_MAP.put('^',"\\^");
+		LATEX_ENCODE_MAP.put('{',"\\{");
+		LATEX_ENCODE_MAP.put('}',"\\}");
+		LATEX_ENCODE_MAP.put('\u00A1',"!'");
+		LATEX_ENCODE_MAP.put('\u00BF',"?'");
+		LATEX_ENCODE_MAP.put('\u00C0',"\\`{A}");
+		LATEX_ENCODE_MAP.put('\u00C1',"\\'{A}");
+		LATEX_ENCODE_MAP.put('\u00C2',"\\^{A}");
+		LATEX_ENCODE_MAP.put('\u00C3',"\\H{A}");
+		LATEX_ENCODE_MAP.put('\u00C4',"\\\"{A}");
+		LATEX_ENCODE_MAP.put('\u00C5',"\\AA");
+		LATEX_ENCODE_MAP.put('\u00C6',"\\AE");
+		LATEX_ENCODE_MAP.put('\u00C7',"\\c{C}");
+		LATEX_ENCODE_MAP.put('\u00C8',"\\`{E}");
+		LATEX_ENCODE_MAP.put('\u00C9',"\\'{E}");
+		LATEX_ENCODE_MAP.put('\u00CA',"\\^{E}");
+		LATEX_ENCODE_MAP.put('\u00CB',"\\\"{E}");
+		LATEX_ENCODE_MAP.put('\u00CC',"\\`{I}");
+		LATEX_ENCODE_MAP.put('\u00CD',"\\'{I}");
+		LATEX_ENCODE_MAP.put('\u00CE',"\\^{I}");
+		LATEX_ENCODE_MAP.put('\u00CF',"\\\"{I}");
+// todo \u00D0
+		LATEX_ENCODE_MAP.put('\u00D1',"\\H{N}");
+		LATEX_ENCODE_MAP.put('\u00D2',"\\`{O}");
+		LATEX_ENCODE_MAP.put('\u00D3',"\\'{O}");
+		LATEX_ENCODE_MAP.put('\u00D4',"\\^{O}");
+		LATEX_ENCODE_MAP.put('\u00D5',"\\H{O}");
+		LATEX_ENCODE_MAP.put('\u00D6',"\\\"{O}");
+// todo \u00D7
+		LATEX_ENCODE_MAP.put('\u00D8',"\\O");
+		LATEX_ENCODE_MAP.put('\u00D9',"\\`{U}");
+		LATEX_ENCODE_MAP.put('\u00DA',"\\'{U}");
+		LATEX_ENCODE_MAP.put('\u00DB',"\\^{U}");
+		LATEX_ENCODE_MAP.put('\u00DC',"\\\"{U}");
+		LATEX_ENCODE_MAP.put('\u00DD',"\\'{Y}");
+// todo \u00DE
+		LATEX_ENCODE_MAP.put('\u00DF',"\\ss");
+		LATEX_ENCODE_MAP.put('\u00E0',"\\`{a}");
+		LATEX_ENCODE_MAP.put('\u00E1',"\\'{a}");
+		LATEX_ENCODE_MAP.put('\u00E2',"\\^{a}");
+		LATEX_ENCODE_MAP.put('\u00E3',"\\H{a}");
+		LATEX_ENCODE_MAP.put('\u00E4',"\\\"{a}");
+		LATEX_ENCODE_MAP.put('\u00E5',"\\aa");
+		LATEX_ENCODE_MAP.put('\u00E6',"\\ae");
+		LATEX_ENCODE_MAP.put('\u00E7',"\\c{c}");
+		LATEX_ENCODE_MAP.put('\u00E8',"\\`{e}");
+		LATEX_ENCODE_MAP.put('\u00E9',"\\'{e}");
+		LATEX_ENCODE_MAP.put('\u00EA',"\\^{e}");
+		LATEX_ENCODE_MAP.put('\u00EB',"\\\"{e}");
+		LATEX_ENCODE_MAP.put('\u00EC',"\\`{i}");
+		LATEX_ENCODE_MAP.put('\u00ED',"\\'{i}");
+		LATEX_ENCODE_MAP.put('\u00EE',"\\^{i}");
+		LATEX_ENCODE_MAP.put('\u00EF',"\\\"{i}");
+// todo \u00F0
+		LATEX_ENCODE_MAP.put('\u00F1',"\\H{n}");
+		LATEX_ENCODE_MAP.put('\u00F2',"\\`{o}");
+		LATEX_ENCODE_MAP.put('\u00F3',"\\'{o}");
+		LATEX_ENCODE_MAP.put('\u00F4',"\\^{o}");
+		LATEX_ENCODE_MAP.put('\u00F5',"\\H{o}");
+		LATEX_ENCODE_MAP.put('\u00F6',"\\\"{o}");
+// todo \u00F7
+		LATEX_ENCODE_MAP.put('\u00F8',"\\o");
+		LATEX_ENCODE_MAP.put('\u00F9',"\\`{u}");
+		LATEX_ENCODE_MAP.put('\u00FA',"\\'{u}");
+		LATEX_ENCODE_MAP.put('\u00FB',"\\^{u}");
+		LATEX_ENCODE_MAP.put('\u00FC',"\\\"{u}");
+		LATEX_ENCODE_MAP.put('\u00FD',"\\'{y}");
+// todo \u00FE
+		LATEX_ENCODE_MAP.put('\u00FF',"\\\"{y}");
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid characters for a java class name.
+	 *
+	 * @param name The string that has to be transformed into a valid class
+	 * name.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeUrl(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeClassname(String name)
+	{
+		if (null == name)
+		{
+			return null;
+		}
+
+		Pattern pattern = Pattern.compile("[^\\w]");
+		Matcher matcher = pattern.matcher(name);
+
+		return matcher.replaceAll("_");
+	}
+
+	private static boolean needsUrlEncoding(String source)
+	{
+		if (null == source)
+		{
+			return false;
+		}
+
+		// check if the string needs encoding first since
+		// the URLEncoder always allocates a StringBuffer, even when the
+		// string is returned as-is
+		boolean encode = false;
+		char ch;
+		for (int i = 0; i < source.length(); i++)
+		{
+			ch = source.charAt(i);
+
+			if (ch >= 'a' && ch <= 'z' ||
+				ch >= 'A' && ch <= 'Z' ||
+				ch >= '0' && ch <= '9' ||
+				ch == '-' || ch == '_' || ch == '.' || ch == '*')
+			{
+				continue;
+			}
+
+			encode = true;
+			break;
+		}
+
+		return encode;
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid URL characters.
+	 *
+	 * @param source The string that has to be transformed into a valid URL
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeUrl(String source)
+	{
+		if (!needsUrlEncoding(source))
+		{
+			return source;
+		}
+
+		try
+		{
+			return URLEncoder.encode(source, ENCODING_ISO_8859_1);
+		}
+		///CLOVER:OFF
+		catch (UnsupportedEncodingException e)
+		{
+			// this should never happen, ISO-8859-1 is a standard encoding
+			throw new RuntimeException(e);
+		}
+		///CLOVER:ON
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * only pure US Ascii strings are preserved and URL encoded in a regular
+	 * way. Strings with characters from other encodings will be encoded in a
+	 * RIFE-specific manner to allow international data to passed along the
+	 * query string.
+	 *
+	 * @param source The string that has to be transformed into a valid URL
+	 * parameter string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #decodeUrlValue(String)
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeUrlValue(String source)
+	{
+		if (!needsUrlEncoding(source))
+		{
+			return source;
+		}
+
+		// check if the string is valid US-ASCII encoding
+		boolean         valid = true;
+		CharsetEncoder encoder = CHARSET_US_ASCII.newEncoder();
+		try
+		{
+			encoder.encode(CharBuffer.wrap(source));
+		}
+		catch (CharacterCodingException e)
+		{
+			valid = false;
+		}
+
+		try
+		{
+			// if it is valid US-ASCII, use the regular URL encoding method
+			if (valid)
+			{
+					return URLEncoder.encode(source, ENCODING_US_ASCII);
+			}
+			// otherwise, base-64 encode the UTF-8 bytes and mark the string
+			// as being encoded in a special way
+			else
+			{
+				StringBuilder encoded = new StringBuilder("%02%02");
+				String base64 = Base64.encodeToString(source.getBytes(ENCODING_UTF_8), false);
+				String base64_urlsafe = replace(base64, "=", "%3D");
+				encoded.append(base64_urlsafe);
+
+				return encoded.toString();
+			}
+		}
+		///CLOVER:OFF
+		catch (UnsupportedEncodingException e)
+		{
+			// this should never happen, ISO-8859-1 is a standard encoding
+			throw new RuntimeException(e);
+		}
+		///CLOVER:ON
+	}
+
+	/**
+	 * Decodes a <code>String</code> that has been encoded in a RIFE-specific
+	 * manner for URL usage.. Before calling this method, you should first
+	 * verify if the value needs decoding by using the
+	 * <code>doesUrlValueNeedDecoding(String)</code> method.
+	 *
+	 * @param source the value that has been encoded for URL usage in a
+	 * RIFE-specific way
+	 * @return The decoded <code>String</code> object.
+	 * @see #encodeUrlValue(String)
+	 * @see #doesUrlValueNeedDecoding(String)
+	 * @since 1.0
+	 */
+	public static String decodeUrlValue(String source)
+	{
+		try
+		{
+			byte[] decoded = Base64.decode(source.substring(2));
+			if (null == decoded)
+			{
+				return null;
+			}
+			else
+			{
+				return new String(decoded, StringUtils.ENCODING_UTF_8);
+			}
+		}
+		///CLOVER:OFF
+		catch (UnsupportedEncodingException e)
+		{
+			// this should never happen, UTF-8 is a standard encoding
+			throw new RuntimeException(e);
+		}
+		///CLOVER:ON
+	}
+
+	/**
+	 * Checks if a <code>String</code> is encoded in a RIFE-specific manner
+	 * for URL usage.
+	 *
+	 * @param source the value that might have been encoded for URL usage in a
+	 * RIFE-specific way
+	 * @return <code>true</code> if the value is encoded in the RIFE-specific
+	 * format; and
+	 * <p><code>false</code> otherwise
+	 * @see #encodeUrlValue(String)
+	 * @see #decodeUrlValue(String)
+	 * @since 1.0
+	 */
+	public static boolean doesUrlValueNeedDecoding(String source)
+	{
+		if (source != null &&
+			source.length() > 2 &&
+			source.startsWith("\u0002\u0002"))
+		{
+			return true;
+		}
+
+		return false;
+	}
+
+	private static boolean needsHtmlEncoding(String source, boolean defensive)
+	{
+		if (null == source)
+		{
+			return false;
+		}
+
+		boolean encode = false;
+		char ch;
+		for (int i = 0; i < source.length(); i++)
+		{
+			ch = source.charAt(i);
+
+			if ((defensive || (ch != '\u0022' && ch != '\u0026' && ch != '\u003C' && ch != '\u003E')) &&
+				ch < '\u00A0')
+			{
+				continue;
+			}
+
+			encode = true;
+			break;
+		}
+
+		return encode;
+	}
+
+	/**
+	 *
+	 * @since 1.6
+	 */
+	public static String decodeHtml(String source)
+	{
+		if (null == source ||
+			0 == source.length())
+		{
+			return source;
+		}
+
+		int		current_index = 0;
+		int		delimiter_start_index = 0;
+		int		delimiter_end_index = 0;
+
+		StringBuilder result = null;
+
+		while (current_index <= source.length())
+		{
+			delimiter_start_index = source.indexOf('&', current_index);
+			if (delimiter_start_index != -1)
+			{
+				delimiter_end_index = source.indexOf(';', delimiter_start_index + 1);
+				if (delimiter_end_index != -1)
+				{
+					// ensure that the string builder is setup correctly
+					if (null == result)
+					{
+						result = new StringBuilder();
+					}
+
+					// add the text that leads up to this match
+					if (delimiter_start_index > current_index)
+					{
+						result.append(source.substring(current_index, delimiter_start_index));
+					}
+
+					// add the decoded entity
+					String entity = source.substring(delimiter_start_index, delimiter_end_index + 1);
+
+					current_index = delimiter_end_index + 1;
+
+					// try to decoded numeric entities
+					if (entity.charAt(1) == '#')
+					{
+						int start = 2;
+						int radix = 10;
+						// check if the number is hexadecimal
+						if (entity.charAt(2) == 'X' || entity.charAt(2) == 'x')
+						{
+							start++;
+							radix = 16;
+						}
+						try
+						{
+							Character c = new Character((char)Integer.parseInt(entity.substring(start, entity.length() - 1), radix));
+							result.append(c);
+						}
+						// when the number of the entity can't be parsed, add the entity as-is
+						catch (NumberFormatException e)
+						{
+							result.append(entity);
+						}
+					}
+					else
+					{
+						// try to decode the entity as a literal
+						Character decoded = HTML_DECODE_MAP.get(entity);
+						if (decoded != null)
+						{
+							result.append(decoded);
+						}
+						// if there was no match, add the entity as-is
+						else
+						{
+							result.append(entity);
+						}
+					}
+				}
+				else
+				{
+					break;
+				}
+			}
+			else
+			{
+				break;
+			}
+		}
+
+		if (null == result)
+		{
+			return source;
+		}
+		else if (current_index < source.length())
+		{
+			result.append(source.substring(current_index));
+		}
+
+		return result.toString();
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid Html characters.
+	 *
+	 * @param source The string that has to be transformed into a valid Html
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeString(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeHtml(String source)
+	{
+		if (needsHtmlEncoding(source, false))
+		{
+			return encode(source, HTML_ENCODER_FALLBACK, AGGRESSIVE_HTML_ENCODE_MAP, DEFENSIVE_HTML_ENCODE_MAP);
+		}
+		return source;
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing as much as possible Html characters. It is safe to already
+	 * feed existing Html to this method since &amp;, &lt; and &gt; will not
+	 * be encoded.
+	 *
+	 * @param source The string that has to be transformed into a valid Html
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeString(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeHtmlDefensive(String source)
+	{
+		if (needsHtmlEncoding(source, true))
+		{
+			return encode(source, null, DEFENSIVE_HTML_ENCODE_MAP);
+		}
+		return source;
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid XML characters.
+	 *
+	 * @param source The string that has to be transformed into a valid XML
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeString(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeXml(String source)
+	{
+		return encode(source, null, XML_ENCODE_MAP);
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid <code>String</code> characters.
+	 *
+	 * @param source The string that has to be transformed into a valid
+	 * sequence of <code>String</code> characters.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeString(String source)
+	{
+		return encode(source, null, STRING_ENCODE_MAP);
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a series of
+	 * unicode escape codes.
+	 *
+	 * @param source The string that has to be transformed into a valid
+	 * sequence of unicode escape codes
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeUnicode(String source)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		StringBuilder	encoded = new StringBuilder();
+		String			hexstring = null;
+		for (int i = 0; i < source.length(); i++)
+		{
+			hexstring = Integer.toHexString((int)source.charAt(i)).toUpperCase();
+			encoded.append("\\u");
+			// fill with zeros
+			for (int j = hexstring.length(); j < 4; j++)
+			{
+				encoded.append("0");
+			}
+			encoded.append(hexstring);
+		}
+
+		return encoded.toString();
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid Sql characters.
+	 *
+	 * @param source The string that has to be transformed into a valid Sql
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeString(String)
+	 * @see #encodeLatex(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeSql(String source)
+	{
+		return encode(source, null, SQL_ENCODE_MAP);
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * containing only valid LaTeX characters.
+	 *
+	 * @param source The string that has to be transformed into a valid LaTeX
+	 * string.
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeString(String)
+	 * @see #encodeRegexp(String)
+	 * @since 1.0
+	 */
+	public static String encodeLatex(String source)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		source = encode(source, null, LATEX_ENCODE_MAP);
+		source = StringUtils.replace(source, "latex", "\\LaTeX", false);
+
+		return source;
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a new string,
+	 * using the mapping that are provided through the supplied encoding
+	 * table.
+	 *
+	 * @param source The string that has to be transformed into a valid
+	 * string, using the mappings that are provided through the supplied
+	 * encoding table.
+	 * @param encodingTables A <code>Map</code> object containing the mappings
+	 * to transform characters into valid entities. The keys of this map
+	 * should be <code>Character</code> objects and the values
+	 * <code>String</code> objects.
+	 * @return The encoded <code>String</code> object.
+	 * @since 1.0
+	 */
+	private static String encode(String source, EncoderFallbackHandler fallbackHandler, Map<Character, String>... encodingTables)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		if (null == encodingTables ||
+			0 == encodingTables.length)
+		{
+			return source;
+		}
+
+		StringBuilder	encoded_string = null;
+		char[]			string_to_encode_array = source.toCharArray();
+		int				last_match = -1;
+
+		for (int i = 0; i < string_to_encode_array.length; i++)
+		{
+			char char_to_encode = string_to_encode_array[i];
+			for (Map<Character, String> encoding_table : encodingTables)
+			{
+				if (encoding_table.containsKey(char_to_encode))
+				{
+					encoded_string = prepareEncodedString(source, encoded_string, i, last_match, string_to_encode_array);
+
+					encoded_string.append(encoding_table.get(char_to_encode));
+					last_match = i;
+				}
+			}
+
+			if (fallbackHandler != null &&
+				last_match < i &&
+				fallbackHandler.hasFallback(char_to_encode))
+			{
+				encoded_string = prepareEncodedString(source, encoded_string, i, last_match, string_to_encode_array);
+
+				fallbackHandler.appendFallback(encoded_string, char_to_encode);
+				last_match = i;
+			}
+		}
+
+		if (null == encoded_string)
+		{
+			return source;
+		}
+		else
+		{
+			int difference = string_to_encode_array.length-(last_match+1);
+			if (difference > 0)
+			{
+				encoded_string.append(string_to_encode_array, last_match+1, difference);
+			}
+			return encoded_string.toString();
+		}
+	}
+
+	private static StringBuilder prepareEncodedString(String source, StringBuilder encodedString, int i, int lastMatch, char[] stringToEncodeArray)
+	{
+		if (null == encodedString)
+		{
+			encodedString = new StringBuilder(source.length());
+		}
+
+		int difference = i - (lastMatch + 1);
+		if (difference > 0)
+		{
+			encodedString.append(stringToEncodeArray, lastMatch + 1, difference);
+		}
+
+		return encodedString;
+	}
+
+	private static interface EncoderFallbackHandler
+	{
+		abstract boolean hasFallback(char character);
+		abstract void appendFallback(StringBuilder encodedBuffer, char character);
+	}
+
+	private static class HtmlEncoderFallbackHandler implements EncoderFallbackHandler
+	{
+		private final static String PREFIX = "&#";
+		private final static String SUFFIX = ";";
+
+		public boolean hasFallback(char character)
+		{
+			if (character < '\u00A0')
+			{
+				return false;
+			}
+
+			return true;
+		}
+
+		public void appendFallback(StringBuilder encodedBuffer, char character)
+		{
+			encodedBuffer.append(PREFIX);
+			encodedBuffer.append((int)character);
+			encodedBuffer.append(SUFFIX);
+		}
+	}
+
+	/**
+	 * Transforms a provided <code>String</code> object into a literal that can
+	 * be included into a regular expression {@link Pattern} as-is. None of the
+	 * regular expression escapes in the string will be functional anymore.
+	 *
+	 * @param source The string that has to be escaped as a literal
+	 * @return The encoded <code>String</code> object.
+	 * @see #encodeClassname(String)
+	 * @see #encodeUrl(String)
+	 * @see #encodeUrlValue(String)
+	 * @see #encodeHtml(String)
+	 * @see #encodeXml(String)
+	 * @see #encodeSql(String)
+	 * @see #encodeString(String)
+	 * @see #encodeLatex(String)
+	 * @since 1.3
+	 */
+    public static String encodeRegexp(String source)
+	{
+        int regexp_quote_start = source.indexOf("\\E");
+        if (-1 == regexp_quote_start)
+		{
+			return "\\Q" + source + "\\E";
+		}
+
+        StringBuilder buffer = new StringBuilder(source.length() * 2);
+		buffer.append("\\Q");
+
+		regexp_quote_start = 0;
+
+		int current = 0;
+		while (-1 == (regexp_quote_start = source.indexOf("\\E", current)))
+		{
+			buffer.append(source.substring(current, regexp_quote_start));
+			current = regexp_quote_start + 2;
+			buffer.append("\\E\\\\E\\Q");
+		}
+
+		buffer.append(source.substring(current, source.length()));
+		buffer.append("\\E");
+
+		return buffer.toString();
+    }
+
+	/**
+	 * Counts the number of times a substring occures in a provided string in
+	 * a case-sensitive manner.
+	 *
+	 * @param source The <code>String</code> object that will be searched in.
+	 * @param substring The string whose occurances will we counted.
+	 * @return An <code>int</code> value containing the number of occurances
+	 * of the substring.
+	 * @since 1.0
+	 */
+	public static int count(String source, String substring)
+	{
+		return count(source, substring, true);
+	}
+
+	/**
+	 * Counts the number of times a substring occures in a provided string.
+	 *
+	 * @param source The <code>String</code> object that will be searched in.
+	 * @param substring The string whose occurances will we counted.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return An <code>int</code> value containing the number of occurances
+	 * of the substring.
+	 * @since 1.0
+	 */
+	public static int count(String source, String substring, boolean matchCase)
+	{
+		if (null == source)
+		{
+			return 0;
+		}
+
+		if (null == substring)
+		{
+			return 0;
+		}
+
+		int current_index = 0;
+		int substring_index = 0;
+		int count = 0;
+
+		if (!matchCase)
+		{
+			source = source.toLowerCase();
+			substring = substring.toLowerCase();
+		}
+
+		while (current_index < source.length()-1)
+		{
+			substring_index = source.indexOf(substring, current_index);
+
+			if (-1 == substring_index)
+			{
+				break;
+			}
+			else
+			{
+				current_index = substring_index + substring.length();
+				count++;
+			}
+		}
+
+		return count;
+	}
+
+	/**
+	 * Splits a string into different parts, using a seperator string to
+	 * detect the seperation boundaries in a case-sensitive manner. The
+	 * seperator will not be included in the list of parts.
+	 *
+	 * @param source The string that will be split into parts.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @return An <code>ArrayList</code> containing the parts as
+	 * <code>String</code> objects.
+	 * @since 1.0
+	 */
+	public static ArrayList<String> split(String source, String seperator)
+	{
+		return split(source, seperator, true);
+	}
+
+	/**
+	 * Splits a string into different parts, using a seperator string to
+	 * detect the seperation boundaries. The seperator will not be included in
+	 * the list of parts.
+	 *
+	 * @param source The string that will be split into parts.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return An <code>ArrayList</code> containing the parts as
+	 * <code>String</code> objects.
+	 * @since 1.0
+	 */
+	public static ArrayList<String> split(String source, String seperator, boolean matchCase)
+	{
+		ArrayList<String>   substrings = new ArrayList<String>();
+
+		if (null == source)
+		{
+			return substrings;
+		}
+
+		if (null == seperator)
+		{
+			substrings.add(source);
+			return substrings;
+		}
+
+		int		current_index = 0;
+		int		delimiter_index = 0;
+		String	element = null;
+
+		String  source_lookup_reference = null;
+		if (!matchCase)
+		{
+			source_lookup_reference = source.toLowerCase();
+			seperator = seperator.toLowerCase();
+		}
+		else
+		{
+			source_lookup_reference = source;
+		}
+
+		while (current_index <= source_lookup_reference.length())
+		{
+			delimiter_index = source_lookup_reference.indexOf(seperator, current_index);
+
+			if (-1 == delimiter_index)
+			{
+				element = new String(source.substring(current_index, source.length()));
+				substrings.add(element);
+				current_index = source.length() + 1;
+			}
+			else
+			{
+				element = new String(source.substring(current_index, delimiter_index));
+				substrings.add(element);
+				current_index = delimiter_index + seperator.length();
+			}
+		}
+
+		return substrings;
+	}
+
+	/**
+	 * Splits a string into different parts, using a seperator string to
+	 * detect the seperation boundaries in a case-sensitive manner. The
+	 * seperator will not be included in the parts array.
+	 *
+	 * @param source The string that will be split into parts.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @return A <code>String[]</code> array containing the seperated parts.
+	 * @since 1.0
+	 */
+	public static String[] splitToArray(String source, String seperator)
+	{
+		return splitToArray(source, seperator, true);
+	}
+
+	/**
+	 * Splits a string into different parts, using a seperator string to
+	 * detect the seperation boundaries. The seperator will not be included in
+	 * the parts array.
+	 *
+	 * @param source The string that will be split into parts.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return A <code>String[]</code> array containing the seperated parts.
+	 * @since 1.0
+	 */
+	public static String[] splitToArray(String source, String seperator, boolean matchCase)
+	{
+		ArrayList<String>   substrings = split(source, seperator, matchCase);
+		String[]            substrings_array = new String[substrings.size()];
+		substrings_array = substrings.toArray(substrings_array);
+
+		return substrings_array;
+	}
+
+	/**
+	 * Splits a string into integers, using a seperator string to detect the
+	 * seperation boundaries in a case-sensitive manner. If a part couldn't be
+	 * converted to an integer, it will be omitted from the resulting array.
+	 *
+	 * @param source The string that will be split into integers.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @return An <code>int[]</code> array containing the seperated parts.
+	 * @since 1.0
+	 */
+	public static int[] splitToIntArray(String source, String seperator)
+	{
+		return splitToIntArray(source, seperator, true);
+	}
+
+	/**
+	 * Splits a string into integers, using a seperator string to detect the
+	 * seperation boundaries. If a part couldn't be converted to an integer,
+	 * it will be omitted from the resulting array.
+	 *
+	 * @param source The string that will be split into integers.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return An <code>int[]</code> array containing the seperated parts.
+	 * @since 1.0
+	 */
+	public static int[] splitToIntArray(String source, String seperator, boolean matchCase)
+	{
+		ArrayList<String>   string_parts = split(source, seperator, matchCase);
+		int                 number_of_valid_parts = 0;
+
+		for (String string_part : string_parts)
+		{
+			try
+			{
+				Integer.parseInt(string_part);
+				number_of_valid_parts++;
+			}
+			catch (NumberFormatException e)
+			{
+				// just continue
+			}
+		}
+
+		int[]   string_parts_int = (int[]) Array.newInstance(int.class, number_of_valid_parts);
+		int     added_parts = 0;
+
+		for (String string_part : string_parts)
+		{
+			try
+			{
+				string_parts_int[added_parts] = Integer.parseInt(string_part);
+				added_parts++;
+			}
+			catch (NumberFormatException e)
+			{
+				// just continue
+			}
+		}
+
+		return string_parts_int;
+	}
+
+	/**
+	 * Splits a string into bytes, using a seperator string to detect the
+	 * seperation boundaries in a case-sensitive manner. If a part couldn't be
+	 * converted to a <code>byte</code>, it will be omitted from the resulting
+	 * array.
+	 *
+	 * @param source The string that will be split into bytes.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @return A <code>byte[]</code> array containing the bytes.
+	 * @since 1.0
+	 */
+	public static byte[] splitToByteArray(String source, String seperator)
+	{
+		return splitToByteArray(source, seperator, true);
+	}
+
+	/**
+	 * Splits a string into bytes, using a seperator string to detect the
+	 * seperation boundaries. If a part couldn't be converted to a
+	 * <code>byte</code>, it will be omitted from the resulting array.
+	 *
+	 * @param source The string that will be split into bytes.
+	 * @param seperator The seperator string that will be used to determine
+	 * the parts.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return A <code>byte[]</code> array containing the bytes.
+	 * @since 1.0
+	 */
+	public static byte[] splitToByteArray(String source, String seperator, boolean matchCase)
+	{
+		ArrayList<String>   string_parts = split(source, seperator, matchCase);
+		int                 number_of_valid_parts = 0;
+		for (String string_part : string_parts)
+		{
+			try
+			{
+				Byte.parseByte(string_part);
+				number_of_valid_parts++;
+			}
+			catch (NumberFormatException e)
+			{
+				// just continue
+			}
+		}
+
+		byte[]  string_parts_byte = (byte[])Array.newInstance(byte.class, number_of_valid_parts);
+		int     added_parts = 0;
+		for (String string_part : string_parts)
+		{
+			try
+			{
+				string_parts_byte[added_parts] = Byte.parseByte(string_part);
+				added_parts++;
+			}
+			catch (NumberFormatException e)
+			{
+				// just continue
+			}
+		}
+
+		return string_parts_byte;
+	}
+
+	/**
+	 * Removes all occurances of a string from the front of another string in
+	 * a case-sensitive manner.
+	 *
+	 * @param source The string in which the matching will be done.
+	 * @param stringToStrip The string that will be stripped from the front.
+	 * @return A new <code>String</code> containing the stripped result.
+	 * @since 1.0
+	 */
+	public static String stripFromFront(String source, String stringToStrip)
+	{
+		return stripFromFront(source, stringToStrip, true);
+	}
+
+	/**
+	 * Removes all occurances of a string from the front of another string.
+	 *
+	 * @param source The string in which the matching will be done.
+	 * @param stringToStrip The string that will be stripped from the front.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return A new <code>String</code> containing the stripping result.
+	 * @since 1.0
+	 */
+	public static String stripFromFront(String source, String stringToStrip, boolean matchCase)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		if (null == stringToStrip)
+		{
+			return source;
+		}
+
+		int strip_length = stringToStrip.length();
+		int new_index = 0;
+		int last_index = 0;
+
+		String  source_lookup_reference = null;
+		if (!matchCase)
+		{
+			source_lookup_reference = source.toLowerCase();
+			stringToStrip = stringToStrip.toLowerCase();
+		}
+		else
+		{
+			source_lookup_reference = source;
+		}
+
+		new_index = source_lookup_reference.indexOf(stringToStrip);
+		if (0 == new_index)
+		{
+			do
+			{
+				last_index = new_index;
+				new_index = source_lookup_reference.indexOf(stringToStrip, new_index+strip_length);
+			}
+			while (new_index != -1 &&
+				   new_index == last_index+strip_length);
+
+			return source.substring(last_index+strip_length);
+		}
+		else
+		{
+			return source;
+		}
+	}
+
+	/**
+	 * Removes all occurances of a string from the end of another string in a
+	 * case-sensitive manner.
+	 *
+	 * @param source The string in which the matching will be done.
+	 * @param stringToStrip The string that will be stripped from the end.
+	 * @return A new <code>String</code> containing the stripped result.
+	 * @since 1.0
+	 */
+	public static String stripFromEnd(String source, String stringToStrip)
+	{
+		return stripFromEnd(source, stringToStrip, true);
+	}
+
+	/**
+	 * Removes all occurances of a string from the end of another string.
+	 *
+	 * @param source The string in which the matching will be done.
+	 * @param stringToStrip The string that will be stripped from the end.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return A new <code>String</code> containing the stripped result.
+	 * @since 1.0
+	 */
+	public static String stripFromEnd(String source, String stringToStrip, boolean matchCase)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		if (null == stringToStrip)
+		{
+			return source;
+		}
+
+		int strip_length = stringToStrip.length();
+		int new_index = 0;
+		int last_index = 0;
+
+		String  source_lookup_reference = null;
+		if (!matchCase)
+		{
+			source_lookup_reference = source.toLowerCase();
+			stringToStrip = stringToStrip.toLowerCase();
+		}
+		else
+		{
+			source_lookup_reference = source;
+		}
+
+		new_index = source_lookup_reference.lastIndexOf(stringToStrip);
+		if (new_index != -1 &&
+			source.length() == new_index+strip_length)
+		{
+			do
+			{
+				last_index = new_index;
+				new_index = source_lookup_reference.lastIndexOf(stringToStrip, last_index-1);
+			}
+			while (new_index != -1 &&
+				   new_index == last_index-strip_length);
+
+			return source.substring(0, last_index);
+		}
+		else
+		{
+			return source;
+		}
+	}
+
+	/**
+	 * Searches for a string within a specified string in a case-sensitive
+	 * manner and replaces every match with another string.
+	 *
+	 * @param source The string in which the matching parts will be replaced.
+	 * @param stringToReplace The string that will be searched for.
+	 * @param replacementString The string that will replace each matching
+	 * part.
+	 * @return A new <code>String</code> object containing the replacement
+	 * result.
+	 * @since 1.0
+	 */
+	public static String replace(String source, String stringToReplace, String replacementString)
+	{
+		return replace(source, stringToReplace, replacementString, true);
+	}
+
+	/**
+	 * Searches for a string within a specified string and replaces every
+	 * match with another string.
+	 *
+	 * @param source The string in which the matching parts will be replaced.
+	 * @param stringToReplace The string that will be searched for.
+	 * @param replacementString The string that will replace each matching
+	 * part.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return A new <code>String</code> object containing the replacement
+	 * result.
+	 * @since 1.0
+	 */
+	public static String replace(String source, String stringToReplace, String replacementString, boolean matchCase)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		if (null == stringToReplace)
+		{
+			return source;
+		}
+
+		if (null == replacementString)
+		{
+			return source;
+		}
+
+		Iterator<String> string_parts = split(source, stringToReplace, matchCase).iterator();
+		StringBuilder		new_string = new StringBuilder();
+
+		while (string_parts.hasNext())
+		{
+			String string_part = string_parts.next();
+			new_string.append(string_part);
+			if (string_parts.hasNext())
+			{
+				new_string.append(replacementString);
+			}
+		}
+
+		return new_string.toString();
+	}
+
+	/**
+	 * Creates a new string that contains the provided string a number of
+	 * times.
+	 *
+	 * @param source The string that will be repeated.
+	 * @param count The number of times that the string will be repeated.
+	 * @return A new <code>String</code> object containing the repeated
+	 * concatenation result.
+	 * @since 1.0
+	 */
+	public static String repeat(String source, int count)
+	{
+		if (null == source)
+		{
+			return null;
+		}
+
+		StringBuilder new_string = new StringBuilder();
+		while (count > 0)
+		{
+			new_string.append(source);
+			count --;
+		}
+
+		return new_string.toString();
+	}
+
+	/**
+	 * Creates a new array of <code>String</code> objects, containing the
+	 * elements of a supplied <code>Iterator</code>.
+	 *
+	 * @param iterator The iterator containing the elements to create the
+	 * array with.
+	 * @return The new <code>String</code> array.
+	 * @since 1.0
+	 */
+	public static String[] toStringArray(Iterator<String> iterator)
+	{
+		if (null == iterator)
+		{
+			return new String[0];
+		}
+
+		ArrayList<String>   strings = new ArrayList<String>();
+
+		while (iterator.hasNext())
+		{
+			strings.add(iterator.next());
+		}
+
+		String[] string_array = new String[strings.size()];
+		strings.toArray(string_array);
+
+		return string_array;
+	}
+
+	/**
+	 * Creates a new <code>ArrayList</code>, containing the elements of a
+	 * supplied array of <code>String</code> objects.
+	 *
+	 * @param stringArray The array of <code>String</code> objects that have
+	 * to be converted.
+	 * @return The new <code>ArrayList</code> with the elements of the
+	 * <code>String</code> array.
+	 * @since 1.0
+	 */
+	public static ArrayList<String> toArrayList(String[] stringArray)
+	{
+		ArrayList<String> strings = new ArrayList<String>();
+
+		if (null == stringArray)
+		{
+			return strings;
+		}
+
+		for (String element : stringArray)
+		{
+			strings.add(element);
+		}
+
+		return strings;
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied <code>Collection</code> of <code>String</code> objects joined
+	 * by a given seperator.
+	 *
+	 * @param collection The <code>Collection</code> containing the elements
+	 * to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(Collection collection, String seperator)
+	{
+		if (null == collection)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == collection.size())
+		{
+			return "";
+		}
+		else
+		{
+			StringBuilder result = new StringBuilder();
+			for (Object element : collection)
+			{
+				result.append(String.valueOf(element));
+				result.append(seperator);
+			}
+
+			result.setLength(result.length()-seperator.length());
+			return result.toString();
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The object array containing the elements to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(Object[] array, String seperator)
+	{
+		return join(array, seperator, null, false);
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The object array containing the elements to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @param delimiter The delimiter used to surround the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(Object[] array, String seperator, String delimiter)
+	{
+		return join(array, seperator, delimiter, false);
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The object array containing the elements to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @param delimiter The delimiter used to surround the string elements.
+	 * @param encodeStrings Indicates whether the characters of the string
+	 * representation of the Array values should be encoded.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(Object[] array, String seperator, String delimiter, boolean encodeStrings)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (null == delimiter)
+		{
+			delimiter = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int				current_index = 0;
+			String			array_value = null;
+			StringBuilder	result = new StringBuilder();
+			while (current_index < array.length - 1)
+			{
+				if (null == array[current_index])
+				{
+					result.append("null");
+				}
+				else
+				{
+					array_value = String.valueOf(array[current_index]);
+					if (encodeStrings)
+					{
+						array_value = encodeString(array_value);
+					}
+					result.append(delimiter);
+					result.append(array_value);
+					result.append(delimiter);
+				}
+				result.append(seperator);
+				current_index++;
+			}
+
+			if (null == array[current_index])
+			{
+				result.append("null");
+			}
+			else
+			{
+				array_value = String.valueOf(array[current_index]);
+				if (encodeStrings)
+				{
+					array_value = encodeString(array_value);
+				}
+				result.append(delimiter);
+				result.append(array_value);
+				result.append(delimiter);
+			}
+			return result.toString();
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The boolean array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(boolean[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The byte array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(byte[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The double array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(double[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The float array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(float[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The integer array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(int[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The long array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(long[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The short array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(short[] array, String seperator)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			String result = "";
+			while (current_index < array.length - 1)
+			{
+				result = result + array[current_index] + seperator;
+				current_index++;
+			}
+
+			result = result +  array[current_index];
+			return result;
+		}
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The char array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(char[] array, String seperator)
+	{
+		return join(array, seperator, null);
+	}
+
+	/**
+	 * Creates a new <code>String</code> object, containing the elements of a
+	 * supplied array, joined by a given seperator.
+	 *
+	 * @param array The char array containing the values to join.
+	 * @param seperator The seperator used to join the string elements.
+	 * @param delimiter The delimiter used to surround the string elements.
+	 * @return A new <code>String</code> with the join result.
+	 * @since 1.0
+	 */
+	public static String join(char[] array, String seperator, String delimiter)
+	{
+		if (null == array)
+		{
+			return null;
+		}
+
+		if (null == seperator)
+		{
+			seperator = "";
+		}
+
+		if (null == delimiter)
+		{
+			delimiter = "";
+		}
+
+		if (0 == array.length)
+		{
+			return "";
+		}
+		else
+		{
+			int current_index = 0;
+			StringBuilder	result = new StringBuilder();
+			while (current_index < array.length - 1)
+			{
+				result.append(delimiter);
+				result.append(array[current_index]);
+				result.append(delimiter);
+				result.append(seperator);
+				current_index++;
+			}
+
+			result.append(delimiter);
+			result.append(String.valueOf(array[current_index]));
+			result.append(delimiter);
+			return result.toString();
+		}
+	}
+
+	/**
+	 * Returns an array that contains all the occurances of a substring in a
+	 * string in the correct order. The search will be performed in a
+	 * case-sensitive manner.
+	 *
+	 * @param source The <code>String</code> object that will be searched in.
+	 * @param substring The string whose occurances will we counted.
+	 * @return An <code>int[]</code> array containing the indices of the
+	 * substring.
+	 * @since 1.0
+	 */
+	public static int[] indicesOf(String source, String substring)
+	{
+		return indicesOf(source, substring, true);
+	}
+
+	/**
+	 * Returns an array that contains all the occurances of a substring in a
+	 * string in the correct order.
+	 *
+	 * @param source The <code>String</code> object that will be searched in.
+	 * @param substring The string whose occurances will we counted.
+	 * @param matchCase A <code>boolean</code> indicating if the match is
+	 * going to be performed in a case-sensitive manner or not.
+	 * @return An <code>int[]</code> array containing the indices of the
+	 * substring.
+	 * @since 1.0
+	 */
+	public static int[] indicesOf(String source, String substring, boolean matchCase)
+	{
+		if (null == source ||
+			null == substring)
+		{
+			return new int[0];
+		}
+
+		String  source_lookup_reference = null;
+		if (!matchCase)
+		{
+			source_lookup_reference = source.toLowerCase();
+			substring = substring.toLowerCase();
+		}
+		else
+		{
+			source_lookup_reference = source;
+		}
+
+		int current_index = 0;
+		int substring_index = 0;
+		int count = count(source_lookup_reference, substring);
+		int[] indices = new int[count];
+		int counter = 0;
+
+		while (current_index < source.length()-1)
+		{
+			substring_index = source_lookup_reference.indexOf(substring, current_index);
+
+			if (-1 == substring_index)
+			{
+				break;
+			}
+			else
+			{
+				current_index = substring_index + substring.length();
+				indices[counter] = substring_index;
+				counter++;
+			}
+		}
+
+		return indices;
+	}
+
+	/**
+	 * Matches a collection of regular expressions against a string.
+	 *
+	 * @param value The <code>String</code> that will be checked.
+	 * @param regexps The collection of regular expressions against which the
+	 * match will be performed.
+	 * @return The <code>Matcher</code> instance that corresponds to the
+	 * <code>String</code> that returned a successful match; or
+	 * <p><code>null</code> if no match could be found.
+	 * @since 1.0
+	 */
+	public static Matcher getMatchingRegexp(String value, Collection<Pattern> regexps)
+	{
+		if (value != null &&
+			value.length() > 0 &&
+			regexps != null &&
+			regexps.size() > 0)
+		{
+			Matcher matcher = null;
+			for (Pattern regexp : regexps)
+			{
+				matcher = regexp.matcher(value);
+				if (matcher.matches())
+				{
+					return matcher;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Matches a collection of strings against a regular expression.
+	 *
+	 * @param values The <code>Collection</code> of <code>String</code>
+	 * objects that will be checked.
+	 * @param regexp The regular expression <code>Pattern</code> against which
+	 * the matches will be performed.
+	 * @return The <code>Matcher</code> instance that corresponds to the
+	 * <code>String</code> that returned a successful match; or
+	 * <p><code>null</code> if no match could be found.
+	 * @since 1.0
+	 */
+	public static Matcher getRegexpMatch(Collection<String> values, Pattern regexp)
+	{
+		if (values != null &&
+			values.size() > 0 &&
+			regexp != null)
+		{
+			Matcher matcher = null;
+			for (String value : values)
+			{
+				matcher = regexp.matcher(value);
+				if (matcher.matches())
+				{
+					return matcher;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Checks if the name filters through an including and an excluding
+	 * regular expression.
+	 *
+	 * @param name The <code>String</code> that will be filtered.
+	 * @param included The regular expressions that needs to succeed
+	 * @param excluded The regular expressions that needs to fail
+	 * @return <code>true</code> if the name filtered through correctly; or
+	 * <p><code>false</code> otherwise.
+	 * @since 1.0
+	 */
+	public static boolean filter(String name, Pattern included, Pattern excluded)
+	{
+		Pattern[] included_array = null;
+		if (included != null)
+		{
+			included_array = new Pattern[] {included};
+		}
+
+		Pattern[] excluded_array = null;
+		if (excluded != null)
+		{
+			excluded_array = new Pattern[] {excluded};
+		}
+
+		return filter(name, included_array, excluded_array);
+	}
+
+	/**
+	 * Checks if the name filters through a series of including and excluding
+	 * regular expressions.
+	 *
+	 * @param name The <code>String</code> that will be filtered.
+	 * @param included An array of regular expressions that need to succeed
+	 * @param excluded An array of regular expressions that need to fail
+	 * @return <code>true</code> if the name filtered through correctly; or
+	 * <p><code>false</code> otherwise.
+	 * @since 1.0
+	 */
+	public static boolean filter(String name, Pattern[] included, Pattern[] excluded)
+	{
+		if (null == name)
+		{
+			return false;
+		}
+
+		boolean accepted = false;
+
+		// retain only the includes
+		if (null == included)
+		{
+			accepted = true;
+		}
+		else
+		{
+			for (Pattern pattern : included)
+			{
+				if (pattern != null &&
+					pattern.matcher(name).matches())
+				{
+					accepted = true;
+					break;
+				}
+			}
+		}
+
+		// remove the excludes
+		if (accepted &&
+			excluded != null)
+		{
+			for (Pattern pattern : excluded)
+			{
+				if (pattern != null &&
+					pattern.matcher(name).matches())
+				{
+					accepted = false;
+					break;
+				}
+			}
+		}
+
+		return accepted;
+	}
+
+	/**
+	 * Ensure that the first character of the provided string is upper case.
+	 *
+	 * @param source The <code>String</code> to capitalize.
+	 * @return The capitalized <code>String</code>.
+	 * @since 1.0
+	 */
+	public static String capitalize(String source)
+	{
+		if (source == null || source.length() == 0)
+		{
+			return source;
+		}
+
+		if (source.length() > 1 &&
+			Character.isUpperCase(source.charAt(0)))
+		{
+			return source;
+		}
+
+		char chars[] = source.toCharArray();
+		chars[0] = Character.toUpperCase(chars[0]);
+		return new String(chars);
+	}
+
+	/**
+	 * Ensure that the first character of the provided string lower case.
+	 *
+	 * @param source The <code>String</code> to uncapitalize.
+	 * @return The uncapitalized <code>String</code>.
+	 * @since 1.5
+	 */
+	public static String uncapitalize(String source)
+	{
+		if (source == null || source.length() == 0)
+		{
+			return source;
+		}
+
+		if (source.length() > 1 &&
+			Character.isLowerCase(source.charAt(0)))
+		{
+			return source;
+		}
+
+		char chars[] = source.toCharArray();
+		chars[0] = Character.toLowerCase(chars[0]);
+		return new String(chars);
+	}
+
+	private static String convertUrl(String source, Pattern pattern, boolean shorten, boolean sanitize, boolean no_follow)
+	{
+		int max_length = 1024;
+
+		String result = source;
+
+		Matcher url_matcher = pattern.matcher(source);
+		boolean found = url_matcher.find();
+		if (found)
+		{
+			String visual_url = null;
+			String actual_url = null;
+			int last = 0;
+			StringBuilder sb = new StringBuilder();
+			do
+			{
+				actual_url = url_matcher.group(1);
+				if (url_matcher.groupCount() > 1)
+				{
+					visual_url = url_matcher.group(2);
+				}
+				else
+				{
+					visual_url = actual_url;
+				}
+
+				if (sanitize)
+				{
+					// defang javascript
+					actual_url = StringUtils.replace(actual_url, "javascript:", "");
+
+					// fill in http:// for URLs that don't begin with /
+					if ((actual_url.indexOf("://") == -1) &&
+						(!actual_url.startsWith("/")))
+					{
+						actual_url = "http://"+actual_url;
+					}
+				}
+
+				if (pattern.equals(BBCODE_BAREURL))
+				{
+					sb.append(source.substring(last, url_matcher.start(1)));
+				}
+				else
+				{
+					sb.append(source.substring(last, url_matcher.start(0)));
+				}
+				sb.append("<a href=\"");
+				sb.append(actual_url);
+				sb.append("\"");
+				if (actual_url.startsWith("http://") ||
+					actual_url.startsWith("https://"))
+				{
+					sb.append(" target=\"_blank\"");
+				}
+				if (no_follow)
+				{
+					sb.append(" rel=\"nofollow\"");
+				}
+				sb.append(">");
+				if (visual_url.length() <= max_length || !shorten)
+				{
+					sb.append(visual_url);
+				}
+				else
+				{
+					String ellipsis = "...";
+					int query_index = visual_url.indexOf("?");
+
+					// hack query string off
+					// keep '?'
+					if (query_index != -1)
+					{
+						visual_url = visual_url.substring(0, query_index + 1) + ellipsis;
+					}
+
+					if (visual_url.length() >= max_length)
+					{
+						int last_slash = visual_url.lastIndexOf("/");
+						int start_slash = visual_url.indexOf("/", visual_url.indexOf("://")+3);
+
+						if (last_slash != start_slash)
+						{
+							visual_url = visual_url.substring(0, start_slash + 1) + ellipsis + visual_url.substring(last_slash);
+						}
+					}
+
+					sb.append(visual_url);
+				}
+				sb.append("</a>");
+
+				if (pattern.equals(BBCODE_BAREURL))
+				{
+					last = url_matcher.end(1);
+				}
+				else
+				{
+					last = url_matcher.end(0);
+				}
+
+				found = url_matcher.find();
+			}
+			while (found);
+
+			sb.append(source.substring(last));
+			result = sb.toString();
+		}
+
+		return result;
+	}
+
+	private static String parseBBCode(String source, boolean shorten, boolean sanitize, boolean convert_bare, boolean no_follow)
+	{
+		String result = source;
+
+		result = StringUtils.replace(result, "[b]", "<b>", false);
+		result = StringUtils.replace(result, "[/b]", "</b>", false);
+		result = StringUtils.replace(result, "[u]", "<u>", false);
+		result = StringUtils.replace(result, "[/u]", "</u>", false);
+		result = StringUtils.replace(result, "[i]", "<i>", false);
+		result = StringUtils.replace(result, "[/i]", "</i>", false);
+		result = StringUtils.replace(result, "[pre]", "<pre>", false);
+		result = StringUtils.replace(result, "[/pre]", "</pre>", false);
+
+		String			resultCopy = result;
+		String			resultLowerCopy = result.toLowerCase();
+		StringBuilder	buffer = new StringBuilder();
+		int startIndex;
+		int endIndex;
+		while (-1 != (startIndex = resultLowerCopy.indexOf("[*]")))
+		{
+			int begin = resultLowerCopy.indexOf("[list]", startIndex + 3);
+			int end = resultLowerCopy.indexOf("[/list]", startIndex + 3);
+			int next = resultLowerCopy.indexOf("[*]", startIndex + 3); // 3 == sizeof [*]
+
+			if (begin == -1)
+			{
+				begin = Integer.MAX_VALUE;
+			}
+
+			if (end == -1)
+			{
+				end = Integer.MAX_VALUE;
+			}
+
+			if (next == -1)
+			{
+				next = Integer.MAX_VALUE;
+			}
+
+			if (next < begin && next < end)
+			{
+				endIndex = next;
+			}
+			else if (begin < next && begin < end)
+			{
+				endIndex = begin;
+			}
+			else if (end < next && end < begin)
+			{
+				endIndex = end;
+			}
+			else
+			{
+				endIndex = resultLowerCopy.length();
+			}
+
+			buffer
+				.append(resultCopy.substring(0, startIndex))
+				.append("<li>")
+				.append(resultCopy.substring(startIndex + 3, endIndex)) // 3 == sizeof [*]
+				.append("</li>");
+
+			resultCopy = resultCopy.substring(endIndex);
+			resultLowerCopy = resultLowerCopy.substring(endIndex);
+		}
+		buffer.append(resultCopy.substring(0));
+
+		result = buffer.toString();
+
+		result = StringUtils.replace(result, "[list]", "<ul>", false);
+		result = StringUtils.replace(result, "[/list]", "</ul>", false);
+
+		Matcher color_matcher = BBCODE_COLOR.matcher(result);
+		result = color_matcher.replaceAll("<font color=\"$1\">");
+		result = StringUtils.replace(result, "[/color]", "</font>", false);
+
+		Matcher size_matcher = BBCODE_SIZE.matcher(result);
+		result = size_matcher.replaceAll("<font size=\"$1\">");
+		result = StringUtils.replace(result, "[/size]", "</font>", false);
+
+		result = convertUrl(result, BBCODE_URL_SHORT, shorten, sanitize, no_follow);
+		result = convertUrl(result, BBCODE_URL_LONG, shorten, sanitize, no_follow);
+
+		if (convert_bare)
+		{
+			result = convertUrl(result, BBCODE_BAREURL, shorten, sanitize, no_follow);
+		}
+
+		Matcher img_matcher = BBCODE_IMG.matcher(result);
+		result = img_matcher.replaceAll("<div class=\"bbcode_img\"><img src=\"$1\" border=\"0\" alt=\"\" /></div>");
+
+		Matcher quote_matcher_long = BBCODE_QUOTE_LONG.matcher(result);
+		result = quote_matcher_long.replaceAll("<div class=\"quoteaccount\">$1:</div><div class=\"quotebody\">");
+		result = StringUtils.replace(result, "[quote]", "<div class=\"quotebody\">", false);
+		result = StringUtils.replace(result, "[/quote]", "</div>", false);
+
+		result = StringUtils.replace(result, "\r\n", "<br />\r");
+		result = StringUtils.replace(result, "\n", "<br />\n");
+		result = StringUtils.replace(result, "\r", "\r\n");
+
+		// remove the BR that could be added due to code formatting ppl
+		// use to format lists
+		result = StringUtils.replace(result, "ul><br />\r\n", "ul>\r\n");
+		result = StringUtils.replace(result, "ul><br />\n", "ul>\n");
+
+		return result;
+	}
+
+	/**
+	 * Converts a <code>String</code> to a <code>boolean</code> value.
+	 *
+	 * @param value The <code>String</code> to convert.
+	 * @return The corresponding <code>boolean</code> value.
+	 * @since 1.0
+	 */
+	public static boolean convertToBoolean(String value)
+	{
+		if (null == value)
+		{
+			return false;
+		}
+
+		if (value.equals("1") ||
+			value.equalsIgnoreCase("t") ||
+			value.equalsIgnoreCase("true") ||
+			value.equalsIgnoreCase("y") ||
+			value.equalsIgnoreCase("yes") ||
+			value.equalsIgnoreCase("on"))
+		{
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Converts all tabs on a line to spaces according to the provided tab
+	 * width.
+	 *
+	 * @param line The line whose tabs have to be converted.
+	 * @param tabWidth The tab width.
+	 * @return A new <code>String</code> object containing the line with the
+	 * replaced tabs.
+	 * @since 1.0
+	 */
+	public static String convertTabsToSpaces(String line, int tabWidth)
+	{
+		StringBuilder result = new StringBuilder();
+		int tab_index = -1;
+		int last_tab_index = 0;
+		int added_chars = 0;
+		int tab_size;
+		while ((tab_index = line.indexOf("\t", last_tab_index)) != -1)
+		{
+			tab_size = tabWidth-((tab_index+added_chars)%tabWidth);
+			if (0 == tab_size)
+			{
+				tab_size = tabWidth;
+			}
+			added_chars += tab_size-1;
+			result.append(line.substring(last_tab_index, tab_index));
+			result.append(StringUtils.repeat(" ", tab_size));
+			last_tab_index = tab_index+1;
+		}
+		if (0 == last_tab_index)
+		{
+			return line;
+		}
+		else
+		{
+			result.append(line.substring(last_tab_index));
+		}
+
+		return result.toString();
+	}
+
+	/**
+	 * Ensures that all whitespace is removed from a <code>String</code>.
+	 * <p>It also works with a <code>null</code> argument.
+	 *
+	 * @param source The <code>String</code> to trim.
+	 * @return The trimmed <code>String</code>.
+	 * @since 1.0
+	 */
+	public static String trim(String source)
+	{
+		if (source == null || source.length() == 0)
+		{
+			return source;
+		}
+
+		return source.trim();
+	}
+
+    /**
+     * Reformats a string where lines that are longer than <tt>width</tt>
+     * are split apart at the earliest wordbreak or at maxLength, whichever is
+     * sooner. If the width specified is less than 5 or greater than the input
+     * Strings length the string will be returned as is.
+     * <p>
+     * Please note that this method can be lossy - trailing spaces on wrapped
+     * lines may be trimmed.
+     *
+     * @param input the String to reformat.
+     * @param width the maximum length of any one line.
+     * @return a new String with reformatted as needed.
+     */
+    public static String wordWrap(String input, int width, Locale locale)
+    {
+        // handle invalid input
+        if (input == null)
+        {
+            return "";
+        }
+        else if (width < 5)
+        {
+            return input;
+        }
+        else if (width >= input.length())
+        {
+            return input;
+        }
+
+        // default locale
+        if (locale == null)
+        {
+            locale = Locale.US;
+        }
+
+        StringBuilder buffer = new StringBuilder(input.length());
+		int		current_index = 0;
+		int		delimiter_index = 0;
+		String	seperator = "\n";
+		String	line;
+
+		// go over the input string and jump from line to line
+		while (current_index <= input.length())
+		{
+			// look for the next linebreak
+			delimiter_index = input.indexOf(seperator, current_index);
+
+			// get the line that corresponds to it
+			if (-1 == delimiter_index)
+			{
+				line = new String(input.substring(current_index, input.length()));
+				current_index = input.length() + 1;
+			}
+			else
+			{
+				line = new String(input.substring(current_index, delimiter_index));
+				current_index = delimiter_index + seperator.length();
+			}
+
+			// handle the wrapping of the line
+			BreakIterator breaks = BreakIterator.getLineInstance(locale);
+			breaks.setText(line);
+
+			int line_start = 0;
+			int start = breaks.first();
+			int end = breaks.next();
+			while (end != BreakIterator.DONE)
+			{
+				// check if the width has been exceeded
+				if (end - 1 - line_start >= width)
+				{
+					boolean break_line = true;
+
+					// first check if the last characters were spaces,
+					// if they were and by removing them the width is not
+					// exceeded, just continue
+					if (Character.isWhitespace(line.charAt(end - 1)))
+					{
+						for (int j = end - 1; j >= 0; j--)
+						{
+							if (!Character.isWhitespace(line.charAt(j)))
+							{
+								if (j - line_start < width)
+								{
+									break_line = false;
+								}
+
+								break;
+							}
+						}
+					}
+
+					if (break_line)
+					{
+						String line_breaked = line.substring(line_start, start);
+						// this can happen with trailing whitespace
+						if (line_breaked.length() > width)
+						{
+							line_breaked = line_breaked.substring(0, width);
+						}
+						buffer.append(line_breaked);
+
+						buffer.append("\n");
+
+						line_start = start;
+					}
+				}
+
+				start = end;
+				end = breaks.next();
+			}
+
+			if (line_start < line.length())
+			{
+				buffer.append(line.substring(line_start));
+			}
+
+			if (delimiter_index != -1)
+			{
+				buffer.append("\n");
+			}
+		}
+
+		return buffer.toString();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java
new file mode 100644
index 0000000..46a2cc1
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java
@@ -0,0 +1,758 @@
+package com.ximple.eofms.util;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+
+public abstract class TPCLIDConverter
+{
+    private final static int SX1200 = 800;
+    private final static int SY1200 = 500;
+
+    public static String CoordinateToTpclId(Coordinate dp)
+    {
+        long orgX, orgY;
+        int mapX, mapY;
+        int shiftX, shiftY;
+        int dx1, dy1;
+        int dx2, dy2;
+
+        if (dp == null)
+        {
+            return null;
+        }
+
+        double X = dp.x;
+        double Y = dp.y;
+
+        String CoordToTPCLID = "";
+
+        char mapID = CoordinateToSingleMapID(dp);
+
+        if (mapID == 'w')
+        {
+            return "";
+        }
+
+        /* get the origin point of mapID */
+        Coordinate Point = getOriginPoint("" + mapID, Integer.MAX_VALUE);
+
+        if (Point == null)
+        {
+            return null;
+        }
+
+        orgX = (long) Point.x;
+        orgY = (long) Point.y;
+
+        mapX = intDivision((X - orgX), SX1200);
+
+        if (mapID == 'Z' && mapX >= 100)
+        {
+            mapX = mapX - 100;
+        }
+        mapY = intDivision((Y - orgY), SY1200);
+        shiftX = (int) (X - orgX) % SX1200;
+        shiftY = (int) (Y - orgY) % SY1200;
+        dx1 = intDivision((shiftX % 100), 10);
+        dy1 = intDivision((shiftY % 100), 10);
+        dx2 = (shiftX % 100) % 10;
+        dy2 = (shiftY % 100) % 10;
+
+
+        CoordToTPCLID = "" + mapID;
+        CoordToTPCLID = CoordToTPCLID + dataFormat(mapX);
+        CoordToTPCLID = CoordToTPCLID + dataFormat(mapY);
+        CoordToTPCLID = CoordToTPCLID + intToAscii(shiftX / 100 + asciiToInt("A"));
+        CoordToTPCLID = CoordToTPCLID + intToAscii(shiftY / 100 + asciiToInt("A"));
+        CoordToTPCLID = CoordToTPCLID + dx1 + dy1 + dx2 + dy2;
+
+        return CoordToTPCLID;
+    }
+
+    public static char CoordinateToSingleMapID(Coordinate dp)
+    {
+        char mapID = 'w';
+
+        String[] strY = StringUtils.splitToArray(Double.toString(dp.y), ".");
+        String[] strX = StringUtils.splitToArray(Double.toString(dp.x), ".");
+
+        int intY = Integer.parseInt(strY[0]);
+        int intX = Integer.parseInt(strX[0]);
+
+        if (intY > 2944000)
+        {
+            return mapID;
+        }
+        if (intY >= 2894000 && intY <= 2944000 && intX >= 10000 && intX <= 90000)
+        {
+            mapID = 'S';
+            return mapID;
+        }
+        if (intY >= 2614000 && intY <= 2664000 && intX >= 10000 && intX <= 66000)
+        {
+            mapID = 'X';
+            return mapID;
+        }
+        if (intY >= 2564000 && intY <= 2614000 && intX >= 10000 && intX <= 66000)
+        {
+            mapID = 'Y';
+            return mapID;
+        }
+        if (intY >= 2675800 && intY <= 2725800 && intX >= 10000 && intX <= 170000)
+        {
+            mapID = 'Z';
+            return mapID;
+        }
+        if (intY > 2800000)
+        {
+            return mapID;
+        }
+        if (intY >= 2750000)
+        {
+            if (intX < 170000)
+            {
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'A';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'B';
+                return mapID;
+            }
+            if (intX < 410000)
+            {
+                mapID = 'C';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2700000)
+        {
+            if (intX < 170000)
+            {
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'D';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'E';
+                return mapID;
+            }
+            if (intX < 410000)
+            {
+                mapID = 'F';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2650000)
+        {
+            if (intX < 170000)
+            {
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'G';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'H';
+                return mapID;
+            }
+            if (intX < 410000)
+            {
+                mapID = 'I';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2600000)
+        {
+            if (intX < 90000)
+            {
+                return mapID;
+            }
+            if (intX < 170000)
+            {
+                mapID = 'J';
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'K';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'L';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2550000)
+        {
+            if (intX < 90000)
+            {
+                return mapID;
+            }
+            if (intX < 170000)
+            {
+                mapID = 'M';
+                return mapID;
+            }
+
+            if (intX < 250000)
+            {
+                mapID = 'N';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'O';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2500000)
+        {
+            if (intX < 90000)
+            {
+                return mapID;
+            }
+            if (intX < 170000)
+            {
+                mapID = 'P';
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'Q';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'R';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2450000)
+        {
+            if (intX < 90000)
+            {
+                return mapID;
+            }
+            if (intX < 170000)
+            {
+                mapID = 'S';
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'T';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'U';
+                return mapID;
+            }
+            return mapID;
+        }
+        if (intY >= 2400000)
+        {
+            if (intX < 170000)
+            {
+                return mapID;
+            }
+            if (intX < 250000)
+            {
+                mapID = 'V';
+                return mapID;
+            }
+            if (intX < 330000)
+            {
+                mapID = 'W';
+                return mapID;
+            }
+            return mapID;
+        }
+        return mapID;
+    }
+
+    public static Envelope convertTpclIdToEnvelope(String tpclid)
+    {
+        String tempString = "";
+
+        Coordinate point = null;
+        Coordinate tempPoint = null;
+        double width, height;
+
+        if (tpclid.length() < 5)
+        {
+            return null;
+        }
+
+        tempString = tpclid.substring(0, 1);
+        int xmapid = Integer.parseInt(tpclid.substring(1, 3));
+
+        // Get the origin point
+        point = getOriginPoint(tempString, xmapid);
+
+        if (point == null)
+        {
+            return null;
+        }
+
+
+        tempString = tpclid.substring(1, 5);
+        width = SX1200;
+        height = SY1200;
+
+        tempPoint = twoNumberScale(tempString, 800, 500);
+        if (tempPoint != null)
+        {
+            point.x = (point.x + tempPoint.x);
+            point.y = (point.y + tempPoint.y);
+        }
+
+        if (tpclid.length() >= 7)
+        {
+            tempString = (asciiToInt(tpclid.substring(5, 6)) - 65) + "" + (asciiToInt(tpclid.substring(6, 7)) - 65);
+            tempPoint = twoNumberScale(tempString, 100);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+            width = 100.0;
+            height = 100.0;
+        }
+
+        if (tpclid.length() >= 9)
+        {
+            tempString = tpclid.substring(7, 8);
+            tempPoint = twoNumberScale(tempString, 10);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+            width = 10.0;
+            height = 10.0;
+        }
+
+        if (tpclid.length() >= 11)
+        {
+            tempString = tpclid.substring(10);
+            tempPoint = twoNumberScale(tempString, 1);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+            width = 1.0;
+            height = 1.0;
+        }
+
+        Coordinate pt2 = new Coordinate(point);
+        pt2.x += width;
+        pt2.y += height;
+        return  new Envelope(point, pt2);
+    }
+
+    public static Coordinate convertTpclIdToCoordinate(String tpclid)
+    {
+        String tempString = "";
+
+        Coordinate point = null;
+        Coordinate tempPoint = null;
+
+        if (tpclid.length() < 5)
+        {
+            return null;
+        }
+
+        tempString = tpclid.substring(0, 1);
+        int xmapid = Integer.parseInt(tpclid.substring(1, 3));
+
+        // Get the origin point
+        point = getOriginPoint(tempString, xmapid);
+
+        if (point == null)
+        {
+            return null;
+        }
+
+
+        tempString = tpclid.substring(1, 5);
+
+        tempPoint = twoNumberScale(tempString, 800, 500);
+        if (tempPoint != null)
+        {
+            point.x = (point.x + tempPoint.x);
+            point.y = (point.y + tempPoint.y);
+        }
+
+        if (tpclid.length() >= 7)
+        {
+            tempString = (asciiToInt(tpclid.substring(5, 6)) - 65) + "" + (asciiToInt(tpclid.substring(6, 7)) - 65);
+            tempPoint = twoNumberScale(tempString, 100);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+        }
+
+        if (tpclid.length() >= 9)
+        {
+            tempString = tpclid.substring(7, 9);
+            tempPoint = twoNumberScale(tempString, 10);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+        }
+
+        if (tpclid.length() >= 11)
+        {
+            tempString = tpclid.substring(10);
+            tempPoint = twoNumberScale(tempString, 1);
+            if (tempPoint != null)
+            {
+                point.x = point.x + tempPoint.x;
+                point.y = point.y + tempPoint.y;
+            }
+        }
+        return point;
+    }
+
+
+    private static int intDivision(double p1, int p2)
+    {
+        double resultValue = 0.0;
+        String result;
+        resultValue = p1 / p2;
+        result = Double.toString(resultValue);
+        String[] temp = StringUtils.splitToArray(result, ".");
+        result = temp[0];
+        return Integer.parseInt(result);
+    }
+
+
+    private static Coordinate twoNumberScale(String number, int scaleX, int scaleY)
+    {
+        Coordinate tempPoint = new Coordinate();
+
+        if (number.length() == 2)
+        {
+            tempPoint.x = Double.parseDouble(number.substring(0, 1)) * scaleX;
+            tempPoint.y = Double.parseDouble(number.substring(1, 2)) * scaleY;
+        } else if (number.length() == 1)
+        {
+            tempPoint.x = Double.parseDouble(number.substring(0, 1)) * scaleX;
+            tempPoint.y = 0;
+        } else if (number.length() == 4)
+        {
+            tempPoint.x = Double.parseDouble(number.substring(0, 2)) * scaleX;
+            tempPoint.y = Double.parseDouble(number.substring(2, 4)) * scaleY;
+        } else
+        {
+            tempPoint.x = 0;
+            tempPoint.y = 0;
+        }
+        return tempPoint;
+    }
+
+
+    private static int asciiToInt(String p1)
+    {
+        if (p1.endsWith("A")) return 65;
+        if (p1.endsWith("B")) return 66;
+        if (p1.endsWith("C")) return 67;
+        if (p1.endsWith("D")) return 68;
+        if (p1.endsWith("E")) return 69;
+        if (p1.endsWith("F")) return 70;
+        if (p1.endsWith("G")) return 71;
+        if (p1.endsWith("H")) return 72;
+        if (p1.endsWith("I")) return 73;
+        if (p1.endsWith("J")) return 74;
+        if (p1.endsWith("K")) return 75;
+        if (p1.endsWith("L")) return 76;
+        if (p1.endsWith("M")) return 77;
+        if (p1.endsWith("N")) return 78;
+        if (p1.endsWith("O")) return 79;
+        if (p1.endsWith("P")) return 80;
+        if (p1.endsWith("Q")) return 81;
+        if (p1.endsWith("R")) return 82;
+        if (p1.endsWith("S")) return 83;
+        if (p1.endsWith("T")) return 84;
+        if (p1.endsWith("U")) return 85;
+        if (p1.endsWith("V")) return 86;
+        if (p1.endsWith("W")) return 87;
+        if (p1.endsWith("X")) return 88;
+        if (p1.endsWith("Y")) return 89;
+        if (p1.endsWith("Z")) return 90;
+        return 0;
+    }
+
+
+    private static char intToAscii(int p1)
+    {
+        switch (p1)
+        {
+        case 65:
+            return 'A';
+        case 66:
+            return 'B';
+        case 67:
+            return 'C';
+        case 68:
+            return 'D';
+        case 69:
+            return 'E';
+        case 70:
+            return 'F';
+        case 71:
+            return 'G';
+        case 72:
+            return 'H';
+        case 73:
+            return 'I';
+        case 74:
+            return 'J';
+        case 75:
+            return 'K';
+        case 76:
+            return 'L';
+        case 77:
+            return 'M';
+        case 78:
+            return 'N';
+        case 79:
+            return 'O';
+        case 80:
+            return 'P';
+        case 81:
+            return 'Q';
+        case 82:
+            return 'R';
+        case 83:
+            return 'S';
+        case 84:
+            return 'T';
+        case 85:
+            return 'U';
+        case 86:
+            return 'V';
+        case 87:
+            return 'W';
+        case 88:
+            return 'X';
+        case 89:
+            return 'Y';
+        case 90:
+            return 'Z';
+        default:
+            return '1';
+        }
+    }
+
+
+    private static Coordinate getOriginPoint(String letter, int xMapId)
+    {
+        int aSwitch = asciiToInt(letter);
+        Coordinate Point = new Coordinate();
+        switch (aSwitch)
+        {
+        case 65: //A
+        {
+            Point.x = 170000;
+            Point.y = 2750000;
+            break;
+        }
+        case 66: //B
+        {
+            Point.x = 250000;
+            Point.y = 2750000;
+            break;
+        }
+        case 67: //C
+        {
+            Point.x = 330000;
+            Point.y = 2750000;
+            break;
+        }
+        case 68: //D
+        {
+            Point.x = 170000;
+            Point.y = 2700000;
+            break;
+        }
+        case 69: //E
+        {
+            Point.x = 250000;
+            Point.y = 2700000;
+            break;
+        }
+        case 70: //F
+        {
+            Point.x = 330000;
+            Point.y = 2700000;
+            break;
+        }
+        case 71: //G
+        {
+            Point.x = 170000;
+            Point.y = 2650000;
+            break;
+        }
+        case 72: //H
+        {
+            Point.x = 250000;
+            Point.y = 2650000;
+            break;
+        }
+        case 73: //I
+        {
+            Point.x = 330000;
+            Point.y = 2650000;
+            break;
+        }
+        case 74: //J
+        {
+            Point.x = 90000;
+            Point.y = 2600000;
+            break;
+        }
+        case 75: //K
+        {
+            Point.x = 170000;
+            Point.y = 2600000;
+            break;
+        }
+        case 76: //L
+        {
+            Point.x = 250000;
+            Point.y = 2600000;
+            break;
+        }
+        case 77: //M
+        {
+            Point.x = 90000;
+            Point.y = 2550000;
+            break;
+        }
+        case 78: //N
+        {
+            Point.x = 170000;
+            Point.y = 2550000;
+            break;
+        }
+        case 79: //O
+        {
+            Point.x = 250000;
+            Point.y = 2550000;
+            break;
+        }
+        case 80: //P
+        {
+            Point.x = 90000;
+            Point.y = 2500000;
+            break;
+        }
+        case 81: //Q
+        {
+            Point.x = 170000;
+            Point.y = 2500000;
+            break;
+        }
+        case 82: //R
+        {
+            Point.x = 250000;
+            Point.y = 2500000;
+            break;
+        }
+        case 83: //���� S
+        {
+            Point.x = 10000;
+            Point.y = 2894000;
+            break;
+        }
+        case 84: //T
+        {
+            Point.x = 170000;
+            Point.y = 2450000;
+            break;
+        }
+        case 85: //U
+        {
+            Point.x = 250000;
+            Point.y = 2450000;
+            break;
+        }
+        case 86: //V
+        {
+            Point.x = 170000;
+            Point.y = 2400000;
+            break;
+        }
+        case 87: // W
+        {
+            Point.x = 250000;
+            Point.y = 2400000;
+            break;
+        }
+        case 88: //��� X
+        {
+            Point.x = 10000;
+            Point.y = 2614000;
+            break;
+        }
+        case 89: //��� Y
+        {
+            Point.x = 10000;
+            Point.y = 2564000;
+            break;
+        }
+        case 90: //���� ��Z
+        {
+            Point.x = (xMapId < 51) ? 90000 : 10000;
+            Point.y = 2675800;
+            break;
+        }
+        default:
+        {
+            return null;
+        }
+        }
+        return Point;
+    }
+
+
+    private static Coordinate twoNumberScale(String number, int scale)
+    {
+        return twoNumberScale(number, scale, scale);
+    }
+
+
+    private static String dataFormat(int p1)
+    {
+        String s1 = Integer.toString(p1);
+        if (s1.length() < 2)
+            s1 = "0" + s1;
+        return s1;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
new file mode 100644
index 0000000..e9a6445
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
@@ -0,0 +1,508 @@
+package com.ximple.eofms.util;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * TWDDatumConverter
+ * User: Ulysses
+ * Date: 2007/10/8
+ * Time: �U�� 01:35:03
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class TWDDatumConverter
+{
+    /*
+     *   Definition of math related value
+     */
+    private static final double COS67_5 = 0.3826834323650897717284599840304e0;
+    private static final double PI      = 3.14159265358979323e0;
+    private static final double HALF_PI = 1.570796326794896615e0;
+    private static final double DEG_RAD = 0.01745329251994329572e0;
+    private static final double RAD_DEG = 57.295779513082321031e0;
+
+    /*
+     * Definition of datum related value
+     */
+    private static final double AD_C       = 1.0026000e0;
+    private static final double TWD67_A    = 6378160.0e0;
+    private static final double TWD67_B    = 6356774.7192e0;
+    private static final double TWD67_ECC  = 0.00669454185458e0;
+    private static final double TWD67_ECC2 = 0.00673966079586e0;
+
+    // different from garmin and already knowned value, but those all value only
+    private static final double TWD67_DX = -752.32e0;
+
+    // got 5-15m accuracy. the real offical value is holded by somebody and not
+    private static final double TWD67_DY = -361.32e0;
+
+    // release to public. if can got more enough twd67/twd97 control point coordinare,
+    private static final double TWD67_DZ   = -180.51e0;
+    private static final double TWD67_RX   = -0.00000117e0;    // then we can calculate a better value than now.
+    private static final double TWD67_RY   = 0.00000184e0;     //
+    private static final double TWD67_RZ   = 0.00000098e0;     // and, also lack twd67/twd97 altitude convertion value...
+    private static final double TWD67_S    = 0.00002329e0;     //
+    private static final double TWD97_A    = 6378137.0e0;
+    private static final double TWD97_B    = 6356752.3141e0;
+    private static final double TWD97_ECC  = 0.00669438002290e0;
+    private static final double TWD97_ECC2 = 0.00673949677556e0;
+    private static final double TWD67_TM2  = 0.9999e0;         // TWD67->TM2 scale
+    private static final double TWD97_TM2  = 0.9999e0;         // TWD97->TM2 scale
+
+    /*
+     * datum convert function
+     */
+    public static Coordinate toTWD97(Coordinate pt)
+    {
+        double newX, newY, newZ;
+        double r, pole, sin_lat, cos_lat;
+        double lat, lon, height;
+        double x1, y1, z1, x2, y2, z2;
+        double q, q2, t, t1, s, s1, sum, sin_b, cos_b, sin_p, cos_p;
+
+        lon    = pt.x * DEG_RAD;
+        lat    = pt.y * DEG_RAD;
+        height = pt.z * DEG_RAD;
+
+        if ((lat < -HALF_PI) && (lat > -1.001 * HALF_PI))
+        {
+            lat = -HALF_PI;
+        } else if ((lat > HALF_PI) && (lat < 1.001 * HALF_PI))
+        {
+            lat = HALF_PI;
+        } else if ((lat < -HALF_PI) || (lat > HALF_PI))
+        {
+            return null;
+        }
+
+        if (lon > PI)
+        {
+            lon -= (2 * PI);
+        }
+
+        sin_lat = Math.sin(lat);
+        cos_lat = Math.cos(lat);
+        r       = TWD67_A / (Math.sqrt(1.0 - TWD67_ECC * sin_lat * sin_lat));
+        x1      = (r + height) * cos_lat * Math.cos(lon);
+        y1      = (r + height) * cos_lat * Math.sin(lon);
+        z1      = ((r * (1 - TWD67_ECC)) + height) * sin_lat;
+        x2      = x1 + TWD67_DX + TWD67_S * (lon + TWD67_RZ * lat - TWD67_RY * height);
+        y2      = y1 + TWD67_DY + TWD67_S * (-TWD67_RZ * lon + lat + TWD67_RX * height);
+        z2      = z1 + TWD67_DZ + TWD67_S * (TWD67_RY * lon - TWD67_RX * lat + height);
+        pole    = 0.0;
+
+        if (x2 != 0.0)
+        {
+            lon = Math.atan2(y2, x2);
+        } else
+        {
+            if (y2 > 0)
+            {
+                lon = HALF_PI;
+            } else if (y2 < 0)
+            {
+                lon = -HALF_PI;
+            } else
+            {
+                pole = 1;
+                lon  = 0;
+
+                if (z2 > 0)
+                {
+                    lat = HALF_PI;
+                } else if (z2 < 0)
+                {
+                    lat = -HALF_PI;
+                } else
+                {
+                    lat  = HALF_PI;
+                    newX = lon * RAD_DEG;
+                    newY = lat * RAD_DEG;
+                    newZ = -TWD97_B;
+
+                    return new Coordinate(newX, newY, newZ);
+                }
+            }
+        }
+
+        q2    = x2 * x2 + y2 * y2;
+        q     = Math.sqrt(q2);
+        t     = z2 * AD_C;
+        s     = Math.sqrt(t * t + q2);
+        sin_b = t / s;
+        cos_b = q / s;
+        t1    = z2 + TWD97_B * TWD97_ECC2 * sin_b * sin_b * sin_b;
+        sum   = q - TWD97_A * TWD97_ECC * cos_b * cos_b * cos_b;
+        s1    = Math.sqrt(t1 * t1 + sum * sum);
+        sin_p = t1 / s1;
+        cos_p = sum / s1;
+        r     = TWD97_A / Math.sqrt(1.0 - TWD97_ECC * sin_p * sin_p);
+
+        if (cos_p >= COS67_5)
+        {
+            height = q / cos_p - r;
+        } else if (cos_p <= -COS67_5)
+        {
+            height = q / -cos_p - r;
+        } else
+        {
+            height = z2 / sin_p + r * (TWD97_ECC - 1.0);
+        }
+
+        if (pole !=0.0)
+        {
+            lat = Math.atan(sin_p / cos_p);
+        }
+
+        newX = lon * RAD_DEG;
+        newY = lat * RAD_DEG;
+        newZ = height;
+        return new Coordinate(newX, newY, newZ);
+    }
+
+    public static Coordinate toTWD67(Coordinate pt)
+    {
+        double newX, newY, newZ;
+        double r, pole, sin_lat, cos_lat;
+        double lat, lon, height;
+        double x1, y1, z1, x2, y2, z2;
+        double q, q2, t, t1, s, s1, sum, sin_b, cos_b, sin_p, cos_p;
+
+        lon    = pt.x * DEG_RAD;
+        lat    = pt.y * DEG_RAD;
+        height = pt.z * DEG_RAD;
+
+        if ((lat < -HALF_PI) && (lat > -1.001 * HALF_PI))
+        {
+            lat = -HALF_PI;
+        } else if ((lat > HALF_PI) && (lat < 1.001 * HALF_PI))
+        {
+            lat = HALF_PI;
+        } else if ((lat < -HALF_PI) || (lat > HALF_PI))
+        {
+            return null;
+        }
+
+        if (lon > PI)
+        {
+            lon -= (2 * PI);
+        }
+
+        sin_lat = Math.sin(lat);
+        cos_lat = Math.cos(lat);
+        r       = TWD97_A / (Math.sqrt(1.0 - TWD97_ECC * sin_lat * sin_lat));
+        x1      = (r + height) * cos_lat * Math.cos(lon);
+        y1      = (r + height) * cos_lat * Math.sin(lon);
+        z1      = ((r * (1 - TWD97_ECC)) + height) * sin_lat;
+        x2      = x1 - TWD67_DX - TWD67_S * (lon + TWD67_RZ * lat - TWD67_RY * height);
+        y2      = y1 - TWD67_DY - TWD67_S * (-TWD67_RZ * lon + lat + TWD67_RX * height);
+        z2      = z1 - TWD67_DZ - TWD67_S * (TWD67_RY * lon - TWD67_RX * lat + height);
+        pole    = 0;
+
+        if (x2 != 0.0)
+        {
+            lon = Math.atan2(y2, x2);
+        } else
+        {
+            if (y2 > 0)
+            {
+                lon = HALF_PI;
+            } else if (y2 < 0)
+            {
+                lon = -HALF_PI;
+            } else
+            {
+                pole = 1;
+                lon  = 0;
+
+                if (z2 > 0)
+                {
+                    lat = HALF_PI;
+                } else if (z2 < 0)
+                {
+                    lat = -HALF_PI;
+                } else
+                {
+                    lat  = HALF_PI;
+                    newX = lon * RAD_DEG;
+                    newY = lat * RAD_DEG;
+                    newZ = -TWD67_B;
+
+                    return new Coordinate(newX, newY, newZ);
+                }
+            }
+        }
+
+        q2    = x2 * x2 + y2 * y2;
+        q     = Math.sqrt(q2);
+        t     = z2 * AD_C;
+        s     = Math.sqrt(t * t + q2);
+        sin_b = t / s;
+        cos_b = q / s;
+        t1    = z2 + TWD67_B * TWD67_ECC2 * sin_b * sin_b * sin_b;
+        sum   = q - TWD67_A * TWD67_ECC * cos_b * cos_b * cos_b;
+        s1    = Math.sqrt(t1 * t1 + sum * sum);
+        sin_p = t1 / s1;
+        cos_p = sum / s1;
+        r     = TWD67_A / Math.sqrt(1.0 - TWD67_ECC * sin_p * sin_p);
+
+        if (cos_p >= COS67_5)
+        {
+            height = q / cos_p - r;
+        } else if (cos_p <= -COS67_5)
+        {
+            height = q / -cos_p - r;
+        } else
+        {
+            height = z2 / sin_p + r * (TWD67_ECC - 1.0);
+        }
+
+        if (pole != 0.0)
+        {
+            lat = Math.atan(sin_p / cos_p);
+        }
+
+        newX = lon * RAD_DEG;
+        newY = lat * RAD_DEG;
+        newZ = height;
+        return new Coordinate(newX, newY, newZ);
+    }
+
+    public static Coordinate toTM2(double a, double ecc, double ecc2, double lat, double lon, double scale, double x, double y)
+    {
+        double x0, y0, x1, y1, m0, m1;
+        double n, t, c, A;
+        double newX, newY;
+
+        x0   = x * DEG_RAD;
+        y0   = y * DEG_RAD;
+        x1   = lon * DEG_RAD;
+        y1   = lat * DEG_RAD;
+        m0   = mercator(y1, a, ecc);
+        m1   = mercator(y0, a, ecc);
+        n    = a / Math.sqrt(1 - ecc * Math.pow(Math.sin(y0), 2.0));
+        t    = Math.pow(Math.tan(y0), 2.0);
+        c    = ecc2 * Math.pow(Math.cos(y0), 2.0);
+        A    = (x0 - x1) * Math.cos(y0);
+        newX = scale * n
+               * (A + (1.0 - t + c) * A * A * A / 6.0
+                  + (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ecc2) * Math.pow(A, 5.0) / 120.0);
+        newY = scale
+               * (m1 - m0
+                  + n * Math.tan(y0)
+                    * (A * A / 2.0 + (5.0 - t + 9.0 * c + 4 * c * c) * Math.pow(A, 4.0) / 24.0
+                       + (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ecc2) * Math.pow(A, 6.0) / 720.0));
+        return new Coordinate(newX, newY);
+    }
+
+    public static Coordinate fromTM2(double a, double ecc, double ecc2, double lat, double lon, double scale, double x, double y)
+    {
+        double newX, newY;
+        double x0, y0, x1, y1, phi, m, m0, mu, e1;
+        double c1, t1, n1, r1, d;
+
+        x0  = x;
+        y0  = y;
+        x1  = lon * DEG_RAD;
+        y1  = lat * DEG_RAD;
+        m0  = mercator(y1, a, ecc);
+        m   = m0 + y0 / scale;
+        e1  = (1.0 - Math.sqrt(1.0 - ecc)) / (1.0 + Math.sqrt(1.0 - ecc));
+        mu  = m / (a * (1.0 - ecc / 4.0 - 3.0 * ecc * ecc / 64.0 - 5.0 * ecc * ecc * ecc / 256.0));
+        phi = mu + (3.0 * e1 / 2.0 - 27.0 * Math.pow(e1, 3.0) / 32.0) * Math.sin(2.0 * mu)
+              + (21.0 * e1 * e1 / 16.0 - 55.0 * Math.pow(e1, 4.0) / 32.0) * Math.sin(4.0 * mu)
+              + 151.0 * Math.pow(e1, 3.0) / 96.0 * Math.sin(6.0 * mu) + 1097.0 * Math.pow(e1, 4.0) / 512.0 * Math.sin(8.0 * mu);
+        c1   = ecc2 * Math.pow(Math.cos(phi), 2.0);
+        t1   = Math.pow(Math.tan(phi), 2.0);
+        n1   = a / Math.sqrt(1 - ecc * Math.pow(Math.sin(phi), 2.0));
+        r1   = a * (1.0 - ecc) / Math.pow(1.0 - ecc * Math.pow(Math.sin(phi), 2.0), 1.5);
+        d    = x0 / (n1 * scale);
+        newX = (x1 + (d - (1.0 + 2.0 * t1 + c1) * Math.pow(d, 3.0) / 6.0
+                      + (5.0 - 2.0 * c1 + 28.0 * t1 - 3.0 * c1 * c1 + 8.0 * ecc2 + 24.0 * t1 * t1) * Math.pow(d, 5.0)
+                        / 120.0) / Math.cos(phi)) * RAD_DEG;
+        newY = (phi
+                - n1 * Math.tan(phi) / r1
+                  * (d * d / 2.0 - (5.0 + 3.0 * t1 + 10.0 * c1 - 4.0 * c1 * c1 - 9.0 * ecc2) * Math.pow(d, 4.0) / 24.0
+                     + (61.0 + 90.0 * t1 + 298.0 * c1 + 45.0 * t1 * t1 - 252.0 * ecc2 - 3.0 * c1 * c1) * Math.pow(d, 6.0)
+                       / 72.0)) * RAD_DEG;
+        return new Coordinate(newX, newY);
+    }
+
+    private static double mercator(double y, double a, double ecc)
+    {
+        if (y == 0.0)
+        {
+            return 0.0;
+        } else
+        {
+            return a * ((1.0 - ecc / 4.0 - 3.0 * ecc * ecc / 64.0 - 5.0 * ecc * ecc * ecc / 256.0) * y
+                        - (3.0 * ecc / 8.0 + 3.0 * ecc * ecc / 32.0 + 45.0 * ecc * ecc * ecc / 1024.0) * Math.sin(2.0 * y)
+                        + (15.0 * ecc * ecc / 256.0 + 45.0 * ecc * ecc * ecc / 1024.0) * Math.sin(4.0 * y)
+                        - (35.0 * ecc * ecc * ecc / 3072.0) * Math.sin(6.0 * y));
+        }
+    }
+
+    /**
+     *
+     *  Sample code below, using coordinate in Dan Jacob's website.
+     *
+     *
+     * int main()
+     * {
+     *   double x1, y1, z1, x2, y2, z2;
+     *   double tx1, ty1, tx2, ty2;
+     *   double dx, dy, dz, dx1, dy1;
+     *
+     *   x1 = 120.85788004;          // TWD67
+     *   y1 = 24.18347242;
+     *   z1 = 777;
+     *
+     *   x2 = 120.86603958;          // TWD97
+     *   y2 = 24.18170479;
+     *   z2 = 777;
+     *
+     *   tx1 = 235561;               // TWD67->TM2
+     *   ty1 = 2675359;
+     *
+     *   tx2 = 236389.849;           // TWD97->TM2
+     *   ty2 = 2675153.168;
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD67->TM2
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x1;
+     *   dy = y1;
+     *
+     *   toTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx, &dy);
+     *   // center longitude of taiwan is 121, for penghu is 119
+     *
+     *   dx += 250000;   // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD67->TM2nTWD67   (%f, %f)nConvert (%.3f, %.3f)nOrigin  (%.3f, %.3f)n", x1, y1, dx, dy, tx1, ty1);
+     *   printf("Acuuracy (%.3f, X:%.3f, Y:%.3f)nn", sqrt((dx-tx1)*(dx-tx1)+(dy-ty1)*(dy-ty1)), (dx-tx1), (dy-ty1));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD97->TM2
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x2;
+     *   dy = y2;
+     *
+     *   toTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx, &dy);
+     *   // center longitude of taiwan is 121, for penghu is 119
+     *
+     *   dx += 250000;   // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD97->TM2nTWD97   (%f, %f)nConvert (%.3f, %.3f)nOrigin  (%.3f, %.3f)n", x2, y2, dx, dy, tx2, ty2);
+     *   printf("Acuuracy (%.3f, X:%.3f, Y:%.3f)nn", sqrt((dx-tx2)*(dx-tx2)+(dy-ty2)*(dy-ty2)), (dx-tx2), (dy-ty2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TM2->TWD67
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = tx1-250000;    // should minus 250000 first in Taiwan
+     *   dy = ty1;
+     *
+     *   fromTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx, &dy);
+     *
+     *   printf("TM2->TWD67nTM2     (%f, %f)nConvert (%.9f, %.9f)nOrigin  (%.9f, %.9f)n", tx1, ty1, dx, dy, x1, y1);
+     *   printf("Acuuracy (%.9f, X:%.9f, Y:%.9f)nn", sqrt((dx-x1)*(dx-x1)+(dy-y1)*(dy-y1)), (dx-x1), (dy-y1));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TM2->TWD97
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = tx2-250000;    // should minus 250000 first in Taiwan
+     *   dy = ty2;
+     *
+     *   fromTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx, &dy);
+     *
+     *   printf("TM2->TWD97nTM2     (%f, %f)nConvert (%.9f, %.9f)\nOrigin  (%.9f, %.9f)\n", tx2, ty2, dx, dy, x2, y2);
+     *   printf("Acuuracy (%.9f, X:%.9f, Y:%.9f)nn", sqrt((dx-x2)*(dx-x2)+(dy-y2)*(dy-y2)), (dx-x2), (dy-y2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD67->TWD97
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x1;
+     *   dy = y1;
+     *   dz = z1;
+     *
+     *   toTWD97(&dx, &dy, &dz);
+     *
+     *   dx1 = dx;
+     *   dy1 = dy;
+     *
+     *   toTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, &dx1, &dy1);
+     *
+     *   dx1 += 250000;  // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD67->TWD97\nTWD67   (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x1, y1, z1, tx1, ty1);
+     *   printf("Convert (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", dx, dy, dz, dx1, dy1);
+     *   printf("Origin  (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x2, y2, z2, tx2, ty2);
+     *   printf("Acuuracy (%.4f, X:%.4f, Y:%.4f)nn", sqrt((dx1-tx2)*(dx1-tx2)+(dy1-ty2)*(dy1-ty2)), (dx1-tx2), (dy1-ty2));
+     *
+     *   ////////////////////////////////////////////
+     *   /
+     *   /
+     *   // convert TWD97->TWD67
+     *   /
+     *   /
+     *   ////////////////////////////////////////////
+     *
+     *   dx = x2;
+     *   dy = y2;
+     *   dz = z2;
+     *
+     *   toTWD67(&dx, &dy, &dz);
+     *
+     *   dx1 = dx;
+     *   dy1 = dy;
+     *
+     *   toTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, &dx1, &dy1);
+     *
+     *   dx1 += 250000;  // TM2 in Taiwan should add 250000
+     *
+     *   printf("TWD97->TWD67nTWD97   (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x2, y2, z2, tx2, ty2);
+     *   printf("Convert (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", dx, dy, dz, dx1, dy1);
+     *   printf("Origin  (%.9f, %.9f, %6.2f) (%.3f, %.3f)n", x1, y1, z1, tx1, ty1);
+     *   printf("Acuuracy (%.4f, X:%.4f, Y:%.4f)nn", sqrt((dx1-tx1)*(dx1-tx1)+(dy1-ty1)*(dy1-ty1)), (dx1-tx1), (dy1-ty1));
+     * }
+     */
+
+    /**
+     * ��TM2�y���ഫ��TWD97�y��
+     * @param pt TM2��m
+     * @return �s��TWD97�y��
+     */
+    public static Coordinate fromTM2ToTWD97(Coordinate pt)
+    {
+        Coordinate ptTWD67 = fromTM2(TWD67_A, TWD67_ECC, TWD67_ECC2, 0, 121, TWD67_TM2, pt.x - 250000.0, pt.y);
+        ptTWD67.z = 0;
+        Coordinate ptTWD97 = toTWD97(ptTWD67);
+        Coordinate pt97TM2 = toTM2(TWD97_A, TWD97_ECC, TWD97_ECC2, 0, 121, TWD97_TM2, ptTWD97.x, ptTWD97.y);
+        pt97TM2.x += 250000.0;
+        pt97TM2.y -= 200.0;
+        return pt97TM2;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml
new file mode 100644
index 0000000..37c3706
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml
@@ -0,0 +1,161 @@
+<?xml version='1.0' encoding="big5"?>
+<!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "digester-rules.dtd">
+<digester-rules>
+  <pattern value="ElementDispatcherRules">
+    <object-create-rule classname="com.ximple.eofms.filter.ElementDispatcher"/>
+    <set-properties-rule/>
+    <pattern value="TypeFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="elmtype"/>
+      <bean-property-setter-rule pattern="tid"/>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompLevelFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <!-- <bean-property-setter-rule pattern="lid"/> -->
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="elementLayerCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-next-rule methodname="addLayerCriterion" paramtype="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementLayer"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+  </pattern>
+</digester-rules>
\ No newline at end of file
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml
new file mode 100644
index 0000000..a9c3849
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+  <!-- High Voltage Features -->
+  <TypeCompLevelFilter name="FSC-106.C-0">
+    <tid>106</tid>
+    <cid>0</cid>
+    <description>�D�����u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <elementLevelCriterion>
+      <elementLevel>1</elementLevel>
+    </elementLevelCriterion>
+    <LineCreateStrategy/>
+  </TypeCompLevelFilter>
+
+</ElementDispatcherRules>
diff --git a/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml
new file mode 100644
index 0000000..7e23b2c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml
@@ -0,0 +1,1465 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+  <!-- High Voltage Features -->
+  <TypeCompFilter name="FSC-106.C-0">
+    <tid>106</tid>
+    <cid>0</cid>
+    <description>�D�����u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-1">
+    <tid>106</tid>
+    <cid>1</cid>
+    <description>�����u�޽u</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-2">
+    <tid>106</tid>
+    <cid>2</cid>
+    <description>�����u�޽u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-4">
+    <tid>106</tid>
+    <cid>4</cid>
+    <description>�����u�X�u�N���޽u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-0">
+    <tid>402</tid>
+    <cid>0</cid>
+    <description>�ܹq��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-1">
+    <tid>402</tid>
+    <cid>1</cid>
+    <description>�ܹq�ҵ��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-2">
+    <tid>402</tid>
+    <cid>2</cid>
+    <description>�ܹq�Ҥ�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-0">
+    <tid>411</tid>
+    <cid>0</cid>
+    <description>�t�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-1">
+    <tid>411</tid>
+    <cid>1</cid>
+    <description>�t�q�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-2">
+    <tid>411</tid>
+    <cid>2</cid>
+    <description>�t�q��-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-3">
+    <tid>411</tid>
+    <cid>3</cid>
+    <description>�t�q�����O-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-7">
+    <tid>411</tid>
+    <cid>7</cid>
+    <description>�t�q��1/600�ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-420.C-0">
+    <tid>420</tid>
+    <cid>0</cid>
+    <description>�޷�</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-420.C-1">
+    <tid>420</tid>
+    <cid>1</cid>
+    <description>�޷����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-421.C-0">
+    <tid>421</tid>
+    <cid>0</cid>
+    <description>�@�P�޹D</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-421.C-1">
+    <tid>421</tid>
+    <cid>1</cid>
+    <description>�@�P�޹D���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-423.C-0">
+    <tid>423</tid>
+    <cid>0</cid>
+    <description>�޸��_��</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-423.C-1">
+    <tid>423</tid>
+    <cid>1</cid>
+    <description>�޸��_���޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-424.C-0">
+    <tid>424</tid>
+    <cid>0</cid>
+    <description>�S��u�k�X�вŸ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-501.C-0">
+    <tid>501</tid>
+    <cid>0</cid>
+    <description>����X</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-501.C-1">
+    <tid>501</tid>
+    <cid>1</cid>
+    <description>����X��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-502.C-0">
+    <tid>502</tid>
+    <cid>0</cid>
+    <description>���D���X</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-502.C-1">
+    <tid>502</tid>
+    <cid>1</cid>
+    <description>���D���X��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-503.C-0">
+    <tid>503</tid>
+    <cid>0</cid>
+    <description>��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-0">
+    <tid>407</tid>
+    <cid>0</cid>
+    <description>�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-1">
+    <tid>407</tid>
+    <cid>1</cid>
+    <description>�q����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-2">
+    <tid>407</tid>
+    <cid>2</cid>
+    <description>�q��-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-3">
+    <tid>407</tid>
+    <cid>3</cid>
+    <description>�q��-1/600���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-7">
+    <tid>407</tid>
+    <cid>7</cid>
+    <description>1/600�q��ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-8">
+    <tid>407</tid>
+    <cid>8</cid>
+    <description>1/1200�q��ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-9">
+    <tid>407</tid>
+    <cid>9</cid>
+    <description>�q�������u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineTextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-10">
+    <tid>407</tid>
+    <cid>10</cid>
+    <description>�q��츹</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-11">
+    <tid>407</tid>
+    <cid>11</cid>
+    <description>�q�������u���\</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-0">
+    <tid>114</tid>
+    <cid>0</cid>
+    <description>�}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-1">
+    <tid>114</tid>
+    <cid>1</cid>
+    <description>�}���j�����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-2">
+    <tid>114</tid>
+    <cid>2</cid>
+    <description>�}���p�P��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-3">
+    <tid>114</tid>
+    <cid>3</cid>
+    <description>�}���j�P��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-122.C-0">
+    <tid>122</tid>
+    <cid>0</cid>
+    <description>�����s��</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineTextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-122.C-1">
+    <tid>122</tid>
+    <cid>1</cid>
+    <description>�����s�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-107.C-0">
+    <tid>107</tid>
+    <cid>0</cid>
+    <description>������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-107.C-2">
+    <tid>107</tid>
+    <cid>2</cid>
+    <description>�����Τᤤ����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-117.C-0">
+    <tid>117</tid>
+    <cid>0</cid>
+    <description>Tie������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-105.C-0">
+    <tid>105</tid>
+    <cid>0</cid>
+    <description>�������Y</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-105.C-1">
+    <tid>105</tid>
+    <cid>1</cid>
+    <description>�������Y���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-101.C-0">
+    <tid>101</tid>
+    <cid>0</cid>
+    <description>�׬y��</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-120.C-0">
+    <tid>120</tid>
+    <cid>0</cid>
+    <description>�`�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-150.C-0">
+    <tid>150</tid>
+    <cid>0</cid>
+    <description>����}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-130.C-0">
+    <tid>130</tid>
+    <cid>0</cid>
+    <description>�ɽu��e</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-131.C-0">
+    <tid>131</tid>
+    <cid>0</cid>
+    <description>�ɽu�ܧ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-109.C-0">
+    <tid>109</tid>
+    <cid>0</cid>
+    <description>���u�s��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-116.C-0">
+    <tid>116</tid>
+    <cid>0</cid>
+    <description>�����׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-108.C-0">
+    <tid>108</tid>
+    <cid>0</cid>
+    <description>�_����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-108.C-1">
+    <tid>108</tid>
+    <cid>1</cid>
+    <description>�_�������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-119.C-0">
+    <tid>119</tid>
+    <cid>0</cid>
+    <description>�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-0">
+    <tid>115</tid>
+    <cid>0</cid>
+    <description>������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-1">
+    <tid>115</tid>
+    <cid>1</cid>
+    <description>���������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-2">
+    <tid>115</tid>
+    <cid>2</cid>
+    <description>���O�t�������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-3">
+    <tid>115</tid>
+    <cid>3</cid>
+    <description>���O�t����������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-4">
+    <tid>115</tid>
+    <cid>4</cid>
+    <description>�C���t�������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-5">
+    <tid>115</tid>
+    <cid>5</cid>
+    <description>�C���t����������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-6">
+    <tid>115</tid>
+    <cid>6</cid>
+    <description>�C���t�ι�-�t�q�Ǯy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-7">
+    <tid>115</tid>
+    <cid>7</cid>
+    <description>�C���t�ι�-�t�q�Ǯy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-8">
+    <tid>115</tid>
+    <cid>8</cid>
+    <description>�[��������(�a�U�C����)���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-118.C-0">
+    <tid>118</tid>
+    <cid>0</cid>
+    <description>�D������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-102.C-0">
+    <tid>102</tid>
+    <cid>0</cid>
+    <description>�q�e��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-100.C-0">
+    <tid>100</tid>
+    <cid>0</cid>
+    <description>�׹p��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-0">
+    <tid>140</tid>
+    <cid>0</cid>
+    <description>�����u��(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-1">
+    <tid>140</tid>
+    <cid>1</cid>
+    <description>�޽u(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-2">
+    <tid>140</tid>
+    <cid>2</cid>
+    <description>�ɽu���O(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-3">
+    <tid>140</tid>
+    <cid>3</cid>
+    <description>�X�u�N���޽u(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-4">
+    <tid>140</tid>
+    <cid>4</cid>
+    <description>�X�u�N�����O(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-151.C-0">
+    <tid>150</tid>
+    <cid>0</cid>
+    <description>�`���}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- Low Voltage Features -->
+  <TypeCompFilter name="FSC-200.C-0">
+    <tid>200</tid>
+    <cid>0</cid>
+    <description>�C���`�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- *****�C��****** -->
+  <TypeCompFilter name="FSC-201.C-0">
+    <tid>201</tid>
+    <cid>0</cid>
+    <description>����u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-201.C-1">
+    <tid>201</tid>
+    <cid>1</cid>
+    <description>����u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-201.C-2">
+    <tid>201</tid>
+    <cid>2</cid>
+    <description>����u���׵��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-202.C-0">
+    <tid>202</tid>
+    <cid>0</cid>
+    <description>�����I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-202.C-1">
+    <tid>202</tid>
+    <cid>1</cid>
+    <description>�����I���P���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-203.C-0">
+    <tid>203</tid>
+    <cid>0</cid>
+    <description>���O�d�������I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-203.C-1">
+    <tid>203</tid>
+    <cid>1</cid>
+    <description>���O�d�������I���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-204.C-0">
+    <tid>204</tid>
+    <cid>0</cid>
+    <description>�C�����u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-0">
+    <tid>205</tid>
+    <cid>0</cid>
+    <description>�C���ɽu</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-1">
+    <tid>205</tid>
+    <cid>1</cid>
+    <description>�C���ɽu���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-2">
+    <tid>205</tid>
+    <cid>2</cid>
+    <description>�C���ɽu���׵��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-206.C-0">
+    <tid>206</tid>
+    <cid>0</cid>
+    <description>�C�������c</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-206.C-1">
+    <tid>206</tid>
+    <cid>1</cid>
+    <description>�C�������c���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-206.C-2">
+    <tid>206</tid>
+    <cid>2</cid>
+    <description>�C�������c���P���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-207.C-0">
+    <tid>207</tid>
+    <cid>0</cid>
+    <description>�C��ĵ����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-207.C-1">
+    <tid>207</tid>
+    <cid>1</cid>
+    <description>�C��ĵ�������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-207.C-2">
+    <tid>207</tid>
+    <cid>2</cid>
+    <description>�C��ĵ�������P���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-208.C-0">
+    <tid>208</tid>
+    <cid>0</cid>
+    <description>�C���׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-209.C-0">
+    <tid>209</tid>
+    <cid>0</cid>
+    <description>�C����q���x</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-209.C-1">
+    <tid>209</tid>
+    <cid>1</cid>
+    <description>�C����q���x���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-0">
+    <tid>210</tid>
+    <cid>0</cid>
+    <description>�C���a�U�ɽu</description>
+    <elmtype>12</elmtype>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-1">
+    <tid>210</tid>
+    <cid>1</cid>
+    <description>�C���a�U�ɽu���I�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-2">
+    <tid>210</tid>
+    <cid>2</cid>
+    <description>�C���a�U�ɽu���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-3">
+    <tid>210</tid>
+    <cid>3</cid>
+    <description>�C���a�U�ɽu�Ÿ�</description>
+    <elmtype>4</elmtype>
+    <elmtype>12</elmtype>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-4">
+    <tid>210</tid>
+    <cid>4</cid>
+    <description>�C���a�U�ɽu���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-0">
+    <tid>211</tid>
+    <cid>0</cid>
+    <description>�C���[�ű���u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-1">
+    <tid>211</tid>
+    <cid>1</cid>
+    <description>�C���[�ű���u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-2">
+    <tid>211</tid>
+    <cid>2</cid>
+    <description>�C���[�ű���u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-0">
+    <tid>212</tid>
+    <cid>0</cid>
+    <description>�C���[�ųs������u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-1">
+    <tid>212</tid>
+    <cid>1</cid>
+    <description>�C���[�ųs������u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-2">
+    <tid>212</tid>
+    <cid>2</cid>
+    <description>�C���[�ųs������u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-0">
+    <tid>213</tid>
+    <cid>0</cid>
+    <description>�C���a�U�s������u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-1">
+    <tid>213</tid>
+    <cid>1</cid>
+    <description>�C���a�U�s������u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-2">
+    <tid>213</tid>
+    <cid>2</cid>
+    <description>�C���a�U�s������u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-215.C-0">
+    <tid>215</tid>
+    <cid>0</cid>
+    <description>�C���۰ʭt�������}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-0">
+    <tid>216</tid>
+    <cid>0</cid>
+    <description>�a�U�C���ʵ���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-1">
+    <tid>216</tid>
+    <cid>1</cid>
+    <description>�C���ʵ������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-2">
+    <tid>216</tid>
+    <cid>2</cid>
+    <description>�C���ʵ������P���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-217.C-0">
+    <tid>217</tid>
+    <cid>0</cid>
+    <description>�C���a�U���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- ���l -->
+  <TypeCompFilter name="FSC-300.C-0">
+    <tid>300</tid>
+    <cid>0</cid>
+    <description>�q�T���l�u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-1">
+    <tid>300</tid>
+    <cid>1</cid>
+    <description>���ֹq�l����(����)���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-2">
+    <tid>300</tid>
+    <cid>2</cid>
+    <description>���ֹq�l��r�������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-3">
+    <tid>300</tid>
+    <cid>3</cid>
+    <description>���ֹq�l�޽u�Ÿ�</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-4">
+    <tid>300</tid>
+    <cid>4</cid>
+    <description>���ֹq�l���I�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-0">
+    <tid>301</tid>
+    <cid>0</cid>
+    <description>���O����u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-1">
+    <tid>301</tid>
+    <cid>1</cid>
+    <description>���O����u�޽u�Ÿ�</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-3">
+    <tid>301</tid>
+    <cid>3</cid>
+    <description>���O����u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-999.C-0">
+    <tid>999</tid>
+    <cid>0</cid>
+    <description>���O</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-113.C-0">
+    <tid>113</tid>
+    <cid>0</cid>
+    <description>�����a�U���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-0">
+    <tid>403</tid>
+    <cid>0</cid>
+    <description>���C���H���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-1">
+    <tid>403</tid>
+    <cid>1</cid>
+    <description>���C���H��ծy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-2">
+    <tid>403</tid>
+    <cid>2</cid>
+    <description>���C���H��ն��Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-4">
+    <tid>403</tid>
+    <cid>4</cid>
+    <description>���O�ն��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-5">
+    <tid>403</tid>
+    <cid>5</cid>
+    <description>���O�ծy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-6">
+    <tid>403</tid>
+    <cid>6</cid>
+    <description>���֤ն��Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-7">
+    <tid>403</tid>
+    <cid>7</cid>
+    <description>���l�ծy�е��O(���l�ϥ�)</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-0">
+    <tid>401</tid>
+    <cid>0</cid>
+    <description>�޸�</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+      <elementType>16</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-1">
+    <tid>401</tid>
+    <cid>1</cid>
+    <description>�޸��޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-2">
+    <tid>401</tid>
+    <cid>2</cid>
+    <description>�޸��޴U</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-3">
+    <tid>401</tid>
+    <cid>3</cid>
+    <description>�޸���r�������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-5">
+    <tid>401</tid>
+    <cid>5</cid>
+    <description>�޸��_���Ϭq�Ϲj</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-302.C-0">
+    <tid>302</tid>
+    <cid>0</cid>
+    <description>���O�t���u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-302.C-1">
+    <tid>302</tid>
+    <cid>1</cid>
+    <description>���O�t���u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-0">
+    <tid>303</tid>
+    <cid>0</cid>
+    <description>���O�ާ@�u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-1">
+    <tid>303</tid>
+    <cid>1</cid>
+    <description>���O�ާ@�u�޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-3">
+    <tid>303</tid>
+    <cid>3</cid>
+    <description>���O�ާ@�u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-305.C-0">
+    <tid>305</tid>
+    <cid>0</cid>
+    <description>���O�I����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-306.C-0">
+    <tid>306</tid>
+    <cid>0</cid>
+    <description>���O�ɱ��}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-307.C-0">
+    <tid>307</tid>
+    <cid>0</cid>
+    <description>���O���[�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-308.C-0">
+    <tid>308</tid>
+    <cid>0</cid>
+    <description>���O�׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-311.C-0">
+    <tid>311</tid>
+    <cid>0</cid>
+    <description>���O�x�b</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-311.C-1">
+    <tid>311</tid>
+    <cid>1</cid>
+    <description>���O�x�b�ϸ�(�e�q.����)���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-314.C-0">
+    <tid>314</tid>
+    <cid>0</cid>
+    <description>�[�Ÿ��O�t���u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-314.C-1">
+    <tid>314</tid>
+    <cid>1</cid>
+    <description>�[�Ÿ��O�t���u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-315.C-0">
+    <tid>315</tid>
+    <cid>0</cid>
+    <description>�[�Ÿ��O����u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-315.C-1">
+    <tid>315</tid>
+    <cid>1</cid>
+    <description>�[�Ÿ��O����u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-316.C-0">
+    <tid>316</tid>
+    <cid>0</cid>
+    <description>���O����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-317.C-0">
+    <tid>317</tid>
+    <cid>0</cid>
+    <description>���O����}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-317.C-1">
+    <tid>317</tid>
+    <cid>1</cid>
+    <description>���O����}�����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-318.C-0">
+    <tid>318</tid>
+    <cid>0</cid>
+    <description>���O�׬y��</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-319.C-0">
+    <tid>319</tid>
+    <cid>0</cid>
+    <description>���֦��e</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-320.C-0">
+    <tid>320</tid>
+    <cid>0</cid>
+    <description>���ֳq�T���Y</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-323.C-0">
+    <tid>323</tid>
+    <cid>0</cid>
+    <description>���q�ഫ��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-324.C-0">
+    <tid>324</tid>
+    <cid>0</cid>
+    <description>�۰ʤƻ�����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-324.C-1">
+    <tid>324</tid>
+    <cid>1</cid>
+    <description>�۰ʤƻ��������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <!-- Dummy
+  <TypeCompLevelFilter name="DemoFeature3">
+    <tid>999</tid>
+    <cid>2</cid>
+    <lid>34</lid>
+    <description>DemoFilter for DemoFeature</description>
+    <TextCreateStrategy-None/>
+  </TypeCompLevelFilter>
+  -->
+</ElementDispatcherRules>
diff --git a/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java b/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java
new file mode 100644
index 0000000..7b21d8a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/java/com/ximple/eofms/filter/ElementDispatcherTest.java
@@ -0,0 +1,69 @@
+package com.ximple.eofms.filter;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.geotools.TestData;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
+
+public class ElementDispatcherTest
+{
+    private static final String TestRulesName = "testRules.xml";
+    private static final String TestConfigName = "testElementFilter.xml";
+
+    @Test
+    public void testLoadRules() throws IOException
+    {
+        File rules = TestData.file(this, TestRulesName);
+        Digester digester = DigesterLoader.createDigester(rules.toURI().toURL());
+        // File config = TestData.file(this, TestConfigName);
+        // ElementDispatcher ed = (ElementDispatcher) digester.parse(config);
+        Assert.assertNotNull(digester);
+    }
+
+    @Test
+    public void testLoadConfig() throws IOException, SAXException
+    {
+        File rules = TestData.file(this, TestRulesName);
+        Digester digester = DigesterLoader.createDigester(rules.toURI().toURL());
+        File config = TestData.file(this, TestConfigName);
+        ElementDispatcher ed = (ElementDispatcher) digester.parse(config);
+        Assert.assertNotNull(ed);
+       // Assert.assertEquals(ed.getRules().size(), 3);
+
+
+        ElementDispatchableFilter filter = ed.getRules().get(0);
+        /*
+        Assert.assertTrue(filter instanceof TypeIdDispatchableFilter);
+        TypeIdDispatchableFilter tFilter = (TypeIdDispatchableFilter) filter;
+        Assert.assertEquals(tFilter.getName(), "DemoFeature1");
+        Assert.assertEquals(tFilter.getTid(), 106);
+        Assert.assertEquals(tFilter.getElmtype(), 7);
+        Assert.assertNotNull(tFilter.getCreateStrategy());
+        Assert.assertTrue(tFilter.getCreateStrategy() instanceof CreateLineStringStrategy);
+        */
+
+        filter = ed.getRules().get(1);
+        Assert.assertTrue(filter instanceof TypeCompIdDispatchableFilter);
+        TypeCompIdDispatchableFilter tcFilter = (TypeCompIdDispatchableFilter) filter;
+        Assert.assertEquals(tcFilter.getName(), "DemoFeature2");
+        Assert.assertEquals(tcFilter.getTid(), 107);
+        Assert.assertEquals(tcFilter.getCid(), 11);
+        Assert.assertNotNull(tcFilter.getCreateStrategy());
+        Assert.assertTrue(tcFilter.getCreateStrategy() instanceof CreateLineTextStrategy);
+        /*
+        filter = ed.getRules().get(2);
+        Assert.assertTrue(filter instanceof TypeCompLevelIdDispatchableFilter);
+        TypeCompLevelIdDispatchableFilter tclFilter = (TypeCompLevelIdDispatchableFilter) filter;
+        Assert.assertEquals(tclFilter.getName(), "DemoFeature3");
+        Assert.assertEquals(tclFilter.getTid(), 108);
+        Assert.assertEquals(tclFilter.getCid(), 2);
+        Assert.assertEquals(tclFilter.getLid(), 34);
+        Assert.assertNull(tclFilter.getCreateStrategy());
+        */
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml
new file mode 100644
index 0000000..4721538
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testElementFilter.xml
@@ -0,0 +1,1406 @@
+<?xml version="1.0" encoding="big5" ?>
+<ElementDispatcherRules>
+  <!-- High Voltage Features -->
+  <TypeCompFilter name="FSC-106.C-0">
+    <tid>106</tid>
+    <cid>0</cid>
+    <description>�D�����u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-1">
+    <tid>106</tid>
+    <cid>1</cid>
+    <description>�����u�޽u</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-2">
+    <tid>106</tid>
+    <cid>2</cid>
+    <description>�����u�޽u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-106.C-4">
+    <tid>106</tid>
+    <cid>4</cid>
+    <description>�����u�X�u�N���޽u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-0">
+    <tid>402</tid>
+    <cid>0</cid>
+    <description>�ܹq��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-1">
+    <tid>402</tid>
+    <cid>1</cid>
+    <description>�ܹq�ҵ��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-402.C-2">
+    <tid>402</tid>
+    <cid>2</cid>
+    <description>�ܹq�Ҥ�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-0">
+    <tid>411</tid>
+    <cid>0</cid>
+    <description>�t�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-1">
+    <tid>411</tid>
+    <cid>1</cid>
+    <description>�t�q�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-2">
+    <tid>411</tid>
+    <cid>2</cid>
+    <description>�t�q��-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-3">
+    <tid>411</tid>
+    <cid>3</cid>
+    <description>�t�q�����O-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-411.C-7">
+    <tid>411</tid>
+    <cid>7</cid>
+    <description>�t�q��1/600�ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-420.C-0">
+    <tid>420</tid>
+    <cid>0</cid>
+    <description>�޷�</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-420.C-1">
+    <tid>420</tid>
+    <cid>1</cid>
+    <description>�޷����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-421.C-0">
+    <tid>421</tid>
+    <cid>0</cid>
+    <description>�@�P�޹D</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-421.C-1">
+    <tid>421</tid>
+    <cid>1</cid>
+    <description>�@�P�޹D���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-423.C-0">
+    <tid>423</tid>
+    <cid>0</cid>
+    <description>�޸��_��</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-423.C-1">
+    <tid>423</tid>
+    <cid>1</cid>
+    <description>�޸��_���޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-424.C-0">
+    <tid>424</tid>
+    <cid>0</cid>
+    <description>�S��u�k�X�вŸ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-501.C-0">
+    <tid>501</tid>
+    <cid>0</cid>
+    <description>����X</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-501.C-1">
+    <tid>501</tid>
+    <cid>1</cid>
+    <description>����X��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-502.C-0">
+    <tid>502</tid>
+    <cid>0</cid>
+    <description>���D���X</description>
+    <elementCriterion>
+      <elementType>6</elementType>
+    </elementCriterion>
+    <ShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-502.C-1">
+    <tid>502</tid>
+    <cid>1</cid>
+    <description>���D���X��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-503.C-0">
+    <tid>503</tid>
+    <cid>0</cid>
+    <description>��r���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-0">
+    <tid>407</tid>
+    <cid>0</cid>
+    <description>�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-1">
+    <tid>407</tid>
+    <cid>1</cid>
+    <description>�q����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-2">
+    <tid>407</tid>
+    <cid>2</cid>
+    <description>�q��-1/600</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-3">
+    <tid>407</tid>
+    <cid>3</cid>
+    <description>�q��-1/600���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-7">
+    <tid>407</tid>
+    <cid>7</cid>
+    <description>1/600�q��ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-8">
+    <tid>407</tid>
+    <cid>8</cid>
+    <description>1/1200�q��ޤW�U�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-9">
+    <tid>407</tid>
+    <cid>9</cid>
+    <description>�q�������u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-10">
+    <tid>407</tid>
+    <cid>10</cid>
+    <description>�q��츹</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-407.C-11">
+    <tid>407</tid>
+    <cid>11</cid>
+    <description>�q�������u���\</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-0">
+    <tid>114</tid>
+    <cid>0</cid>
+    <description>�}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-1">
+    <tid>114</tid>
+    <cid>1</cid>
+    <description>�}���j�����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-2">
+    <tid>114</tid>
+    <cid>2</cid>
+    <description>�}���p�P��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-114.C-3">
+    <tid>114</tid>
+    <cid>3</cid>
+    <description>�}���j�P��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-122.C-0">
+    <tid>122</tid>
+    <cid>0</cid>
+    <description>�����s��</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineTextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-122.C-1">
+    <tid>122</tid>
+    <cid>1</cid>
+    <description>�����s�����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-107.C-0">
+    <tid>107</tid>
+    <cid>0</cid>
+    <description>������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-107.C-2">
+    <tid>107</tid>
+    <cid>2</cid>
+    <description>�����Τᤤ����O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-117.C-0">
+    <tid>117</tid>
+    <cid>0</cid>
+    <description>Tie������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-105.C-0">
+    <tid>105</tid>
+    <cid>0</cid>
+    <description>�������Y</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-105.C-1">
+    <tid>105</tid>
+    <cid>1</cid>
+    <description>�������Y���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-101.C-0">
+    <tid>101</tid>
+    <cid>0</cid>
+    <description>�׬y��</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-120.C-0">
+    <tid>120</tid>
+    <cid>0</cid>
+    <description>�`�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-150.C-0">
+    <tid>150</tid>
+    <cid>0</cid>
+    <description>����}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-130.C-0">
+    <tid>130</tid>
+    <cid>0</cid>
+    <description>�ɽu��e</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-131.C-0">
+    <tid>131</tid>
+    <cid>0</cid>
+    <description>�ɽu�ܧ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-109.C-0">
+    <tid>109</tid>
+    <cid>0</cid>
+    <description>���u�s��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-116.C-0">
+    <tid>116</tid>
+    <cid>0</cid>
+    <description>�����׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-108.C-0">
+    <tid>108</tid>
+    <cid>0</cid>
+    <description>�_����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-108.C-1">
+    <tid>108</tid>
+    <cid>1</cid>
+    <description>�_�������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-119.C-0">
+    <tid>119</tid>
+    <cid>0</cid>
+    <description>�q��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-0">
+    <tid>115</tid>
+    <cid>0</cid>
+    <description>������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-1">
+    <tid>115</tid>
+    <cid>1</cid>
+    <description>���������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-2">
+    <tid>115</tid>
+    <cid>2</cid>
+    <description>���O�t�������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-3">
+    <tid>115</tid>
+    <cid>3</cid>
+    <description>���O�t����������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-4">
+    <tid>115</tid>
+    <cid>4</cid>
+    <description>�C���t�������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-5">
+    <tid>115</tid>
+    <cid>5</cid>
+    <description>�C���t����������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-6">
+    <tid>115</tid>
+    <cid>6</cid>
+    <description>�C���t�ι�-�t�q�Ǯy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-7">
+    <tid>115</tid>
+    <cid>7</cid>
+    <description>�C���t�ι�-�t�q�Ǯy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-115.C-8">
+    <tid>115</tid>
+    <cid>8</cid>
+    <description>�[��������(�a�U�C����)���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-118.C-0">
+    <tid>118</tid>
+    <cid>0</cid>
+    <description>�D������</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-102.C-0">
+    <tid>102</tid>
+    <cid>0</cid>
+    <description>�q�e��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-100.C-0">
+    <tid>100</tid>
+    <cid>0</cid>
+    <description>�׹p��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-0">
+    <tid>140</tid>
+    <cid>0</cid>
+    <description>�����u��(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-1">
+    <tid>140</tid>
+    <cid>1</cid>
+    <description>�޽u(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-2">
+    <tid>140</tid>
+    <cid>2</cid>
+    <description>�ɽu���O(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-3">
+    <tid>140</tid>
+    <cid>3</cid>
+    <description>�X�u�N���޽u(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-140.C-4">
+    <tid>140</tid>
+    <cid>4</cid>
+    <description>�X�u�N�����O(�u���ϥ�)</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-151.C-0">
+    <tid>150</tid>
+    <cid>0</cid>
+    <description>�`���}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- Low Voltage Features -->
+  <TypeCompFilter name="FSC-200.C-0">
+    <tid>200</tid>
+    <cid>0</cid>
+    <description>�C���`�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- *****�C��****** -->
+  <TypeCompFilter name="FSC-201.C-0">
+    <tid>201</tid>
+    <cid>0</cid>
+    <description>����u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-201.C-1">
+    <tid>201</tid>
+    <cid>1</cid>
+    <description>����u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-201.C-2">
+    <tid>201</tid>
+    <cid>2</cid>
+    <description>����u���׵��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-202.C-0">
+    <tid>202</tid>
+    <cid>0</cid>
+    <description>�����I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-202.C-1">
+    <tid>202</tid>
+    <cid>1</cid>
+    <description>�����I���P���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-203.C-0">
+    <tid>203</tid>
+    <cid>0</cid>
+    <description>���O�d�������I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-203.C-1">
+    <tid>203</tid>
+    <cid>1</cid>
+    <description>���O�d�������I���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-204.C-0">
+    <tid>204</tid>
+    <cid>0</cid>
+    <description>�C�����u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-0">
+    <tid>205</tid>
+    <cid>0</cid>
+    <description>�C���ɽu</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-1">
+    <tid>205</tid>
+    <cid>1</cid>
+    <description>�C���ɽu���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-205.C-2">
+    <tid>205</tid>
+    <cid>2</cid>
+    <description>�C���ɽu���׵��O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-206.C-0">
+    <tid>206</tid>
+    <cid>0</cid>
+    <description>�C�������c</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-207.C-0">
+    <tid>207</tid>
+    <cid>0</cid>
+    <description>�C��ĵ����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-208.C-0">
+    <tid>208</tid>
+    <cid>0</cid>
+    <description>�C���׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-209.C-0">
+    <tid>209</tid>
+    <cid>0</cid>
+    <description>�C����q���x</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-0">
+    <tid>210</tid>
+    <cid>0</cid>
+    <description>�C���a�U�ɽu</description>
+    <elmtype>12</elmtype>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-1">
+    <tid>210</tid>
+    <cid>1</cid>
+    <description>�C���a�U�ɽu���I�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-2">
+    <tid>210</tid>
+    <cid>2</cid>
+    <description>�C���a�U�ɽu���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-3">
+    <tid>210</tid>
+    <cid>3</cid>
+    <description>�C���a�U�ɽu�Ÿ�</description>
+    <elmtype>4</elmtype>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-210.C-4">
+    <tid>210</tid>
+    <cid>4</cid>
+    <description>�C���a�U�ɽu���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-0">
+    <tid>211</tid>
+    <cid>0</cid>
+    <description>�C���[�ű���u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-1">
+    <tid>211</tid>
+    <cid>1</cid>
+    <description>�C���[�ű���u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-211.C-2">
+    <tid>211</tid>
+    <cid>2</cid>
+    <description>�C���[�ű���u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-0">
+    <tid>212</tid>
+    <cid>0</cid>
+    <description>�C���[�ųs������u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-1">
+    <tid>212</tid>
+    <cid>1</cid>
+    <description>�C���[�ųs������u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-212.C-2">
+    <tid>212</tid>
+    <cid>2</cid>
+    <description>�C���[�ųs������u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-0">
+    <tid>213</tid>
+    <cid>0</cid>
+    <description>�C���a�U�s������u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-1">
+    <tid>213</tid>
+    <cid>1</cid>
+    <description>�C���a�U�s������u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-213.C-2">
+    <tid>213</tid>
+    <cid>2</cid>
+    <description>�C���a�U�s������u���׵��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-215.C-0">
+    <tid>215</tid>
+    <cid>0</cid>
+    <description>�C���۰ʭt�������}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-0">
+    <tid>216</tid>
+    <cid>0</cid>
+    <description>�a�U�C���ʵ���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-1">
+    <tid>216</tid>
+    <cid>1</cid>
+    <description>�C���ʵ������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-216.C-2">
+    <tid>216</tid>
+    <cid>2</cid>
+    <description>�C���ʵ������P���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-217.C-0">
+    <tid>217</tid>
+    <cid>0</cid>
+    <description>�C���a�U���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <!-- ���l -->
+  <TypeCompFilter name="FSC-300.C-0">
+    <tid>300</tid>
+    <cid>0</cid>
+    <description>�q�T���l�u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-1">
+    <tid>300</tid>
+    <cid>1</cid>
+    <description>���ֹq�l����(����)���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-2">
+    <tid>300</tid>
+    <cid>2</cid>
+    <description>���ֹq�l��r�������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-3">
+    <tid>300</tid>
+    <cid>3</cid>
+    <description>���ֹq�l�޽u�Ÿ�</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-300.C-4">
+    <tid>300</tid>
+    <cid>4</cid>
+    <description>���ֹq�l���I�Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-0">
+    <tid>301</tid>
+    <cid>0</cid>
+    <description>���O����u</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-1">
+    <tid>301</tid>
+    <cid>1</cid>
+    <description>���O����u�޽u�Ÿ�</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-301.C-3">
+    <tid>301</tid>
+    <cid>3</cid>
+    <description>���O����u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-999.C-0">
+    <tid>999</tid>
+    <cid>0</cid>
+    <description>���O</description>
+    <elementCriterion>
+      <elementType>4</elementType>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-113.C-0">
+    <tid>113</tid>
+    <cid>0</cid>
+    <description>�����a�U���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-0">
+    <tid>403</tid>
+    <cid>0</cid>
+    <description>���C���H���</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-1">
+    <tid>403</tid>
+    <cid>1</cid>
+    <description>���C���H��ծy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-2">
+    <tid>403</tid>
+    <cid>2</cid>
+    <description>���C���H��ն��Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <EllipseShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-4">
+    <tid>403</tid>
+    <cid>4</cid>
+    <description>���O�ն��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <EllipseShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-5">
+    <tid>403</tid>
+    <cid>5</cid>
+    <description>���O�ծy�е��O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-6">
+    <tid>403</tid>
+    <cid>6</cid>
+    <description>���֤ն��Ÿ�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <EllipseShapeCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-403.C-7">
+    <tid>403</tid>
+    <cid>7</cid>
+    <description>���l�ծy�е��O(���l�ϥ�)</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-0">
+    <tid>401</tid>
+    <cid>0</cid>
+    <description>�޸�</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-1">
+    <tid>401</tid>
+    <cid>1</cid>
+    <description>�޸��޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-2">
+    <tid>401</tid>
+    <cid>2</cid>
+    <description>�޸��޴U</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-3">
+    <tid>401</tid>
+    <cid>3</cid>
+    <description>�޸���r�������O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-401.C-5">
+    <tid>401</tid>
+    <cid>5</cid>
+    <description>�޸��_���Ϭq�Ϲj</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-302.C-0">
+    <tid>302</tid>
+    <cid>0</cid>
+    <description>���O�t���u</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-302.C-1">
+    <tid>302</tid>
+    <cid>1</cid>
+    <description>���O�t���u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-0">
+    <tid>303</tid>
+    <cid>0</cid>
+    <description>���O�ާ@�u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-1">
+    <tid>303</tid>
+    <cid>1</cid>
+    <description>���O�ާ@�u�޽u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-303.C-3">
+    <tid>303</tid>
+    <cid>3</cid>
+    <description>���O�ާ@�u���O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-305.C-0">
+    <tid>305</tid>
+    <cid>0</cid>
+    <description>���O�I����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-306.C-0">
+    <tid>306</tid>
+    <cid>0</cid>
+    <description>���O�ɱ��}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-307.C-0">
+    <tid>307</tid>
+    <cid>0</cid>
+    <description>���O���[�I</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-308.C-0">
+    <tid>308</tid>
+    <cid>0</cid>
+    <description>���O�׺�</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-311.C-0">
+    <tid>311</tid>
+    <cid>0</cid>
+    <description>���O�x�b</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-311.C-1">
+    <tid>311</tid>
+    <cid>1</cid>
+    <description>���O�x�b�ϸ�(�e�q.����)���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-314.C-0">
+    <tid>314</tid>
+    <cid>0</cid>
+    <description>�[�Ÿ��O�t���u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-314.C-1">
+    <tid>314</tid>
+    <cid>1</cid>
+    <description>�[�Ÿ��O�t���u���O</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-315.C-0">
+    <tid>315</tid>
+    <cid>0</cid>
+    <description>�[�Ÿ��O����u</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-315.C-1">
+    <tid>315</tid>
+    <cid>1</cid>
+    <description>�[�Ÿ��O����u</description>
+    <elementCriterion>
+      <elementType>7</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-316.C-0">
+    <tid>316</tid>
+    <cid>0</cid>
+    <description>���O����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-317.C-0">
+    <tid>317</tid>
+    <cid>0</cid>
+    <description>���O����}��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-317.C-1">
+    <tid>317</tid>
+    <cid>1</cid>
+    <description>���O����}�����O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-318.C-0">
+    <tid>318</tid>
+    <cid>0</cid>
+    <description>���O�׬y��</description>
+    <elementCriterion>
+      <elementType>12</elementType>
+    </elementCriterion>
+    <LineCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-319.C-0">
+    <tid>319</tid>
+    <cid>0</cid>
+    <description>���֦��e</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-320.C-0">
+    <tid>320</tid>
+    <cid>0</cid>
+    <description>���ֳq�T���Y</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-323.C-0">
+    <tid>323</tid>
+    <cid>0</cid>
+    <description>���q�ഫ��</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-324.C-0">
+    <tid>324</tid>
+    <cid>0</cid>
+    <description>�۰ʤƻ�����</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <SymbolCreateStrategy/>
+  </TypeCompFilter>
+  <TypeCompFilter name="FSC-324.C-1">
+    <tid>324</tid>
+    <cid>1</cid>
+    <description>�۰ʤƻ��������O</description>
+    <elementCriterion>
+      <elementType>17</elementType>
+    </elementCriterion>
+    <TextCreateStrategy/>
+  </TypeCompFilter>
+  <!-- Dummy
+  <TypeCompLevelFilter name="DemoFeature3">
+    <tid>999</tid>
+    <cid>2</cid>
+    <lid>34</lid>
+    <description>DemoFilter for DemoFeature</description>
+    <TextCreateStrategy-None/>
+  </TypeCompLevelFilter>
+  -->
+</ElementDispatcherRules>
diff --git a/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml
new file mode 100644
index 0000000..0272b88
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml
@@ -0,0 +1,161 @@
+<?xml version='1.0' encoding="big5"?>
+<!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "digester-rules.dtd">
+<digester-rules>
+  <pattern value="ElementDispatcherRules">
+    <object-create-rule classname="com.ximple.eofms.filter.ElementDispatcher"/>
+    <set-properties-rule/>
+    <pattern value="TypeFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="elmtype"/>
+      <bean-property-setter-rule pattern="tid"/>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+    <pattern value="TypeCompLevelFilter">
+      <object-create-rule classname="com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter"/>
+      <set-next-rule methodname="addRule" paramtype="com.ximple.eofms.filter.ElementDispatchableFilter"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <!-- <bean-property-setter-rule pattern="elmtype"/> -->
+      <bean-property-setter-rule pattern="tid"/>
+      <bean-property-setter-rule pattern="cid"/>
+      <!-- <bean-property-setter-rule pattern="lid"/> -->
+      <pattern value="elementCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-next-rule methodname="addCriterion" paramtype="com.ximple.eofms.filter.ElementTypeCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementType"/>
+      </pattern>
+      <pattern value="elementLayerCriterion">
+        <object-create-rule classname="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-next-rule methodname="addLayerCriterion" paramtype="com.ximple.eofms.filter.ElementLevelCriterion"/>
+        <set-properties-rule/>
+        <bean-property-setter-rule pattern="elementLayer"/>
+      </pattern>
+      <pattern value="LineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="TextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="SymbolCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateSymbolStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="LineTextCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateLineTextStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateShapeStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="ArcLineCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateArcLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+      <pattern value="EllipseShapeCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateEllipseShapeLineStringStrategy"/>
+        <set-next-rule methodname="setCreateStrategy" paramtype="com.ximple.eofms.filter.CreateFeatureTypeStrategy"/>
+        <set-properties-rule/>
+      </pattern>
+    </pattern>
+  </pattern>
+</digester-rules>
\ No newline at end of file

--
Gitblit v0.0.0-SNAPSHOT