From 62374bde751f6bbef2673ca2a3b7cf0293e2be3a Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Thu, 26 Jun 2008 16:08:47 +0800
Subject: [PATCH] tag 0.6

---
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java                                  |   91 
 .gitattributes                                                                                                           |  138 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java                                |  135 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/OracleConvertMySQLJobContext.java           |  311 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/OracleConvertPostGISJobContext.java       |  390 
 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                                    |  509 
 xdgnjobs/ximple-spatialjob/pom.xml                                                                                       |  158 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java    |  762 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java                         |  117 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java                                    |   15 
 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                        |  116 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractOracleToOraSDOJobContext.java      |   72 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWD97GeometryConverterDecorator.java                      |   68 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2MySQLJob.java                            | 1107 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java                                 |  161 
 xdgnjobs/ximple-build/maven/jar-collector/src/main/java/com/ximple/eofms/maven/JarCollector.java                         |  177 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java                              |  469 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileException.java                                            |   24 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java                                                  |  352 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractFLinkageDispatchableFilter.java                 |   26 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/FeatureDgnConvertOraSDOJobContext.java     |  267 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java                           |  189 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/AbstractDgnToShapefileJobContext.java   |   13 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractDgnFileJobContext.java                    |   83 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java                               |  237 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractOracleToMySQLJobContext.java        |   70 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/OracleConvertShapefilesJobContext.java  |  371 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/FeatureDgnConvertMySQLJobContext.java       |  268 
 xdgnjobs/ximple-jobcarrier/pom.xml                                                                                       |  242 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java                                              |  387 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/HV88494_0.dgn                                      |    0 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/UserAttributeData.java                                            |   34 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/FrammeAttributeData.java                                          |   71 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java       |  521 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7TextElementReaderTest.java                                    |  180 
 xdgnjobs/ximple-build/maven/pom.xml                                                                                      |   70 
 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-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/Dgn7fileHeader.java                                               |   58 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java   |  613 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/OracleUpgradeJobContext.java                      |   23 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/GeomUtil.java                                             |   30 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexElement.java                                               |   16 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineElement.java                                                  |  130 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java                             |  201 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java                        |  305 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java                                  |  187 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz.properties                                                          |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java                          |   17 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/FeatureTypeEvent.java                                   |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java                           |  120 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java                  |  148 
 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-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/DummyFeatureConvertPostGISJobContext.java |  284 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractDgnToOraSDOJobContext.java         |   65 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java                                         | 4830 ++++++
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/DgnUtility.java                                                      |  259 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java                                |  173 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ByteArrayCompressor.java                                  |   97 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java                                          | 2885 +++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java                            |  414 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java                                   |  180 
 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 
 xdgnjobs/ximple-build/maven/jar-collector/pom.xml                                                                        |   46 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/GeneralDgnConvertShpJobContext.java     |  556 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java                                                      |  334 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs_shapefiles.xml                                                 |   99 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java                                                 |  127 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/GeneralDgnConvertOraSDOJobContext.java     |  522 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/FeatureDgnConvertShpJobContext.java     |  303 
 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-dgnio/src/main/java/com/ximple/io/dgn7/ShapeElement.java                                                 |   62 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateComplexChainStrategy.java                         |  188 
 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/context/orasdo/OracleConvertOraSDOJobContext.java         |  310 
 xdgnjobs/ximple-jobcarrier/src/test/java/com/ximple/eofms/XQuartzJobCarrierTest.java                                     |   19 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java                                                 |  163 
 xdgnjobs/ximple-dgnio/src/test/resources/com/ximple/io/dgn7/test-data/testHV.dgn                                         |    0 
 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-spatialjob/src/main/resources/conf/DefaultMapGroups.xml                                                  |   91 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/IndexDgnConvertShpJobContext.java       |  343 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2OraSDOJob.java                           | 1108 +
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultConvertShpFilter.xml                                           | 1465 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java                              |  958 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/FeatureDgnConvertPostGISJobContext.java   |  266 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java                                    |  368 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/DummyFeatureConvertOraSDOJobContext.java   |  303 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2PostGISJob.java                          | 1118 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/IndexDgnConvertMySQLJobContext.java         |  320 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/IndexDgnConvertOraSDOJobContext.java       |  320 
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/FeatureClassificationRules.xml                     |   16 
 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                          |   18 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java       |  760 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Lock.java                                                         |  263 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java                                                    | 4830 ++++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractDgnToMySQLJobContext.java           |   66 
 xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties                                                           |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractOracleJobContext.java                     |  286 
 xdgnjobs/ximple-dgnio/pom.xml                                                                                            |   79 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java                                              |   25 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureTypeCollector.java                            |   48 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/Base64.java                                               |  551 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java     |  468 
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml                         |  176 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java                         |  101 
 xdgnjobs/pom.xml                                                                                                         |  763 +
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java                                           |  118 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/StreamLogging.java                                                |   45 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/DummyFeatureConvertMySQlJobContext.java     |  304 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7fileReader.java                                               |  722 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java                              |  287 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Dgn7Exception.java                                                |   24 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/DummyFeatureConvertShpJobContext.java   |  320 
 xdgnjobs/ximple-build/pom.xml                                                                                            |   50 
 xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java                                         |  127 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java                             |   73 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java                                  |  420 
 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-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java                                          |  222 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java                                     |  339 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java                               |  143 
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml                                                            |  131 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeEventListener.java                     |    8 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/LineStringElement.java                                            |  171 
 xdgnjobs/ximple-spatialjob/src/test/resources/com/ximple/eofms/filter/test-data/testRules.xml                            |  161 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java                                          |  215 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureClassification.java                           |   28 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java                       |  139 
 139 files changed, 45,619 insertions(+), 0 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 8e948c5..8aac53f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,139 @@
 * text=auto !eol
+xdgnjobs/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-build/maven/jar-collector/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-build/maven/jar-collector/src/main/java/com/ximple/eofms/maven/JarCollector.java svneol=native#text/plain
+xdgnjobs/ximple-build/maven/pom.xml svneol=native#text/xml
+xdgnjobs/ximple-build/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 -text
+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/main/resources/quartz_jobs_shapefiles.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/collector/FeatureClassification.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureTypeCollector.java svneol=native#text/plain
+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/CreateComplexChainStrategy.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/CreateFeatureTypeEventListener.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/FeatureTypeEvent.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2MySQLJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2OraSDOJob.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2PostGISJob.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/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/context/AbstractDgnFileJobContext.java -text
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractOracleJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/OracleUpgradeJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractDgnToMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractOracleToMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/DummyFeatureConvertMySQlJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/FeatureDgnConvertMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/IndexDgnConvertMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/OracleConvertMySQLJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractDgnToOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractOracleToOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/DummyFeatureConvertOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/FeatureDgnConvertOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/GeneralDgnConvertOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/IndexDgnConvertOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/OracleConvertOraSDOJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/DummyFeatureConvertPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/FeatureDgnConvertPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/OracleConvertPostGISJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/AbstractDgnToShapefileJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/DummyFeatureConvertShpJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/FeatureDgnConvertShpJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/GeneralDgnConvertShpJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/IndexDgnConvertShpJobContext.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/OracleConvertShapefilesJobContext.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/FeatureTypeBuilderUtil.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/TWD97GeometryConverterDecorator.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/java/com/ximple/eofms/util/postjts/JTSShape.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.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/com/ximple/eofms/filter/FeatureClassificationRules.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/main/resources/conf/DefaultMapGroups.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..73d417d
--- /dev/null
+++ b/xdgnjobs/pom.xml
@@ -0,0 +1,763 @@
+<?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.6.0</xdgnio.version>
+    <gt.version>2.4.4</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.6.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>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-jdbc</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-oracle-spatial</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-postgis</artifactId>
+        <version>${gt.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.geotools</groupId>
+        <artifactId>gt2-mysql</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>
+        <groupId>com.oracle</groupId>
+        <artifactId>ojdbc5</artifactId>
+        <version>11.1.0</version>
+      </dependency>
+      <dependency>
+        <groupId>com.oracle</groupId>
+        <artifactId>sdoapi</artifactId>
+        <version>11.1.0</version>
+      </dependency>
+      <dependency>
+        <groupId>com.oracle</groupId>
+        <artifactId>sdotype</artifactId>
+        <version>11.1.0</version>
+      </dependency>
+      <dependency>
+        <groupId>com.oracle</groupId>
+        <artifactId>sdoutl</artifactId>
+        <version>11.1.0</version>
+      </dependency>
+
+      <dependency>
+        <groupId>postgresql</groupId>
+        <artifactId>postgresql</artifactId>
+        <version>8.3-603.jdbc3</version>
+      </dependency>
+      <dependency>
+        <groupId>org.postgis</groupId>
+        <artifactId>postgis-driver</artifactId>
+        <version>1.3.3</version>
+      </dependency>
+
+      <dependency>
+        <groupId>mysql</groupId>
+        <artifactId>mysql-connector-java</artifactId>
+        <version>5.1.6</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>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-build</module>
+    <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-build/maven/jar-collector/pom.xml b/xdgnjobs/ximple-build/maven/jar-collector/pom.xml
new file mode 100644
index 0000000..ca14767
--- /dev/null
+++ b/xdgnjobs/ximple-build/maven/jar-collector/pom.xml
@@ -0,0 +1,46 @@
+<?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.maven</groupId>
+    <artifactId>ximple-maven</artifactId>
+    <version>0.6.0</version>
+  </parent>
+
+
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms.maven</groupId>
+  <artifactId>ximple-jar-collector</artifactId>
+  <packaging>maven-plugin</packaging>
+  <name>JAR files collector</name>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url>
+  </scm>
+
+  <description>
+    Copy all JAR files (including dependencies) in the target directory
+    of the parent project descriptor.
+  </description>
+
+  <licenses>
+    <license>
+      <name>Lesser General Public License (LGPL)</name>
+      <url>http://www.gnu.org/copyleft/lesser.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <!-- =========================================================== -->
+  <!--     Developers and Contributors                             -->
+  <!-- =========================================================== -->
+
+</project>
diff --git a/xdgnjobs/ximple-build/maven/jar-collector/src/main/java/com/ximple/eofms/maven/JarCollector.java b/xdgnjobs/ximple-build/maven/jar-collector/src/main/java/com/ximple/eofms/maven/JarCollector.java
new file mode 100644
index 0000000..c76d6ae
--- /dev/null
+++ b/xdgnjobs/ximple-build/maven/jar-collector/src/main/java/com/ximple/eofms/maven/JarCollector.java
@@ -0,0 +1,177 @@
+package org.ximple.eofms.maven;
+
+import org.apache.maven.artifact.Artifact;
+
+// Maven and Plexus dependencies
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.FileUtils;
+
+// J2SE dependencies
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Set;
+
+
+// Note: javadoc in class and fields descriptions must be XHTML.
+/**
+ * Copies <code>.jar</code> files in a single directory. Dependencies are copied as well,
+ * except if already presents.
+ *
+ * @goal collect
+ * @phase package
+ * @source $URL$
+ * @version $Id$
+ * @author Martin Desruisseaux
+ */
+public class JarCollector extends AbstractMojo {
+    /**
+     * The sub directory to create inside the "target" directory.
+     */
+    private static final String SUB_DIRECTORY = "binaries";
+
+    /**
+     * The directory where JARs are to be copied. It should
+     * be the "target" directory of the parent {@code pom.xml}.
+     */
+    private String collectDirectory;
+
+    /**
+     * Directory containing the generated JAR.
+     *
+     * @parameter expression="${project.build.directory}"
+     * @required
+     */
+    private String outputDirectory;
+
+    /**
+     * Name of the generated JAR.
+     *
+     * @parameter expression="${project.build.finalName}"
+     * @required
+     */
+    private String jarName;
+
+    /**
+     * Project dependencies.
+     *
+     * @parameter expression="${project.artifacts}"
+     * @required
+     */
+    private Set /*<Artifact>*/ dependencies;
+
+    /**
+     * The Maven project running this plugin.
+     *
+     * @parameter expression="${project}"
+     * @required
+     */
+    private MavenProject project;
+
+    /**
+     * Copies the {@code .jar} files to the collect directory.
+     *
+     * @throws MojoExecutionException if the plugin execution failed.
+     */
+    public void execute() throws MojoExecutionException {
+        /*
+         * Gets the parent "target" directory.
+         */
+        MavenProject parent = project;
+
+        while (parent.hasParent()) {
+            parent = parent.getParent();
+        }
+
+        collectDirectory = parent.getBuild().getDirectory();
+
+        /*
+         * Now collects the JARs.
+         */
+        try {
+            collect();
+        } catch (IOException e) {
+            throw new MojoExecutionException("Error collecting the JAR file.", e);
+        }
+    }
+
+    /**
+     * Implementation of the {@link #execute} method.
+     */
+    private void collect() throws MojoExecutionException, IOException {
+        /*
+         * Make sure that we are collecting the JAR file from a module which produced
+         * such file. Some modules use pom packaging, which do not produce any JAR file.
+         */
+        final File jarFile = new File(outputDirectory, jarName + ".jar");
+
+        if (!jarFile.isFile()) {
+            return;
+        }
+
+        /*
+         * Get the "target" directory of the parent pom.xml and make sure it exists.
+         */
+        File collect = new File(collectDirectory);
+
+        if (!collect.exists()) {
+            if (!collect.mkdir()) {
+                throw new MojoExecutionException("Failed to create target directory.");
+            }
+        }
+
+        if (collect.getCanonicalFile().equals(jarFile.getParentFile().getCanonicalFile())) {
+            /*
+             * The parent's directory is the same one than this module's directory.
+             * In other words, this plugin is not executed from the parent POM. Do
+             * not copy anything, since this is not the place where we want to
+             * collect the JAR files.
+             */
+            return;
+        }
+
+        /*
+         * Creates a "binaries" subdirectory inside the "target" directory.
+         */
+        collect = new File(collect, SUB_DIRECTORY);
+
+        if (!collect.exists()) {
+            if (!collect.mkdir()) {
+                throw new MojoExecutionException("Failed to create binaries directory.");
+            }
+        }
+
+        int count = 1;
+        FileUtils.copyFileToDirectory(jarFile, collect);
+
+        if (dependencies != null) {
+            for (final Iterator it = dependencies.iterator(); it.hasNext();) {
+                final Artifact artifact = (Artifact) it.next();
+                final String scope = artifact.getScope();
+
+                if ((scope != null) // Maven 2.0.6 bug?
+                        && (scope.equalsIgnoreCase(Artifact.SCOPE_COMPILE)
+                        || scope.equalsIgnoreCase(Artifact.SCOPE_RUNTIME))) {
+                    final File file = artifact.getFile();
+                    final File copy = new File(collect, file.getName());
+
+                    if (!copy.exists()) {
+                        /*
+                         * Copies the dependency only if it was not already copied. Note that
+                         * the module's JAR was copied inconditionnaly above (because it may
+                         * be the result of a new compilation). If a Geotools JAR from the
+                         * dependencies list changed, it will be copied inconditionnaly when
+                         * the module for this JAR will be processed by Maven.
+                         */
+                        FileUtils.copyFileToDirectory(file, collect);
+                        count++;
+                    }
+                }
+            }
+        }
+
+        getLog().info("Copied " + count + " JAR to parent directory.");
+    }
+}
diff --git a/xdgnjobs/ximple-build/maven/pom.xml b/xdgnjobs/ximple-build/maven/pom.xml
new file mode 100644
index 0000000..af14632
--- /dev/null
+++ b/xdgnjobs/ximple-build/maven/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.maven</groupId>
+    <artifactId>ximple-build</artifactId>
+    <version>0.6.0</version>
+  </parent>
+
+
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms.maven</groupId>
+  <artifactId>ximple-maven</artifactId>
+  <packaging>pom</packaging>
+  <name>Maven plugins for Ximple</name>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url>
+  </scm>
+
+  <description>
+    Maven plugins specific to the the Ximple Dgn IO Library.
+  </description>
+
+  <licenses>
+    <license>
+      <name>Lesser General Public License (LGPL)</name>
+      <url>http://www.gnu.org/copyleft/lesser.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+
+  <!-- =========================================================== -->
+  <!--     Dependency Management                                   -->
+  <!-- =========================================================== -->
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-project</artifactId>
+      <version>2.0.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <version>1.2</version>
+    </dependency>
+  </dependencies>
+
+
+  <!-- =========================================================== -->
+  <!--     Modules included in the build                           -->
+  <!-- =========================================================== -->
+  <modules>
+    <module>jar-collector</module>
+  </modules>
+</project>
diff --git a/xdgnjobs/ximple-build/pom.xml b/xdgnjobs/ximple-build/pom.xml
new file mode 100644
index 0000000..8208e70
--- /dev/null
+++ b/xdgnjobs/ximple-build/pom.xml
@@ -0,0 +1,50 @@
+<?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.6.0</version>
+  </parent>
+
+
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms.maven</groupId>
+  <artifactId>ximple-build</artifactId>
+  <packaging>pom</packaging>
+  <name>Build tools for Ximple DgnJobs</name>
+
+  <scm>
+    <connection>
+      scm:svn:http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/
+    </connection>
+    <url>http://www.ximple.com.tw/svn/xeofms/xdgnio/truck/</url>
+  </scm>
+
+  <description>
+    Build tools for the the Ximple Dgn IO Library.
+  </description>
+
+  <licenses>
+    <license>
+      <name>Lesser General Public License (LGPL)</name>
+      <url>http://www.gnu.org/copyleft/lesser.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+
+  <!-- =========================================================== -->
+  <!--     Modules included in the build                           -->
+  <!-- =========================================================== -->
+  <modules>
+    <module>maven</module>
+  </modules>
+
+
+</project>
diff --git a/xdgnjobs/ximple-dgnio/pom.xml b/xdgnjobs/ximple-dgnio/pom.xml
new file mode 100644
index 0000000..5916a17
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/pom.xml
@@ -0,0 +1,79 @@
+<?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.6.0</version>
+  </parent>
+
+  <!-- =========================================================== -->
+  <!--     Module Description                                      -->
+  <!-- =========================================================== -->
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-dgnio</artifactId>
+  <version>0.6.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-dgnio</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>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-sample-data</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>ojdbc5</artifactId>
+    </dependency>
+  </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..9b722d7
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
@@ -0,0 +1,222 @@
+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.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+
+/**
+ * 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)
+    {
+        ArrayList<LineString> lineStrings = new ArrayList<LineString>();
+
+        for (ListIterator it = listIterator(); it.hasNext();)
+        {
+            Element element = (Element) it.next();
+
+            if (element instanceof LineStringElement)
+            {
+                if (((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
+                {
+                    lineStrings.add((LineString) ((LineStringElement) element).toGeometry(factory));
+                }
+
+            } else if (element instanceof LineElement)
+            {
+
+                if (((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1)
+                {
+                    lineStrings.add((LineString) ((LineElement) element).toGeometry(factory));
+                }
+            } else if (element instanceof ArcElement)
+            {
+                lineStrings.add((LineString) ((ArcElement) element).toGeometry(factory));
+            }
+        }
+
+        LineString[] lines = lineStrings.toArray(new LineString[lineStrings.size()]);
+        if ((lines == null) || (lines.length == 0))
+            return null;
+        return factory.createMultiLineString(lines);
+    }
+
+    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..f34dc57
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexShapeElement.java
@@ -0,0 +1,215 @@
+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.CoordinateList;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * ComplexShapeElement
+ *
+ * @author Ulysses
+ * @version 0.1
+ * @since 2006/5/18 �U�� 03:45:15
+ */
+public class ComplexShapeElement extends Element implements ComplexElement, GeometryConverter
+{
+    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..b8908cf
--- /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..33f3a1c
--- /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..35a3156
--- /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..030b950
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/Element.java
@@ -0,0 +1,334 @@
+package com.ximple.io.dgn7;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vividsolutions.jts.geom.Envelope;
+
+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 void setRange(Envelope bbox)
+    {
+        int lowCoordX = DgnUtility.converCoordToUnit(bbox.getMinX());
+        int temp = DgnUtility.converToDGN(lowCoordX);
+        raw[3] = (short) (temp >> 16 & 0x0000ffff);
+        raw[2] = (short) (temp & 0x0000ffff);
+
+        int lowCoordY = DgnUtility.converCoordToUnit(bbox.getMinY());
+        temp = DgnUtility.converToDGN(lowCoordY);
+        raw[5] = (short) (temp >> 16 & 0x0000ffff);
+        raw[4] = (short) (temp & 0x0000ffff);
+
+        int highCoorX = DgnUtility.converCoordToUnit(bbox.getMaxX());
+        temp = DgnUtility.converToDGN(highCoorX);
+        raw[9] = (short) (temp >> 16 & 0x0000ffff);
+        raw[8] = (short) (temp & 0x0000ffff);
+
+        int highCoorY = DgnUtility.converCoordToUnit(bbox.getMaxY());
+        temp = DgnUtility.converToDGN(highCoorY);
+        raw[11] = (short) (temp >> 16 & 0x0000ffff);
+        raw[10] = (short) (temp & 0x0000ffff);
+    }
+
+    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 void setLevelIndex(int value)
+    {
+        raw[0] = (short) ((raw[0] & 0xffc0) | (value & 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 ByteBuffer getRawBuffer()
+    {
+        return rawBuffer.asReadOnlyBuffer();
+    }
+
+    public short[] getRawArray()
+    {
+        if (raw == null) return null;
+        short[] result = new short[raw.length];
+        System.arraycopy(raw, 0, result, 0, raw.length);
+        return result;
+    }
+
+    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 void resyncBuffer()
+    {
+        byte[] tempRaw = new byte[this.raw.length * 2];
+        ByteBuffer tempBuffer = ByteBuffer.wrap(tempRaw);
+        tempBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        tempBuffer.asShortBuffer().put(this.raw);
+
+        int pos = rawBuffer.position();
+        rawBuffer = tempBuffer;
+        rawBuffer.position(pos);
+    }
+
+    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;
+        }
+
+        public int getBufferLength(Object element)
+        {
+            return ((Element) element).rawBuffer.limit();
+        }
+
+        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..5612e7a
--- /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..2a9840e
--- /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..37af5e9
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/IElementHandler.java
@@ -0,0 +1,25 @@
+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);
+
+    public int getBufferLength(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..2996936
--- /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..ce09145
--- /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..58f9f73
--- /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..84c14af
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/NIOUtilities.java
@@ -0,0 +1,127 @@
+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.
+ *
+ * @author Andres Aimes
+ * @version $Id$
+ * @source $URL$
+ * @since 2.0
+ */
+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..8000b5c
--- /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..79ca5eb
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextElement.java
@@ -0,0 +1,352 @@
+package com.ximple.io.dgn7;
+
+import java.awt.geom.AffineTransform;
+import java.nio.ByteBuffer;
+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 Object clone() throws CloneNotSupportedException
+    {
+        int pos = this.rawBuffer.position();
+        this.rawBuffer.position(0);
+        byte[] rawBytes = this.rawBuffer.array();
+        byte[] otherRaw = new byte[rawBytes.length];
+        System.arraycopy(rawBytes, 0, otherRaw, 0, rawBytes.length);
+        this.rawBuffer.position(pos);
+
+        TextElement other = new TextElement(otherRaw);
+        return other;
+    }
+
+    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..fbab7cd
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/TextNodeElement.java
@@ -0,0 +1,387 @@
+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 getTotalWords()
+    {
+        return (raw[18] & 0x0000ffff);
+    }
+
+    public void setTotalWords(int value)
+    {
+        raw[18] = (short) (value & 0x0000ffff);
+    }
+
+    public int getNumString()
+    {
+        return (raw[19] & 0x0000ffff);
+    }
+
+    public void setNumString(int value)
+    {
+        raw[19] = (short) (value & 0x0000ffff);
+    }
+
+    public int getNodeNumber()
+    {
+        return (raw[20] & 0x0000ffff);
+    }
+
+    public void setNodeNumber(int value)
+    {
+        raw[20] = (short) (value & 0x0000ffff);
+    }
+
+    public int getMaxLength()
+    {
+        return (raw[21] & 0x00ff);
+    }
+
+    public void setMaxLength(int value)
+    {
+        raw[21] = (short) ((value & 0x00ff) | (raw[21] & 0xff00));
+    }
+
+    public int getMaxUsed()
+    {
+        return ((raw[21] >> 8) & 0x00ff);
+    }
+
+    public void setMaxUsed(int value)
+    {
+        raw[21] = (short) (((value << 8) & 0xff00) | (raw[21] & 0x00ff));
+    }
+
+    public int getJustification()
+    {
+        return ((raw[22] >> 8) & 0x00ff);
+    }
+
+    public void setJustification(int value)
+    {
+        raw[22] = (short) (((value << 8) & 0xff00) | (raw[22] & 0x00ff));
+    }
+    
+    public int getFontIndex()
+    {
+        return (raw[22] & 0x00ff);
+    }
+
+    public void setFontIndex(int value)
+    {
+        raw[22] = (short) ((value & 0x00ff) | (raw[22] & 0xff00));
+    }
+
+    public double getLineSpacing()
+    {
+        int lineSpace;
+        lineSpace = ((raw[23] & 0x0000ffff) << 16) | (raw[24] & 0x0000ffff);
+
+        return lineSpace;
+    }
+
+    public void setLineSpacing(double value)
+    {
+        int temp = (int) (value*1000.0);
+        raw[23] = (short) ((temp >> 16) & 0x0000ffff);
+        raw[24] = (short) (temp & 0x0000ffff);
+    }
+
+    public double getTextNodeLength()
+    {
+        int lengthMult;
+
+        lengthMult = ((raw[25] << 16) & 0xffff0000);
+        lengthMult += (raw[26] & 0x0000ffff);
+
+        return DgnUtility.converIntToDouble(lengthMult);
+    }
+
+    public void setTextNodeLength(double value)
+    {
+        int temp = DgnUtility.converDoubleToInt(value);
+        raw[25] = (short) ((temp >> 16) & 0x0000ffff);
+        raw[26] = (short) (temp & 0x0000ffff);
+    }
+
+    public double getTextNodeHeight()
+    {
+        int heightMult;
+
+        heightMult = ((raw[27] << 16) & 0xffff0000);
+        heightMult += (raw[28] & 0x0000ffff);
+
+        return DgnUtility.converIntToDouble(heightMult);
+    }
+
+    public void setTextNodeHeight(double value)
+    {
+        int temp = DgnUtility.converDoubleToInt(value);
+        raw[27] = (short) ((temp >> 16) & 0x0000ffff);
+        raw[28] = (short) (temp & 0x0000ffff);
+    }
+
+    public double getRotationAngle()
+    {
+        int rotation = (raw[29] << 16 & 0xffff0000);
+        rotation += raw[30];
+
+        return DgnUtility.converIntToRotation(rotation);
+    }
+
+    public void setRotationAngle(double value)
+    {
+        int temp = DgnUtility.converRotatioToInt(value);
+        raw[29] = (short) (temp >> 16 & 0x0000ffff);
+        raw[30] = (short) (temp & 0x0000ffff);
+    }
+
+    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 void setOrigin(Coordinate value)
+    {
+        int x = DgnUtility.converCoordToUnit(value.x);
+        raw[31] = (short) (x >> 16 & 0x0000ffff);
+        raw[32] = (short) (x & 0x0000ffff);
+
+        int y = DgnUtility.converCoordToUnit(value.y);
+        raw[33] = (short) (y >> 16 & 0x0000ffff);
+        raw[34] = (short) (y & 0x0000ffff);
+    }
+
+    public Object clone() throws CloneNotSupportedException
+    {
+        int pos = this.rawBuffer.position();
+        this.rawBuffer.position(0);
+        byte[] rawBytes = this.rawBuffer.array();
+        byte[] otherRaw = new byte[rawBytes.length];
+        System.arraycopy(rawBytes, 0, otherRaw, 0, rawBytes.length);
+        this.rawBuffer.position(pos);
+
+        TextNodeElement other = new TextNodeElement(otherRaw);
+        for (Object o : this)
+        {
+            TextElement textElm = (TextElement) o;
+            other.add(textElm.clone());
+        }
+
+        return other;
+    }
+
+    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..2b87fe1
--- /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..0009270
--- /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..5ec015f
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/util/PrintfFormat.java
@@ -0,0 +1,4830 @@
+//
+//(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>
+ * <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>
+ * <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>
+ * <p/>
+ * <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/>
+ * <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.
+ * <p/>
+ * <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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         * <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 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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..f01a630
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7OracleReaderTest.java
@@ -0,0 +1,127 @@
+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 oracle.jdbc.OracleConnection;
+
+import com.ximple.util.PrintfFormat;
+
+/**
+ * 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..301255e
--- /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.geotools.TestData;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * 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..40b6d1f
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/Dgn7fileReaderTest.java
@@ -0,0 +1,118 @@
+package com.ximple.io.dgn7;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import org.apache.log4j.Logger;
+import org.geotools.TestData;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * 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..11d8fe0
--- /dev/null
+++ b/xdgnjobs/ximple-dgnio/src/test/java/com/ximple/io/dgn7/OracleTarget.java
@@ -0,0 +1,163 @@
+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..ae21fcb
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/pom.xml
@@ -0,0 +1,242 @@
+<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.6.0</version>
+  </parent>
+
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-jobcarrier</artifactId>
+  <version>0.6.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-jobcarrier</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <xdgnio.version>0.6.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>
+
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-shapefile</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-sample-data</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-data</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-jdbc</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-postgis</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-oracle-spatial</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-mysql</artifactId>
+    </dependency>
+
+    <!-- because main and sample-data depend on referencing we need a tie breaker -->
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-referencing</artifactId>
+    </dependency>
+
+    <!-- We need this to make the referencing module useful -->
+    <dependency>
+      <artifactId>gt2-epsg-hsql</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <artifactId>jdom</artifactId>
+      <groupId>jdom</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>velocity</artifactId>
+      <groupId>velocity</groupId>
+    </dependency>
+
+    <!-- ORACLE -->
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>ojdbc5</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>ojdbc5</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdoapi</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdotype</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdoutl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.postgis</groupId>
+      <artifactId>postgis-driver</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+    </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>
+      <plugin>
+        <groupId>com.ximple.eofms.maven</groupId>
+        <artifactId>ximple-jar-collector</artifactId>
+        <version>${project.version}</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>collect</goal>
+            </goals>
+          </execution>
+        </executions>
+      </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..1bd55fc
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
@@ -0,0 +1,131 @@
+<?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>ConvertDgn2PostGisIntoPostgre</name>
+      <group>DEFAULT</group>
+      <description>A job that convert dgn to shapefiles</description>
+      <job-class>com.ximple.eofms.jobs.OracleConvertDgn2PostGISJob</job-class>
+      <volatility>false</volatility>
+      <durability>false</durability>
+      <recover>false</recover>
+      <job-data-map allows-transient-data="true">
+        <entry>
+          <key>JOBDATA_DIR</key>
+          <value>g:\temp\data</value>
+        </entry>
+        <!--
+        <entry>
+          <key>ELMSFILTER_CONF</key>
+          <value></value>
+        </entry>
+        -->
+        <entry>
+          <key>PGHOST</key>
+          <value>192.168.11.200</value>
+        </entry>
+        <entry>
+          <key>PGDDATBASE</key>
+          <value>tctpc</value>
+        </entry>
+        <entry>
+          <key>PGPORT</key>
+          <value>5432</value>
+        </entry>
+        <entry>
+          <key>PGSCHEMA</key>
+          <value>public</value>
+        </entry>
+        <entry>
+          <key>PGUSER</key>
+          <value>spatialdb</value>
+        </entry>
+        <entry>
+          <key>PGPASS</key>
+          <value>spatialdb000</value>
+        </entry>
+        <entry>
+          <key>ORAHOST</key>
+          <value>192.168.11.200</value>
+        </entry>
+        <entry>
+          <key>ORAINST</key>
+          <value>tctpc</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>true</value>
+        </entry>
+        <entry>
+          <key>CONVERTFILE</key>
+          <value>true</value>
+        </entry>
+        <entry>
+          <key>CONVERTELEMIN</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>CREATEDUMMY</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>ELEMLOG</key>
+          <value>true</value>
+        </entry>
+        <entry>
+          <key>USEWKB</key>
+          <value>true</value>
+        </entry>
+        <entry>
+          <key>TESTMODE</key>
+          <value>false</value>
+        </entry>
+        <entry>
+          <key>TESTCOUNT</key>
+          <value>2</value>
+        </entry>
+        <entry>
+          <key>COPYCONNECTIVITYMODE</key>
+          <value>true</value>
+        </entry>
+      </job-data-map>
+    </job-detail>
+
+    <trigger>
+      <simple>
+        <name>convertTrigger</name>
+        <group>DEFAULT</group>
+        <job-name>ConvertDgn2PostGisIntoPostgre</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/main/resources/quartz_jobs_shapefiles.xml b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs_shapefiles.xml
new file mode 100644
index 0000000..f03ace3
--- /dev/null
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs_shapefiles.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>ELMSFILTER_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..996a354
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/pom.xml
@@ -0,0 +1,158 @@
+<?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.6.0</version>
+  </parent>
+
+  <groupId>com.ximple.eofms</groupId>
+  <artifactId>ximple-spatialjob</artifactId>
+  <version>0.6.0</version>
+  <packaging>jar</packaging>
+  <name>ximple-spatialjob</name>
+  <url>http://www.ximple.com.tw</url>
+
+  <properties>
+    <xdgnio.version>0.6.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>
+
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-shapefile</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-sample-data</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-data</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-jdbc</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-postgis</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-oracle-spatial</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-mysql</artifactId>
+    </dependency>
+
+    <!-- because main and sample-data depend on referencing we need a tie breaker -->
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-referencing</artifactId>
+    </dependency>
+
+    <!-- We need this to make the referencing module useful -->
+    <dependency>
+      <artifactId>gt2-epsg-hsql</artifactId>
+      <groupId>org.geotools</groupId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <artifactId>jdom</artifactId>
+      <groupId>jdom</groupId>
+    </dependency>
+
+    <dependency>
+      <artifactId>velocity</artifactId>
+      <groupId>velocity</groupId>
+    </dependency>
+
+    <!-- ORACLE -->
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>ojdbc5</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdoapi</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdotype</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>sdoutl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.postgis</groupId>
+      <artifactId>postgis-driver</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+    </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/collector/FeatureClassification.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureClassification.java
new file mode 100644
index 0000000..e4024ec
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureClassification.java
@@ -0,0 +1,28 @@
+package com.ximple.eofms.collector;
+
+import java.util.TreeMap;
+
+public class FeatureClassification
+{
+    private TreeMap<String, FeatureTypeCollector> clasificationRules;
+
+    public FeatureClassification()
+    {
+        clasificationRules = new TreeMap<String, FeatureTypeCollector>();
+    }
+
+    public void addCollector(FeatureTypeCollector collector)
+    {
+        collector.getName();
+    }
+
+    public boolean containsKey(String typeName)
+    {
+        return clasificationRules.containsKey(typeName);
+    }
+
+    public TreeMap<String, FeatureTypeCollector> getClasificationRules()
+    {
+        return clasificationRules;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureTypeCollector.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureTypeCollector.java
new file mode 100644
index 0000000..7309b3f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/collector/FeatureTypeCollector.java
@@ -0,0 +1,48 @@
+package com.ximple.eofms.collector;
+
+import java.util.ArrayList;
+
+import com.ximple.eofms.util.StringUtils;
+
+public class FeatureTypeCollector
+{
+    private String name;
+    private String description;
+    private String featuretypeList;
+    private ArrayList<String> featureTypeNameList;
+
+    public FeatureTypeCollector()
+    {
+    }
+
+    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 String getFeaturetypeList()
+    {
+        return featuretypeList;
+    }
+
+    public void setFeaturetypeList(String featuretypeList)
+    {
+        featureTypeNameList = StringUtils.split(featuretypeList, ",");
+        this.featuretypeList = featuretypeList;
+    }
+}
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..d6e91b1
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/AbstractDispatchableFilter.java
@@ -0,0 +1,101 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import javax.swing.event.EventListenerList;
+
+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);
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
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..54bc021
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateArcLineStringStrategy.java
@@ -0,0 +1,116 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createArcFeatureTypeBuilder(featureName);
+            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);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof ArcElement)
+        {
+            ArcElement lineStringElement = (ArcElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        }
+        return feature;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateComplexChainStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateComplexChainStrategy.java
new file mode 100644
index 0000000..a372f9b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateComplexChainStrategy.java
@@ -0,0 +1,188 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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.vividsolutions.jts.geom.LineString;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+import com.ximple.io.dgn7.ArcElement;
+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;
+
+public class CreateComplexChainStrategy 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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    public CreateComplexChainStrategy()
+    {
+    }
+
+    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 = FeatureTypeBuilderUtil.createMultiLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj instanceof LineString)
+            {
+                gobj = geometryFactory.createMultiLineString(new LineString[] {(LineString) gobj});
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChain);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj instanceof LineString)
+            {
+                gobj = geometryFactory.createMultiLineString(new LineString[] {(LineString) gobj});
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) complexChain.getLevelIndex(),
+                    colorTable.getColorCode(complexChain.getColorIndex()),
+                    (short) complexChain.getWeight(),
+                    (short) complexChain.getLineStyle()
+            });
+        } else if (element instanceof LineElement)
+        {
+            LineElement lineElement = (LineElement) element;
+            convertDecorator.setConverter(lineElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj instanceof LineString)
+            {
+                gobj = geometryFactory.createMultiLineString(new LineString[] {(LineString) gobj});
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineElement.getLevelIndex(),
+                    colorTable.getColorCode(lineElement.getColorIndex()),
+                    (short) lineElement.getWeight(),
+                    (short) lineElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ArcElement)
+        {
+            ArcElement lineStringElement = (ArcElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj instanceof LineString)
+            {
+                gobj = geometryFactory.createMultiLineString(new LineString[] {(LineString) gobj});
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        }
+
+        return feature;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
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..9d0c215
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateEllipseShapeStrategy.java
@@ -0,0 +1,117 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createEllipseFeatureTypeBuilder(featureName);
+            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);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof EllipseElement)
+        {
+            EllipseElement shapeElement = (EllipseElement) element;
+            convertDecorator.setConverter(shapeElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) shapeElement.getLevelIndex(),
+                    colorTable.getColorCode(shapeElement.getColorIndex()),
+                    (short) shapeElement.getWeight(),
+                    (short) shapeElement.getLineStyle()
+            });
+        }
+        return feature;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
+
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeEventListener.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeEventListener.java
new file mode 100644
index 0000000..4a81200
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeEventListener.java
@@ -0,0 +1,8 @@
+package com.ximple.eofms.filter;
+
+import java.util.EventListener;
+
+public interface CreateFeatureTypeEventListener extends EventListener
+{
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt);
+}
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..83d0074
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateFeatureTypeStrategy.java
@@ -0,0 +1,17 @@
+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;
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener);
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener);
+}
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..41ab334
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
@@ -0,0 +1,189 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.CoordinateArrays;
+import com.vividsolutions.jts.geom.CoordinateList;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+import com.ximple.io.dgn7.ArcElement;
+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;
+
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChain);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if ((gobj != null) && (gobj instanceof MultiLineString))
+            {
+                MultiLineString mline = (MultiLineString) gobj;
+                CoordinateList coordinateList = new CoordinateList();
+                if (mline.getNumGeometries() == 1)
+                {
+                    for (int i = 0; i < mline.getNumGeometries(); i++)
+                    {
+                        coordinateList.add(mline.getGeometryN(i).getCoordinates(), true);
+                    }
+                }
+
+                gobj = geometryFactory.createLineString(coordinateList.toCoordinateArray());
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) complexChain.getLevelIndex(),
+                    colorTable.getColorCode(complexChain.getColorIndex()),
+                    (short) complexChain.getWeight(),
+                    (short) complexChain.getLineStyle()
+            });
+        } else if (element instanceof LineElement)
+        {
+            LineElement lineElement = (LineElement) element;
+            convertDecorator.setConverter(lineElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineElement.getLevelIndex(),
+                    colorTable.getColorCode(lineElement.getColorIndex()),
+                    (short) lineElement.getWeight(),
+                    (short) lineElement.getLineStyle()
+            });
+            return feature;
+        } else if (element instanceof ArcElement)
+        {
+            ArcElement lineStringElement = (ArcElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        }
+
+        return feature;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
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..e6a2761
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
@@ -0,0 +1,201 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.CoordinateList;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof LineStringElement)
+        {
+            LineStringElement lineStringElement = (LineStringElement) element;
+            convertDecorator.setConverter(lineStringElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineStringElement.getLevelIndex(),
+                    colorTable.getColorCode(lineStringElement.getColorIndex()),
+                    (short) lineStringElement.getWeight(),
+                    (short) lineStringElement.getLineStyle()
+            });
+        } 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 = featureType.create(new Object[]{
+                    line,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    (short) txtElement.getWeight(),
+                    (short) txtElement.getLineStyle()
+            });
+        } else if (element instanceof ComplexChainElement)
+        {
+            ComplexChainElement complexChain = (ComplexChainElement) element;
+            convertDecorator.setConverter(complexChain);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if ((gobj != null) && (gobj instanceof MultiLineString))
+            {
+                MultiLineString mline = (MultiLineString) gobj;
+                CoordinateList coordinateList = new CoordinateList();
+                if (mline.getNumGeometries() == 1)
+                {
+                    for (int i = 0; i < mline.getNumGeometries(); i++)
+                    {
+                        coordinateList.add(mline.getGeometryN(i).getCoordinates(), true);
+                    }
+                }
+
+                gobj = geometryFactory.createLineString(coordinateList.toCoordinateArray());
+            }
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) complexChain.getLevelIndex(),
+                    colorTable.getColorCode(complexChain.getColorIndex()),
+                    (short) complexChain.getWeight(),
+                    (short) complexChain.getLineStyle()
+            });
+        } else if (element instanceof LineElement)
+        {
+            LineElement lineElement = (LineElement) element;
+            convertDecorator.setConverter(lineElement);
+            feature = featureType.create(new Object[]{
+                    convertDecorator.toGeometry(geometryFactory),
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) lineElement.getLevelIndex(),
+                    colorTable.getColorCode(lineElement.getColorIndex()),
+                    (short) lineElement.getWeight(),
+                    (short) lineElement.getLineStyle()
+            });
+        }
+
+        return feature;
+    }
+
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
+
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..d42c1c8
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateShapeStrategy.java
@@ -0,0 +1,135 @@
+package com.ximple.eofms.filter;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createPolygonFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        if (fLinkage == null) return null;
+        if (element instanceof ShapeElement)
+        {
+            ShapeElement shapeElement = (ShapeElement) element;
+            convertDecorator.setConverter(shapeElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+                feature = featureType.create(new Object[]{
+                        gobj,
+                        fLinkage.getFsc(),
+                        (long) fLinkage.getUfid(),
+                        (short) fLinkage.getComponentID(),
+                        (short) 0,
+                        (short) shapeElement.getLevelIndex(),
+                        colorTable.getColorCode(shapeElement.getColorIndex()),
+                        (short) shapeElement.getWeight(),
+                        (short) shapeElement.getLineStyle()
+                });
+        } else if (element instanceof ComplexShapeElement)
+        {
+            ComplexShapeElement complexShape = (ComplexShapeElement) element;
+            convertDecorator.setConverter(complexShape);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+                feature = featureType.create(new Object[]{
+                        gobj,
+                        fLinkage.getFsc(),
+                        (long) fLinkage.getUfid(),
+                        (short) fLinkage.getComponentID(),
+                        (short) 0,
+                        (short) complexShape.getLevelIndex(),
+                        colorTable.getColorCode(complexShape.getColorIndex()),
+                        (short) complexShape.getWeight(),
+                        (short) complexShape.getLineStyle()
+                });
+        }
+        return feature;
+    }
+
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
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..2b3fd93
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateSymbolStrategy.java
@@ -0,0 +1,143 @@
+package com.ximple.eofms.filter;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.TreeMap;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createSymbolFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        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);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    (short) txtElement.getWeight(),
+                    (short) txtElement.getLineStyle(),
+                    (short) txtElement.getJustification(),
+                    (float) txtElement.getTextHeight(),
+                    (float) txtElement.getTextWidth(),
+                    (float) angle,
+                    sb.toString()
+            });
+        } else
+        {
+            logger.info("CreateSymbolStrategy cannot conver " + element.toString() + "to Feature");
+        }
+        return feature;
+    }
+
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
+
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..cca31c2
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateTextStrategy.java
@@ -0,0 +1,161 @@
+package com.ximple.eofms.filter;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.TreeMap;
+import javax.swing.event.EventListenerList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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();
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    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 = FeatureTypeBuilderUtil.createPointFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public Feature createFeature(FeatureType featureType, Element element) throws IllegalAttributeException
+    {
+        DefaultColorTable colorTable = (DefaultColorTable) DefaultColorTable.getInstance();
+        FrammeAttributeData fLinkage = getFeatureLinkage(element);
+        Feature feature = null;
+        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();
+            String content = txtElement.getText();
+            content = content.replace('\u0000', ' ');
+            convertDecorator.setConverter(txtElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) txtElement.getLevelIndex(),
+                    colorTable.getColorCode(txtElement.getColorIndex()),
+                    (short) txtElement.getWeight(),
+                    (short) txtElement.getLineStyle(),
+                    (short) txtElement.getJustification(),
+                    (float) txtElement.getTextHeight(),
+                    (float) txtElement.getTextWidth(),
+                    (float) angle,
+                    content
+            });
+        } 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");
+                String content = text.replace('\u0000', ' ');
+                sb.append(content);
+            }
+
+            double angle = nodeElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            convertDecorator.setConverter(nodeElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    fLinkage.getFsc(),
+                    (long) fLinkage.getUfid(),
+                    (short) fLinkage.getComponentID(),
+                    (short) 0,
+                    (short) nodeElement.getLevelIndex(),
+                    colorTable.getColorCode(nodeElement.getColorIndex()),
+                    (short) nodeElement.getWeight(),
+                    (short) nodeElement.getLineStyle(),
+                    (short) nodeElement.getJustification(),
+                    (float) nodeElement.getTextNodeHeight(),
+                    (float) nodeElement.getTextNodeLength(),
+                    (float) angle,
+                    sb.toString()
+            });
+        }
+        return feature;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+}
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..2eb54be
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatchableFilter.java
@@ -0,0 +1,18 @@
+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);
+
+    void setUseLongName(boolean useLongName);
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener);
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener);
+}
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..a64d23e
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/ElementDispatcher.java
@@ -0,0 +1,91 @@
+package com.ximple.eofms.filter;
+
+import java.util.LinkedList;
+
+import javax.swing.event.EventListenerList;
+
+import org.geotools.feature.Feature;
+
+import com.ximple.io.dgn7.Element;
+
+public class ElementDispatcher implements CreateFeatureTypeEventListener
+{
+    private LinkedList<ElementDispatchableFilter> rules;
+    private boolean useLongName = false;
+
+    // Create the listener list
+    protected EventListenerList listenerList = new EventListenerList();
+
+    public ElementDispatcher()
+    {
+        rules = new LinkedList<ElementDispatchableFilter>();
+    }
+
+    public LinkedList<ElementDispatchableFilter> getRules()
+    {
+        return rules;
+    }
+
+    public void addRule(ElementDispatchableFilter rule)
+    {
+        rule.setUseLongName(useLongName);
+        rule.addCreateFeatureTypeEventListener(this);
+        rules.add(rule);
+    }
+
+    public boolean isUseLongName()
+    {
+        return useLongName;
+    }
+
+    public void setUseLongName(boolean useLongName)
+    {
+        if (this.useLongName != useLongName)
+        {
+            this.useLongName = useLongName;
+            for (ElementDispatchableFilter filter : rules)
+            {
+                filter.setUseLongName(useLongName);
+            }
+        }
+    }
+
+    public Feature execute(Element element)
+    {
+        for (ElementDispatchableFilter rule : rules)
+        {
+            if (rule.isDispatchable(element))
+            {
+                return rule.execute(element);
+            }
+        }
+        return null;
+    }
+
+    public void addCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.add(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    public void removeCreateFeatureTypeEventListener(CreateFeatureTypeEventListener listener)
+    {
+        listenerList.remove(CreateFeatureTypeEventListener.class, listener);
+    }
+
+    protected void fireFeatureTypeEvent(FeatureTypeEvent evt)
+    {
+        Object[] listeners = listenerList.getListenerList();
+        for (int i = 0; i < listeners.length; i += 2)
+        {
+            if (listeners[i] == CreateFeatureTypeEventListener.class)
+            {
+                ((CreateFeatureTypeEventListener) listeners[i + 1]).createFeatureTypeOccurred(evt);
+            }
+        }
+    }
+
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt)
+    {
+        fireFeatureTypeEvent(evt);
+    }
+}
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/FeatureTypeEvent.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/FeatureTypeEvent.java
new file mode 100644
index 0000000..c821382
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/FeatureTypeEvent.java
@@ -0,0 +1,28 @@
+package com.ximple.eofms.filter;
+
+import java.util.EventObject;
+
+import org.geotools.feature.FeatureType;
+
+public class FeatureTypeEvent extends EventObject
+{
+    private FeatureType featureType;
+
+    /**
+     * Constructs a prototypical Event.
+     *
+     * @param source The object on which the Event initially occurred.
+     * @param featureType featureType
+     * @throws IllegalArgumentException if source is null.
+     */
+    public FeatureTypeEvent(Object source, FeatureType featureType)
+    {
+        super(source);
+        this.featureType = featureType;
+    }
+
+    public FeatureType getFeatureType()
+    {
+        return featureType;
+    }
+}
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..4810b12
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompIdDispatchableFilter.java
@@ -0,0 +1,139 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.eofms.util.StringUtils;
+
+public class TypeCompIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+        implements CreateFeatureTypeEventListener
+{
+    private int tid;
+    private int cid;
+    private CreateFeatureTypeStrategy createStrategy;
+    private boolean useLongName = false;
+
+    public TypeCompIdDispatchableFilter()
+    {
+    }
+
+    public TypeCompIdDispatchableFilter(String fname,
+                                        CreateFeatureTypeStrategy createStrategy,
+                                        int tid, int compid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.cid = compid;
+        this.createStrategy = createStrategy;
+        this.createStrategy.addCreateFeatureTypeEventListener(this);
+    }
+
+    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)
+    {
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.removeCreateFeatureTypeEventListener(this);
+        }
+        this.createStrategy = createStrategy;
+
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.addCreateFeatureTypeEventListener(this);
+        }
+    }
+
+    public boolean isUseLongName()
+    {
+        return useLongName;
+    }
+
+    public void setUseLongName(boolean useLongName)
+    {
+        this.useLongName = useLongName;
+    }
+
+    //�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();
+        String name = getName().toLowerCase();
+        String[] pices = StringUtils.splitToArray(name, ".");
+        boolean bfirst = true;
+        for (String temp : pices)
+        {
+            if (bfirst) bfirst = false;
+            else sb.append('-');
+            sb.append(temp);
+        }
+        if (useLongName)
+        {
+            sb.append("_");
+            sb.append(element.getLevelIndex());
+            sb.append("_");
+            sb.append(element.getWeight());
+        }
+        return sb.toString();
+    }
+
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt)
+    {
+        fireFeatureTypeEvent(evt);
+    }
+}
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..ab302e4
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeCompLevelIdDispatchableFilter.java
@@ -0,0 +1,148 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.eofms.util.StringUtils;
+
+public class TypeCompLevelIdDispatchableFilter extends AbstractFLinkageDispatchableFilter
+        implements CreateFeatureTypeEventListener
+{
+    private int tid;
+    private int cid;
+    private int lid;
+    private CreateFeatureTypeStrategy createStrategy;
+    private boolean useLongName = false;
+
+    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;
+        this.createStrategy.addCreateFeatureTypeEventListener(this);
+    }
+
+    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)
+    {
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.removeCreateFeatureTypeEventListener(this);
+        }
+        this.createStrategy = createStrategy;
+
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.addCreateFeatureTypeEventListener(this);
+        }
+    }
+
+
+    public boolean isUseLongName()
+    {
+        return useLongName;
+    }
+
+    public void setUseLongName(boolean useLongName)
+    {
+        this.useLongName = useLongName;
+    }
+
+    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();
+        String name = getName().toLowerCase();
+        String[] pices = StringUtils.splitToArray(name, ".");
+        boolean bfirst = true;
+        for (String temp : pices)
+        {
+            if (bfirst) bfirst = false;
+            else sb.append('-');
+            sb.append(temp);
+        }
+        if (useLongName)
+        {
+            sb.append("_");
+            sb.append(element.getLevelIndex());
+            sb.append("_");
+            sb.append(element.getWeight());
+        }
+        return sb.toString();
+    }
+
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt)
+    {
+        fireFeatureTypeEvent(evt);
+    }
+}
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..b683361
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/TypeIdDispatchableFilter.java
@@ -0,0 +1,120 @@
+package com.ximple.eofms.filter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.eofms.util.StringUtils;
+
+public class TypeIdDispatchableFilter extends AbstractFLinkageDispatchableFilter implements CreateFeatureTypeEventListener
+{
+    private int tid;
+    private CreateFeatureTypeStrategy createStrategy;
+    private boolean useLongName = false;
+
+    public TypeIdDispatchableFilter()
+    {
+    }
+
+    public TypeIdDispatchableFilter(String fname, CreateFeatureTypeStrategy createStrategy,
+                                    int tid)
+    {
+        this.setName(fname);
+        this.tid = tid;
+        this.createStrategy = createStrategy;
+        this.createStrategy.addCreateFeatureTypeEventListener(this);
+    }
+
+    public int getTid()
+    {
+        return tid;
+    }
+
+    public void setTid(int tid)
+    {
+        this.tid = tid;
+    }
+
+    public CreateFeatureTypeStrategy getCreateStrategy()
+    {
+        return createStrategy;
+    }
+
+    public void setCreateStrategy(CreateFeatureTypeStrategy createStrategy)
+    {
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.removeCreateFeatureTypeEventListener(this);
+        }
+        this.createStrategy = createStrategy;
+
+        if (this.createStrategy != null)
+        {
+            this.createStrategy.addCreateFeatureTypeEventListener(this);
+        }
+    }
+
+    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 boolean isUseLongName()
+    {
+        return useLongName;
+    }
+
+    public void setUseLongName(boolean useLongName)
+    {
+        this.useLongName = useLongName;
+    }
+
+    public String getFeatureTypeName(Element element)
+    {
+        StringBuilder sb = new StringBuilder();
+        String name = getName().toLowerCase();
+        String[] pices = StringUtils.splitToArray(name, ".");
+        boolean bfirst = true;
+        for (String temp : pices)
+        {
+            if (bfirst) bfirst = false;
+            else sb.append('-');
+            sb.append(temp);
+        }
+        if (useLongName)
+        {
+            sb.append("_");
+            sb.append(element.getLevelIndex());
+            sb.append("_");
+            sb.append(element.getWeight());
+        }
+        return sb.toString();
+    }
+
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt)
+    {
+        fireFeatureTypeEvent(evt);
+    }
+}
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..3ff0034
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/AbstractOracleDatabaseJob.java
@@ -0,0 +1,414 @@
+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.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import org.apache.commons.logging.Log;
+import org.geotools.data.DataStore;
+import org.geotools.data.jdbc.ConnectionPoolManager;
+import org.geotools.data.oracle.OracleDataStore;
+import org.geotools.data.oracle.OracleDataStoreFactory;
+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;
+
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+
+public abstract class AbstractOracleDatabaseJob implements Job
+{
+    /**
+     * The Oracle driver class name
+     */
+    private static final String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver";
+
+    private static final String ORACLE_URL = "jdbc:oracle:thin:@";
+    private static final String PROPUsrKey = "user";
+    private static final String PROPPassKey = "password";
+
+    private static final String JOBDATA_DIR = "JOBDATA_DIR";
+    private static final String CONFELMSFILTER = "ELMSFILTER_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";
+    private static final String COPYCONNECTIVITYMODE = "COPYCONNECTIVITYMODE";
+
+    protected static OracleDataStoreFactory dataStoreFactory = new OracleDataStoreFactory();
+
+    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 boolean _copyConnectivityMode = false;
+    protected int _testCount = -1;
+    protected OracleDataStore sourceDataStore;
+    private boolean driverFound = true;
+
+    protected AbstractOracleDatabaseJob()
+    {
+        try
+        {
+            Class.forName(JDBC_DRIVER);
+        } catch (Throwable t)
+        {
+            // must be running off dummy jar!
+            driverFound = false;
+        }
+    }
+
+    public abstract void execute(JobExecutionContext context) throws JobExecutionException;
+
+    public abstract Log getLogger();
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        // The directory to scan is stored in the job map
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _dataPath = dataMap.getString(JOBDATA_DIR);
+        _filterPath = dataMap.getString(CONFELMSFILTER);
+        _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("JOBDATA_DIR=" + _dataPath);
+        logger.info("CONFELMSFILTER=" + _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);
+        _copyConnectivityMode = dataMap.getBooleanFromString(COPYCONNECTIVITYMODE);
+
+        // 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)
+        {
+            logger.warn("OracleHOST is null");
+            throw new JobExecutionException("Unknown Oracle Host.");
+        }
+        if (_oracleInstance == null)
+        {
+            logger.warn("OracleINSTANCE is null");
+            throw new JobExecutionException("Unknown Oracle Instance.");
+        }
+        if (_username == null)
+        {
+            logger.warn("OracleUSER is null");
+            throw new JobExecutionException("Unknown Oracle Username.");
+        }
+        if (_password == null)
+        {
+            logger.warn("OraclePASS is null");
+            throw new JobExecutionException("Unknown Oracle Password.");
+        }
+        if (_orgSchema == null)
+        {
+            logger.warn("OracleSchema is 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 boolean isDriverFound()
+    {
+        return driverFound;
+    }
+
+    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");
+    }
+
+    public boolean isCopyConnectivityMode()
+    {
+        return _copyConnectivityMode;
+    }
+
+    public DataStore getSourceDataStore()
+    {
+        return sourceDataStore;
+    }
+
+    protected void createSourceDataStore() throws JobExecutionException
+    {
+        if (sourceDataStore != null)
+        {
+            sourceDataStore.dispose();
+            sourceDataStore = null;
+        }
+
+        if (!isDriverFound())
+        {
+            throw new JobExecutionException("Oracle JDBC Driver not found.-" + JDBC_DRIVER);
+        }
+        Map<String, String> map = new TreeMap<String, String>();
+        map.put("host", _oracleHost);
+        map.put("port", _oraclePort);
+        map.put("instance", _oracleInstance);
+        map.put("user", _username);
+        map.put("passwd", _password);
+        map.put("dbtype", "oracle");
+        map.put("alias", _oracleInstance);
+        map.put("namespace", null);
+        if (!map.containsKey(OracleDataStoreFactory.MAXCONN.key))
+        {
+            map.put(OracleDataStoreFactory.MAXCONN.key, "10");
+        }
+        if (!map.containsKey(OracleDataStoreFactory.MINCONN.key))
+        {
+            map.put(OracleDataStoreFactory.MINCONN.key, "1");
+        }
+
+        if (!dataStoreFactory.canProcess(map))
+        {
+            getLogger().warn("cannot process properties-");
+            throw new JobExecutionException("cannot process properties-");
+        }
+        try
+        {
+            sourceDataStore = (OracleDataStore) dataStoreFactory.createDataStore(map);
+        } catch (IOException e)
+        {
+            getLogger().warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+    }
+
+    protected void disconnect()
+    {
+        ConnectionPoolManager manager = ConnectionPoolManager.getInstance();
+        manager.closeAll();
+    }
+}
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..ba6584d
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/DummyFeatureConvertJobContext.java
@@ -0,0 +1,305 @@
+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.ElementDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.TypeCompIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeIdDispatchableFilter;
+import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2MySQLJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2MySQLJob.java
new file mode 100644
index 0000000..ccea789
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2MySQLJob.java
@@ -0,0 +1,1107 @@
+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.sql.Connection;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
+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.data.DataStore;
+import org.geotools.data.mysql.MySQLDataStore;
+import org.geotools.data.mysql.MySQLDataStoreFactory;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.quartz.JobDataMap;
+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.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.mysql.OracleConvertMySQLJobContext;
+import com.ximple.eofms.jobs.context.mysql.IndexDgnConvertMySQLJobContext;
+import com.ximple.eofms.jobs.context.mysql.GeneralDgnConvertMySQLJobContext;
+import com.ximple.eofms.jobs.context.mysql.FeatureDgnConvertMySQLJobContext;
+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 OracleConvertDgn2MySQLJob extends AbstractOracleDatabaseJob
+{
+    final static Log logger = LogFactory.getLog(OracleConvertDgn2PostGISJob.class);
+
+    private static final String MYHOST = "MYHOST";
+    private static final String MYDATBASE = "MYDATBASE";
+    private static final String MYPORT = "MYPORT";
+    private static final String MYSCHEMA = "MYSCHEMA";
+    private static final String MYUSER = "MYUSER";
+    private static final String MYPASS = "MYPASS";
+    private static final String USEWKB = "USEWKB";
+
+    private static final int FETCHSIZE = 30;
+    private static final int COMMITSIZE = 20;
+
+    class Pair
+    {
+        Object first;
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    protected static MySQLDataStoreFactory dataStoreFactory = new MySQLDataStoreFactory();
+
+    GeometryFactory _geomFactory = new GeometryFactory();
+    protected String _myHost;
+    protected String _myDatabase;
+    protected String _myPort;
+    protected String _mySchema;
+    protected String _myUsername;
+    protected String _myPassword;
+    protected String _myUseWKB;
+
+    protected Map<String, String> myProperties;
+    protected MySQLDataStore targetDataStore;
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleConvertMySQLJobContext(getDataPath(), getTargetDataStore(), filterPath);
+    }
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        super.extractJobConfiguration(jobDetail);
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _myHost = dataMap.getString(MYHOST);
+        _myDatabase = dataMap.getString(MYDATBASE);
+        _myPort = dataMap.getString(MYPORT);
+        _mySchema = dataMap.getString(MYSCHEMA);
+        _myUsername = dataMap.getString(MYUSER);
+        _myPassword = dataMap.getString(MYPASS);
+        _myUseWKB = dataMap.getString(USEWKB);
+
+        Log logger = getLogger();
+        /*
+        logger.info("MYHOST=" + _myHost);
+        logger.info("MYDATBASE=" + _myDatabase);
+        logger.info("MYPORT=" + _myPort);
+        logger.info("MYSCHEMA=" + _mySchema);
+        logger.info("MYUSER=" + _myUsername);
+        logger.info("MYPASS=" + _myPassword);
+        logger.info("USEWKB=" + _myUseWKB);
+        */
+
+        if (_myHost == null)
+        {
+            logger.warn("MYHOST is null");
+            throw new JobExecutionException("Unknown PostGIS host.");
+        }
+        if (_myDatabase == null)
+        {
+            logger.warn("PGDATABASE is null");
+            throw new JobExecutionException("Unknown PostGIS database.");
+        }
+        if (_myPort == null)
+        {
+            logger.warn("MYPORT is null");
+            throw new JobExecutionException("Unknown PostGIS port.");
+        }
+        if (_mySchema == null)
+        {
+            logger.warn("MYSCHEMA is null");
+            throw new JobExecutionException("Unknown PostGIS schema.");
+        }
+        if (_myUsername == null)
+        {
+            logger.warn("PGUSERNAME is null");
+            throw new JobExecutionException("Unknown PostGIS username.");
+        }
+        if (_myPassword == null)
+        {
+            logger.warn("PGPASSWORD is null");
+            throw new JobExecutionException("Unknown PostGIS password.");
+        }
+
+        Map<String, String> remote = new TreeMap<String, String>();
+        remote.put("dbtype", "postgis");
+        remote.put("charset", "UTF-8");
+        remote.put("host", _myHost);
+        remote.put("port", _myPort);
+        remote.put("database", _myDatabase);
+        remote.put("user", _myUsername);
+        remote.put("passwd", _myPassword);
+        remote.put("namespace", null);
+        myProperties = remote;
+    }
+
+    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);
+        createSourceDataStore();
+        createTargetDataStore();
+        if (getSourceDataStore() == null)
+        {
+            logger.warn("Cannot connect source oracle database.");
+            throw new JobExecutionException("Cannot connect source oracle database.");
+        }
+
+        if (getTargetDataStore() == null)
+        {
+            logger.warn("Cannot connect source postgreSQL database.");
+            throw new JobExecutionException("Cannot connect source postgreSQL database.");
+        }
+
+        long t1 = System.currentTimeMillis();
+        try
+        {
+            logger.info("-- step:clearOutputDatabase --");
+            clearOutputDatabase();
+            boolean bFirst = isCopyConnectivityMode();
+            if (checkConvertDB())
+            {
+                logger.info("-- step:convertOracleDB --");
+
+                for (String orgSchema : _orgSchema)
+                {
+                    OracleConvertMySQLJobContext jobContext =
+                            (OracleConvertMySQLJobContext) prepareJobContext(_filterPath);
+                    jobContext.setSourceDataStore(getSourceDataStore());
+                    // 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);
+            }
+
+            disconnect();
+            long t2 = System.currentTimeMillis();
+            // public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
+            // SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
+            logger.warn("use time = " + ((t2 - t1) / 60000.0) + " min");
+        } 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(OracleConvertMySQLJobContext jobContext) throws SQLException
+    {
+        Connection connection = jobContext.getOracleConnection();
+        Statement stmt = connection.createStatement();
+        stmt.execute(AbstractOracleJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+        stmt.execute(AbstractOracleJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+    }
+
+    private void exetcuteConvert(OracleConvertMySQLJobContext 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("ConvertDgn2PostGISJobProgress", 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();
+                System.runFinalization();
+            }
+
+            int now = order % step;
+            if (now != current)
+            {
+                current = now;
+                jobContext.getExecutionContext().put("ConvertDgn2PostGISJobProgress", current);
+
+            }
+        }
+        jobContext.getExecutionContext().put("ConvertDgn2PostGISJobProgress", 100);
+
+        jobContext.commitTransaction();
+
+        logger.info("end convert job:[" + order + "]");
+        System.gc();
+        System.runFinalization();
+    }
+
+    protected OrderedMap getBlobStorageList(Connection 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);
+            int size = rs.getMetaData().getColumnCount();
+            while (rs.next())
+            {
+                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);
+        int size = rs.getMetaData().getColumnCount();
+        while (rs.next())
+        {
+            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(OracleConvertMySQLJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        Connection 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);
+        int igdsMetaType = rsSrc.getMetaData().getColumnType(1);
+        while (rsSrc.next())
+        {
+            byte[] raw;
+
+            if (igdsMetaType == 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(OracleConvertMySQLJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        Connection 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)
+        {
+            IndexDgnConvertMySQLJobContext convertContext =
+                    new IndexDgnConvertMySQLJobContext(getDataPath(), getTargetDataStore());
+            logger.debug("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(IndexDgnConvertMySQLJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processIndexElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processIndexElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processIndexElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processIndexElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processIndexElement(Element element, IndexDgnConvertMySQLJobContext 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)
+        {
+            GeneralDgnConvertMySQLJobContext convertContext =
+                    new GeneralDgnConvertMySQLJobContext(getDataPath(), getTargetDataStore());
+            logger.info("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(GeneralDgnConvertMySQLJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processOtherElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processOtherElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processOtherElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processOtherElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processOtherElement(Element element, GeneralDgnConvertMySQLJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void clearOutputDatabase()
+    {
+        /*
+        File outDataPath = new File(getDataPath(), OracleConvertPostGISJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), IndexDgnConvertShpJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), GeneralDgnConvertShpJobContext.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)
+        {
+            FeatureDgnConvertMySQLJobContext convertContext =
+                    new FeatureDgnConvertMySQLJobContext(getDataPath(), getTargetDataStore(), _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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(FeatureDgnConvertMySQLJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processFeatureElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processFeatureElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processFeatureElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processFeatureElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processFeatureElement(Element element, FeatureDgnConvertMySQLJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void createDummyFeatureFile(JobExecutionContext context) throws JobExecutionException
+    {
+        /*
+        DummyFeatureConvertShpJobContext convertContext = new DummyFeatureConvertShpJobContext(getDataPath(), _filterPath);
+        try {
+            convertContext.startTransaction();
+            convertContext.commitTransaction();
+            convertContext.closeFeatureWriter();
+        } catch (IOException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+        */
+    }
+
+    public DataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    protected void createTargetDataStore() throws JobExecutionException
+    {
+        if (targetDataStore != null)
+        {
+            targetDataStore.dispose();
+            targetDataStore = null;
+        }
+
+        /*
+        if (!isDriverFound())
+        {
+            throw new JobExecutionException("Oracle JDBC Driver not found.-" + JDBC_DRIVER);
+        }
+        */
+
+        if (!myProperties.containsKey("max connections" /*MySQLDataStoreFactory.MAXCONN.key */))
+        {
+            myProperties.put("max connections", "2");
+        }
+
+        if (!myProperties.containsKey("min connections" /* MySQLDataStoreFactory.MINCONN.key */))
+        {
+            myProperties.put("min connections", "1");
+        }
+
+        if (!myProperties.containsKey(MySQLDataStoreFactory.WKBENABLED.key))
+        {
+            myProperties.put(MySQLDataStoreFactory.WKBENABLED.key, "true");
+        }
+
+        if (!dataStoreFactory.canProcess(myProperties))
+        {
+            getLogger().warn("cannot process properties-");
+            throw new JobExecutionException("cannot process properties-");
+        }
+        try
+        {
+            targetDataStore = (MySQLDataStore) dataStoreFactory.createDataStore(myProperties);
+        } catch (IOException e)
+        {
+            getLogger().warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2OraSDOJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2OraSDOJob.java
new file mode 100644
index 0000000..e444094
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2OraSDOJob.java
@@ -0,0 +1,1108 @@
+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.sql.Connection;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
+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.data.DataStore;
+import org.geotools.data.oracle.OracleDataStore;
+import org.geotools.data.oracle.OracleDataStoreFactory;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.quartz.JobDataMap;
+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.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.orasdo.FeatureDgnConvertOraSDOJobContext;
+import com.ximple.eofms.jobs.context.orasdo.GeneralDgnConvertOraSDOJobContext;
+import com.ximple.eofms.jobs.context.orasdo.IndexDgnConvertOraSDOJobContext;
+import com.ximple.eofms.jobs.context.orasdo.OracleConvertOraSDOJobContext;
+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 OracleConvertDgn2OraSDOJob extends AbstractOracleDatabaseJob
+{
+    final static Log logger = LogFactory.getLog(OracleConvertDgn2OraSDOJob.class);
+
+    private static final String SDOHOST = "SDOHOST";
+    private static final String SDODDATBASE = "SDODDATBASE";
+    private static final String SDOPORT = "SDOPORT";
+    private static final String SDOSCHEMA = "SDOSCHEMA";
+    private static final String SDOUSER = "SDOUSER";
+    private static final String SDOPASS = "SDOPASS";
+    private static final String USEWKB = "USEWKB";
+
+    private static final int FETCHSIZE = 30;
+    private static final int COMMITSIZE = 20;
+
+    class Pair
+    {
+        Object first;
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    protected static OracleDataStoreFactory dataStoreFactory = new OracleDataStoreFactory();
+
+    GeometryFactory _geomFactory = new GeometryFactory();
+    protected String _sdoHost;
+    protected String _sdoDatabase;
+    protected String _sdoPort;
+    protected String _sdoSchema;
+    protected String _sdoUsername;
+    protected String _sdoPassword;
+    protected String _sdoUseWKB;
+
+    protected Map<String, String> sdoProperties;
+    protected OracleDataStore targetDataStore;
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleConvertOraSDOJobContext(getDataPath(), getTargetDataStore(), filterPath);
+    }
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        super.extractJobConfiguration(jobDetail);
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _sdoHost = dataMap.getString(SDOHOST);
+        _sdoDatabase = dataMap.getString(SDODDATBASE);
+        _sdoPort = dataMap.getString(SDOPORT);
+        _sdoSchema = dataMap.getString(SDOSCHEMA);
+        _sdoUsername = dataMap.getString(SDOUSER);
+        _sdoPassword = dataMap.getString(SDOPASS);
+        _sdoUseWKB = dataMap.getString(USEWKB);
+
+        Log logger = getLogger();
+        /*
+        logger.info("SDOHOST=" + _myHost);
+        logger.info("SDODDATBASE=" + _myDatabase);
+        logger.info("SDOPORT=" + _myPort);
+        logger.info("SDOSCHEMA=" + _mySchema);
+        logger.info("SDOUSER=" + _myUsername);
+        logger.info("SDOPASS=" + _myPassword);
+        logger.info("USEWKB=" + _myUseWKB);
+        */
+
+        if (_sdoHost == null)
+        {
+            logger.warn("SDOHOST is null");
+            throw new JobExecutionException("Unknown OraSDO host.");
+        }
+        if (_sdoDatabase == null)
+        {
+            logger.warn("PGDATABASE is null");
+            throw new JobExecutionException("Unknown OraSDO database.");
+        }
+        if (_sdoPort == null)
+        {
+            logger.warn("SDOPORT is null");
+            throw new JobExecutionException("Unknown OraSDO port.");
+        }
+        if (_sdoSchema == null)
+        {
+            logger.warn("SDOSCHEMA is null");
+            throw new JobExecutionException("Unknown OraSDO schema.");
+        }
+        if (_sdoUsername == null)
+        {
+            logger.warn("PGUSERNAME is null");
+            throw new JobExecutionException("Unknown OraSDO username.");
+        }
+        if (_sdoPassword == null)
+        {
+            logger.warn("PGPASSWORD is null");
+            throw new JobExecutionException("Unknown OraSDO password.");
+        }
+
+        Map<String, String> remote = new TreeMap<String, String>();
+        remote.put("dbtype", "OraSDO");
+        remote.put("charset", "UTF-8");
+        remote.put("host", _sdoHost);
+        remote.put("port", _sdoPort);
+        remote.put("database", _sdoDatabase);
+        remote.put("user", _sdoUsername);
+        remote.put("passwd", _sdoPassword);
+        remote.put("namespace", null);
+        sdoProperties = remote;
+    }
+
+    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);
+        createSourceDataStore();
+        createTargetDataStore();
+        if (getSourceDataStore() == null)
+        {
+            logger.warn("Cannot connect source oracle database.");
+            throw new JobExecutionException("Cannot connect source oracle database.");
+        }
+
+        if (getTargetDataStore() == null)
+        {
+            logger.warn("Cannot connect source postgreSQL database.");
+            throw new JobExecutionException("Cannot connect source postgreSQL database.");
+        }
+
+        long t1 = System.currentTimeMillis();
+        try
+        {
+            logger.info("-- step:clearOutputDatabase --");
+            clearOutputDatabase();
+            boolean bFirst = isCopyConnectivityMode();
+            if (checkConvertDB())
+            {
+                logger.info("-- step:convertOracleDB --");
+
+                for (String orgSchema : _orgSchema)
+                {
+                    OracleConvertOraSDOJobContext jobContext =
+                            (OracleConvertOraSDOJobContext) prepareJobContext(_filterPath);
+                    jobContext.setSourceDataStore(getSourceDataStore());
+                    // 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);
+            }
+
+            disconnect();
+            long t2 = System.currentTimeMillis();
+            // public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
+            // SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
+            logger.warn("use time = " + ((t2 - t1) / 60000.0) + " min");
+        } 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(OracleConvertOraSDOJobContext jobContext) throws SQLException
+    {
+        Connection connection = jobContext.getOracleConnection();
+        Statement stmt = connection.createStatement();
+        stmt.execute(AbstractOracleJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+        stmt.execute(AbstractOracleJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+    }
+
+    private void exetcuteConvert(OracleConvertOraSDOJobContext 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("ConvertDgn2OraSDOJobProgress", 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();
+                System.runFinalization();
+            }
+
+            int now = order % step;
+            if (now != current)
+            {
+                current = now;
+                jobContext.getExecutionContext().put("ConvertDgn2OraSDOJobProgress", current);
+
+            }
+        }
+        jobContext.getExecutionContext().put("ConvertDgn2OraSDOJobProgress", 100);
+
+        jobContext.commitTransaction();
+
+        logger.info("end convert job:[" + order + "]");
+        System.gc();
+        System.runFinalization();
+    }
+
+    protected OrderedMap getBlobStorageList(Connection 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);
+            int size = rs.getMetaData().getColumnCount();
+            while (rs.next())
+            {
+                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);
+        int size = rs.getMetaData().getColumnCount();
+        while (rs.next())
+        {
+            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(OracleConvertOraSDOJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        Connection 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);
+        int igdsMetaType = rsSrc.getMetaData().getColumnType(1);
+        while (rsSrc.next())
+        {
+            byte[] raw;
+            if (igdsMetaType == 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(OracleConvertOraSDOJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        Connection 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)
+        {
+            IndexDgnConvertOraSDOJobContext convertContext =
+                    new IndexDgnConvertOraSDOJobContext(getDataPath(), getTargetDataStore());
+            logger.debug("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(IndexDgnConvertOraSDOJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processIndexElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processIndexElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processIndexElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processIndexElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processIndexElement(Element element, IndexDgnConvertOraSDOJobContext 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)
+        {
+            GeneralDgnConvertOraSDOJobContext convertContext =
+                    new GeneralDgnConvertOraSDOJobContext(getDataPath(), getTargetDataStore());
+            logger.info("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(GeneralDgnConvertOraSDOJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processOtherElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processOtherElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processOtherElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processOtherElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processOtherElement(Element element, GeneralDgnConvertOraSDOJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void clearOutputDatabase()
+    {
+        /*
+        File outDataPath = new File(getDataPath(), OracleConvertOraSDOJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), IndexDgnConvertShpJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), GeneralDgnConvertShpJobContext.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)
+        {
+            FeatureDgnConvertOraSDOJobContext convertContext =
+                    new FeatureDgnConvertOraSDOJobContext(getDataPath(), getTargetDataStore(), _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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(FeatureDgnConvertOraSDOJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processFeatureElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processFeatureElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processFeatureElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processFeatureElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processFeatureElement(Element element, FeatureDgnConvertOraSDOJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void createDummyFeatureFile(JobExecutionContext context) throws JobExecutionException
+    {
+        /*
+        DummyFeatureConvertShpJobContext convertContext = new DummyFeatureConvertShpJobContext(getDataPath(), _filterPath);
+        try {
+            convertContext.startTransaction();
+            convertContext.commitTransaction();
+            convertContext.closeFeatureWriter();
+        } catch (IOException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+        */
+    }
+
+    public DataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    protected void createTargetDataStore() throws JobExecutionException
+    {
+        if (targetDataStore != null)
+        {
+            targetDataStore.dispose();
+            targetDataStore = null;
+        }
+
+        /*
+        if (!isDriverFound())
+        {
+            throw new JobExecutionException("Oracle JDBC Driver not found.-" + JDBC_DRIVER);
+        }
+        */
+
+        if (!sdoProperties.containsKey(OracleDataStoreFactory.MAXCONN.key))
+        {
+            sdoProperties.put(OracleDataStoreFactory.MAXCONN.key, "2");
+        }
+
+        if (!sdoProperties.containsKey(OracleDataStoreFactory.MINCONN.key))
+        {
+            sdoProperties.put(OracleDataStoreFactory.MINCONN.key, "1");
+        }
+
+        /*
+        if (!sdoProperties.containsKey(OracleDataStoreFactory.WKBENABLED.key))
+        {
+            sdoProperties.put(OracleDataStoreFactory.WKBENABLED.key, "true");
+        }
+        */
+
+        if (!dataStoreFactory.canProcess(sdoProperties))
+        {
+            getLogger().warn("cannot process properties-");
+            throw new JobExecutionException("cannot process properties-");
+        }
+        try
+        {
+            targetDataStore = (OracleDataStore) dataStoreFactory.createDataStore(sdoProperties);
+        } catch (IOException e)
+        {
+            getLogger().warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2PostGISJob.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2PostGISJob.java
new file mode 100644
index 0000000..47e8f13
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2PostGISJob.java
@@ -0,0 +1,1118 @@
+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.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
+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.data.DataStore;
+import org.geotools.data.postgis.PostgisDataStore;
+import org.geotools.data.postgis.PostgisDataStoreFactory;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.quartz.JobDataMap;
+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.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.postgis.FeatureDgnConvertPostGISJobContext;
+import com.ximple.eofms.jobs.context.postgis.GeneralDgnConvertPostGISJobContext;
+import com.ximple.eofms.jobs.context.postgis.IndexDgnConvertPostGISJobContext;
+import com.ximple.eofms.jobs.context.postgis.OracleConvertPostGISJobContext;
+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 OracleConvertDgn2PostGISJob extends AbstractOracleDatabaseJob
+{
+    final static Log logger = LogFactory.getLog(OracleConvertDgn2PostGISJob.class);
+
+    private static final String PGHOST = "PGHOST";
+    private static final String PGDDATBASE = "PGDDATBASE";
+    private static final String PGPORT = "PGPORT";
+    private static final String PGSCHEMA = "PGSCHEMA";
+    private static final String PGUSER = "PGUSER";
+    private static final String PGPASS = "PGPASS";
+    private static final String USEWKB = "USEWKB";
+
+    private static final int FETCHSIZE = 30;
+    private static final int COMMITSIZE = 100;
+
+    class Pair
+    {
+        Object first;
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    protected static PostgisDataStoreFactory dataStoreFactory = new PostgisDataStoreFactory();
+
+    GeometryFactory _geomFactory = new GeometryFactory();
+    protected String _pgHost;
+    protected String _pgDatabase;
+    protected String _pgPort;
+    protected String _pgSchema;
+    protected String _pgUsername;
+    protected String _pgPassword;
+    protected String _pgUseWKB;
+
+    protected Map<String, String> pgProperties;
+    protected PostgisDataStore targetDataStore;
+    // protected OracleConvertPostGISJobContext oracleJobContext;
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        /*
+        if (oracleJobContext == null)
+        {
+            oracleJobContext = new OracleConvertPostGISJobContext(getDataPath(), getTargetDataStore(), filterPath);
+        }
+        return oracleJobContext;
+        */
+        return new OracleConvertPostGISJobContext(getDataPath(), getTargetDataStore(), filterPath);
+    }
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        super.extractJobConfiguration(jobDetail);
+        JobDataMap dataMap = jobDetail.getJobDataMap();
+        _pgHost = dataMap.getString(PGHOST);
+        _pgDatabase = dataMap.getString(PGDDATBASE);
+        _pgPort = dataMap.getString(PGPORT);
+        _pgSchema = dataMap.getString(PGSCHEMA);
+        _pgUsername = dataMap.getString(PGUSER);
+        _pgPassword = dataMap.getString(PGPASS);
+        _pgUseWKB = dataMap.getString(USEWKB);
+
+        Log logger = getLogger();
+        /*
+        logger.info("PGHOST=" + _myHost);
+        logger.info("PGDDATBASE=" + _myDatabase);
+        logger.info("PGPORT=" + _myPort);
+        logger.info("PGSCHEMA=" + _mySchema);
+        logger.info("PGUSER=" + _myUsername);
+        logger.info("PGPASS=" + _myPassword);
+        logger.info("USEWKB=" + _myUseWKB);
+        */
+
+        if (_pgHost == null)
+        {
+            logger.warn("PGHOST is null");
+            throw new JobExecutionException("Unknown PostGIS host.");
+        }
+        if (_pgDatabase == null)
+        {
+            logger.warn("PGDATABASE is null");
+            throw new JobExecutionException("Unknown PostGIS database.");
+        }
+        if (_pgPort == null)
+        {
+            logger.warn("PGPORT is null");
+            throw new JobExecutionException("Unknown PostGIS port.");
+        }
+        if (_pgSchema == null)
+        {
+            logger.warn("PGSCHEMA is null");
+            throw new JobExecutionException("Unknown PostGIS schema.");
+        }
+        if (_pgUsername == null)
+        {
+            logger.warn("PGUSERNAME is null");
+            throw new JobExecutionException("Unknown PostGIS username.");
+        }
+        if (_pgPassword == null)
+        {
+            logger.warn("PGPASSWORD is null");
+            throw new JobExecutionException("Unknown PostGIS password.");
+        }
+
+        Map<String, String> remote = new TreeMap<String, String>();
+        remote.put("dbtype", "postgis");
+        remote.put("charset", "UTF-8");
+        remote.put("host", _pgHost);
+        remote.put("port", _pgPort);
+        remote.put("database", _pgDatabase);
+        remote.put("user", _pgUsername);
+        remote.put("passwd", _pgPassword);
+        remote.put("namespace", null);
+        pgProperties = remote;
+    }
+
+    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);
+        createSourceDataStore();
+        createTargetDataStore();
+        if (getSourceDataStore() == null)
+        {
+            logger.warn("Cannot connect source oracle database.");
+            throw new JobExecutionException("Cannot connect source oracle database.");
+        }
+
+        if (getTargetDataStore() == null)
+        {
+            logger.warn("Cannot connect source postgreSQL database.");
+            throw new JobExecutionException("Cannot connect source postgreSQL database.");
+        }
+
+        long t1 = System.currentTimeMillis();
+        try
+        {
+            logger.info("-- step:clearOutputDatabase --");
+            clearOutputDatabase();
+            if (checkConvertDB())
+            {
+                logger.info("-- step:convertOracleDB --");
+
+                OracleConvertPostGISJobContext jobContext =
+                        (OracleConvertPostGISJobContext) prepareJobContext(_filterPath);
+                jobContext.setSourceDataStore(getSourceDataStore());
+                // jobContext.setConvertElementIn(_convertElementIn);
+                jobContext.setElementLogging(checkElementLogging());
+                jobContext.setExecutionContext(context);
+                if (isCopyConnectivityMode())
+                    copyConnectivity(jobContext);
+
+                for (String orgSchema : _orgSchema)
+                {
+                    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);
+            }
+
+            disconnect();
+            long t2 = System.currentTimeMillis();
+            // public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
+            // SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
+            logger.warn("use time = " + ((int) ((t2 - t1) / 60000.0)) + " min - " +
+                    (((int) ((t2 - t1) % 60000.0)) / 1000) + " sec");
+        } 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(OracleConvertPostGISJobContext jobContext) throws SQLException
+    {
+        Connection connection = jobContext.getOracleConnection();
+        Statement stmt = connection.createStatement();
+        stmt.execute(AbstractOracleJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+        stmt.execute(AbstractOracleJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+        stmt.close();
+    }
+
+    private void exetcuteConvert(OracleConvertPostGISJobContext 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("ConvertDgn2PostGISJobProgress", 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();
+                System.runFinalization();
+            }
+
+            int now = order % step;
+            if (now != current)
+            {
+                current = now;
+                jobContext.getExecutionContext().put("ConvertDgn2PostGISJobProgress", current);
+
+            }
+        }
+        jobContext.getExecutionContext().put("ConvertDgn2PostGISJobProgress", 100);
+
+        jobContext.commitTransaction();
+        jobContext.resetFeatureContext();
+        logger.info("end convert job:[" + order + "]");
+        System.gc();
+        System.runFinalization();
+    }
+
+    protected OrderedMap getBlobStorageList(Connection 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);
+            int size = rs.getMetaData().getColumnCount();
+
+            while (rs.next())
+            {
+                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);
+        try
+        {
+            int size = rs.getMetaData().getColumnCount();
+            while (rs.next())
+            {
+                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;
+            }
+        } finally
+        {
+            rs.close();
+            stmt.close();
+        }
+        return orderedMap;
+    }
+
+    protected void queryIgsetElement(OracleConvertPostGISJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        Connection 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);
+        int igdsMetaType = rsSrc.getMetaData().getColumnType(1);
+        while (rsSrc.next())
+        {
+            byte[] raw;
+            if (igdsMetaType == 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(OracleConvertPostGISJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        Connection 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);
+
+        try
+        {
+            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);
+                }
+            }
+        } finally
+        {
+            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)
+        {
+            IndexDgnConvertPostGISJobContext convertContext =
+                    new IndexDgnConvertPostGISJobContext(getDataPath(), getTargetDataStore());
+            logger.debug("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.clearOutputDatabase();
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(IndexDgnConvertPostGISJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processIndexElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processIndexElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processIndexElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processIndexElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processIndexElement(Element element, IndexDgnConvertPostGISJobContext 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)
+        {
+            GeneralDgnConvertPostGISJobContext convertContext =
+                    new GeneralDgnConvertPostGISJobContext(getDataPath(), getTargetDataStore());
+            logger.info("--- start dgnfile-" + dgnFile.toString() + " ---");
+            try
+            {
+                convertContext.setExecutionContext(context);
+                String dgnPaths[] = StringUtils.splitToArray(dgnFile.toString(), File.separator);
+                convertContext.setFilename(dgnPaths[dgnPaths.length - 1]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(GeneralDgnConvertPostGISJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processOtherElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processOtherElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processOtherElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processOtherElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processOtherElement(Element element, GeneralDgnConvertPostGISJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void clearOutputDatabase()
+    {
+        /*
+        File outDataPath = new File(getDataPath(), OracleConvertPostGISJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), IndexDgnConvertShpJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), GeneralDgnConvertShpJobContext.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)
+        {
+            FeatureDgnConvertPostGISJobContext convertContext =
+                    new FeatureDgnConvertPostGISJobContext(getDataPath(), getTargetDataStore(), _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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(FeatureDgnConvertPostGISJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processFeatureElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processFeatureElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processFeatureElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processFeatureElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processFeatureElement(Element element, FeatureDgnConvertPostGISJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void createDummyFeatureFile(JobExecutionContext context) throws JobExecutionException
+    {
+        /*
+        DummyFeatureConvertShpJobContext convertContext = new DummyFeatureConvertShpJobContext(getDataPath(), _filterPath);
+        try {
+            convertContext.startTransaction();
+            convertContext.commitTransaction();
+            convertContext.closeFeatureWriter();
+        } catch (IOException e)
+        {
+            logger.warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+        */
+    }
+
+    public DataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    protected void createTargetDataStore() throws JobExecutionException
+    {
+        if (targetDataStore != null)
+        {
+            targetDataStore.dispose();
+            targetDataStore = null;
+        }
+
+        /*
+        if (!isDriverFound())
+        {
+            throw new JobExecutionException("Oracle JDBC Driver not found.-" + JDBC_DRIVER);
+        }
+        */
+
+        if (!pgProperties.containsKey(PostgisDataStoreFactory.MAXCONN.key))
+        {
+            pgProperties.put(PostgisDataStoreFactory.MAXCONN.key, "10");
+        }
+
+        if (!pgProperties.containsKey(PostgisDataStoreFactory.MINCONN.key))
+        {
+            pgProperties.put(PostgisDataStoreFactory.MINCONN.key, "1");
+        }
+
+        if (!pgProperties.containsKey(PostgisDataStoreFactory.WKBENABLED.key))
+        {
+            pgProperties.put(PostgisDataStoreFactory.WKBENABLED.key, "true");
+        }
+
+        if (!dataStoreFactory.canProcess(pgProperties))
+        {
+            getLogger().warn("cannot process properties-");
+            throw new JobExecutionException("cannot process properties-");
+        }
+        try
+        {
+            targetDataStore = (PostgisDataStore) dataStoreFactory.createDataStore(pgProperties);
+        } catch (IOException e)
+        {
+            getLogger().warn(e.getMessage(), e);
+            throw new JobExecutionException(e.getMessage(), e);
+        }
+    }
+
+}
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..4777d94
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleConvertDgn2ShpJob.java
@@ -0,0 +1,958 @@
+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.sql.Connection;
+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.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.shapefile.FeatureDgnConvertShpJobContext;
+import com.ximple.eofms.jobs.context.shapefile.GeneralDgnConvertShpJobContext;
+import com.ximple.eofms.jobs.context.shapefile.IndexDgnConvertShpJobContext;
+import com.ximple.eofms.jobs.context.shapefile.OracleConvertShapefilesJobContext;
+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 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 OracleConvertShapefilesJobContext(filterPath);
+    }
+
+    protected void extractJobConfiguration(JobDetail jobDetail) throws JobExecutionException
+    {
+        super.extractJobConfiguration(jobDetail);
+    }
+
+    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);
+        createSourceDataStore();
+        if (getSourceDataStore() == null)
+        {
+            throw new JobExecutionException("Cannot connect source oracle database.");
+        }
+
+        try
+        {
+            logger.info("-- step:clearOutputDirectory --");
+            clearOutputDirectory();
+            boolean bFirst = isCopyConnectivityMode();
+            if (checkConvertDB())
+            {
+                logger.info("-- step:convertOracleDB --");
+
+                for (String orgSchema : _orgSchema)
+                {
+                    OracleConvertShapefilesJobContext jobContext = (OracleConvertShapefilesJobContext) prepareJobContext(_filterPath);
+                    jobContext.setSourceDataStore(getSourceDataStore());
+                    jobContext.setDataPath(_dataPath);
+                    jobContext.setConvertElementIn(_convertElementIn);
+                    jobContext.setElementLogging(checkElementLogging());
+                    jobContext.setExecutionContext(context);
+
+                    if (bFirst)
+                    {
+                        copyConnectivity(jobContext);
+                        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);
+            }
+
+            disconnect();
+        } 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(OracleConvertShapefilesJobContext jobContext) throws SQLException
+    {
+        Connection connection = jobContext.getOracleConnection();
+        Statement stmt = connection.createStatement();
+        stmt.execute(OracleConvertShapefilesJobContext.TRUNCATE_CONNECTIVITY_WEBCHECK);
+        stmt.execute(OracleConvertShapefilesJobContext.COPY_CONNECTIVITY_TO_WEBCHECK);
+    }
+
+    private void exetcuteConvert(OracleConvertShapefilesJobContext 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();
+                System.runFinalization();
+            }
+
+            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();
+        System.runFinalization();
+    }
+
+    protected OrderedMap getBlobStorageList(Connection 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);
+            int size = rs.getMetaData().getColumnCount();
+
+            while (rs.next())
+            {
+                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);
+        int size = rs.getMetaData().getColumnCount();
+
+        while (rs.next())
+        {
+            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(OracleConvertShapefilesJobContext jobContext,
+                                     String srcschema, String srctable) throws SQLException
+    {
+        Connection 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);
+        int igdsMetaType = rsSrc.getMetaData().getColumnType(1);
+        while (rsSrc.next())
+        {
+            byte[] raw;
+
+            if (igdsMetaType == 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(OracleConvertShapefilesJobContext jobContext,
+                                   String srcschema, String srctable) throws SQLException
+    {
+        Connection 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)
+        {
+            IndexDgnConvertShpJobContext convertContext = new IndexDgnConvertShpJobContext(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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(IndexDgnConvertShpJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processIndexElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processIndexElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processIndexElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processIndexElement(lastComplex, convertContext);
+        }
+
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processIndexElement(Element element, IndexDgnConvertShpJobContext 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)
+        {
+            GeneralDgnConvertShpJobContext convertContext = new GeneralDgnConvertShpJobContext(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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(GeneralDgnConvertShpJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processOtherElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processOtherElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processOtherElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processOtherElement(lastComplex, convertContext);
+        }
+
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processOtherElement(Element element, GeneralDgnConvertShpJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void clearOutputDirectory()
+    {
+        File outDataPath = new File(getDataPath(), OracleConvertShapefilesJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), IndexDgnConvertShpJobContext.SHPOUTPATH);
+        if (outDataPath.exists() && outDataPath.isDirectory())
+        {
+            deleteFilesInPath(outDataPath);
+        }
+        outDataPath = new File(getDataPath(), GeneralDgnConvertShpJobContext.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)
+        {
+            FeatureDgnConvertShpJobContext convertContext = new FeatureDgnConvertShpJobContext(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]);
+                convertContext.startTransaction();
+
+                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();
+                System.runFinalization();
+            } 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(FeatureDgnConvertShpJobContext 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()))
+                {
+					if (lastComplex != null)
+					{
+                    	processFeatureElement(lastComplex, convertContext);
+                    	lastComplex = null;
+					}
+
+                    processFeatureElement(element, convertContext);
+                } else if (element.isComponentElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        ((ComplexElement) lastComplex).add(element);
+                    }
+                } else if (type.isComplexElement())
+                {
+                    if (lastComplex != null)
+                    {
+                        processFeatureElement(lastComplex, convertContext);
+                    }
+					lastComplex = element;
+                }
+            }
+            count++;
+        }
+
+        if (lastComplex != null)
+        {
+            processFeatureElement(lastComplex, convertContext);
+        }
+        logger.debug("ElementRecord Count=" + count);
+    }
+
+    private void processFeatureElement(Element element, FeatureDgnConvertShpJobContext convertContext)
+            throws IllegalAttributeException, SchemaException
+    {
+        convertContext.putFeatureCollection(element);
+    }
+
+    private void createDummyFeatureFile(JobExecutionContext context) throws JobExecutionException
+    {
+        /*
+        DummyFeatureConvertShpJobContext convertContext = new DummyFeatureConvertShpJobContext(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/OracleElementLogger.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java
new file mode 100644
index 0000000..2b146b3
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleElementLogger.java
@@ -0,0 +1,420 @@
+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 java.util.ArrayList;
+import java.util.ListIterator;
+
+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;
+import com.ximple.io.dgn7.IElementHandler;
+import com.ximple.io.dgn7.Dgn7fileException;
+
+public class OracleElementLogger
+{
+    static Log logger = LogFactory.getLog(OracleElementLogger.class);
+    private static final String DEFAULT_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;
+    private ArrayList<byte[]> dgnFileHeader = null;
+    private String elmOutPath;
+
+    public OracleElementLogger(Connection connection)
+    {
+        this.connection = connection;
+        elmOutPath = DEFAULT_ELMOUTPATH;
+    }
+
+    public OracleElementLogger(Connection connection, String elmOutPath)
+    {
+        this.connection = connection;
+        this.elmOutPath = elmOutPath;
+    }
+
+    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;
+                }
+            }
+        }
+
+        ArrayList<ByteBuffer> subBuffers = new ArrayList<ByteBuffer>();
+        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);
+                ComplexChainElement complexElement = (ComplexChainElement) element;
+                ListIterator it = complexElement.listIterator();
+                while (it.hasNext())
+                {
+                    Element subElm = (Element) it.next();
+                    try
+                    {
+                        IElementHandler handler = subElm.getElementType().getElementHandler();
+                        size = handler.getLength(subElm);
+                        ByteBuffer subBuf = ByteBuffer.allocate(size * 2);
+                        handler.write(subBuf, subElm);
+                        subBuffers.add(subBuf);
+                    } catch (Dgn7fileException e)
+                    {
+                        logger.warn(e.getMessage(), e);
+                    }
+                }
+            } else if (element instanceof ComplexShapeElement)
+            {
+                int size = ComplexShapeElement.ElementHandler.getInstance().getLength(element);
+                buf = ByteBuffer.allocate(size * 2);
+                ComplexShapeElement.ElementHandler.getInstance().write(buf, element);
+                ComplexShapeElement complexElement = (ComplexShapeElement) element;
+                ListIterator it = complexElement.listIterator();
+                while (it.hasNext())
+                {
+                    Element subElm = (Element) it.next();
+                    try
+                    {
+                        IElementHandler handler = subElm.getElementType().getElementHandler();
+                        size = handler.getLength(subElm);
+                        ByteBuffer subBuf = ByteBuffer.allocate(size * 2);
+                        handler.write(subBuf, subElm);
+                        subBuffers.add(subBuf);
+                    } catch (Dgn7fileException e)
+                    {
+                        logger.warn(e.getMessage(), e);
+                    }
+                }
+            } 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);
+                TextNodeElement nodeElement = (TextNodeElement) element;
+                ListIterator it = nodeElement.listIterator();
+                while (it.hasNext())
+                {
+                    Element subElm = (Element) it.next();
+                    try
+                    {
+                        IElementHandler handler = subElm.getElementType().getElementHandler();
+                        size = handler.getLength(subElm);
+                        ByteBuffer subBuf = ByteBuffer.allocate(size * 2);
+                        handler.write(subBuf, subElm);
+                        subBuffers.add(subBuf);
+                    } catch (Dgn7fileException e)
+                    {
+                        logger.warn(e.getMessage(), e);
+                    }
+                }
+            }
+
+            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);
+                }
+            }
+        }
+
+        if ((subBuffers.size() != 0) && (fch != null))
+        {
+            for (ByteBuffer buf : subBuffers)
+            {
+                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;
+        }
+
+        if (dgnFileHeader != null)
+        {
+            for (byte[] raw : dgnFileHeader)
+            {
+                putElementIntoStream(raw);
+            }
+            return;
+        }
+
+        dgnFileHeader = new ArrayList<byte[]>();
+        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);
+        int igdsMetaType = rsSrc.getMetaData().getColumnType(1);
+
+        while (rsSrc.next())
+        {
+            byte[] raw;
+
+            if (igdsMetaType == Types.BLOB)
+            {
+                BLOB blob = (BLOB) rsSrc.getBlob(1);
+
+                raw = getBytesFromBLOB(blob);
+                blob.close();
+            } else
+            {
+                raw = rsSrc.getBytes(1);
+            }
+
+            if (raw != null)
+            {
+                dgnFileHeader.add(raw);
+                putElementIntoStream(raw);
+            }
+        }
+        rsSrc.close();
+        stmtSrc.close();
+    }
+
+    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;
+
+        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)
+        {
+            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;
+        }
+    }
+
+    public boolean isSchemaChanged()
+    {
+        return schemaChanged;
+    }
+}
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..76865af
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/OracleUpgradeBlob2UDTJob.java
@@ -0,0 +1,73 @@
+package com.ximple.eofms.jobs;
+
+import java.sql.SQLException;
+import java.sql.Connection;
+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;
+
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.OracleUpgradeJobContext;
+
+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);
+        createSourceDataStore();
+        if (getSourceDataStore() == null)
+        {
+            throw new JobExecutionException("Cannot connect source oracle database.");
+        }
+
+        AbstractOracleJobContext jobContext = prepareJobContext(_filterPath);
+        jobContext.setSourceDataStore(getSourceDataStore());
+
+        try
+        {
+            for (String orgSchema : _orgSchema)
+            {
+                exetcuteConvert(jobContext, orgSchema, _dataPath);
+            }
+        } catch (SQLException e)
+        {
+            throw new JobExecutionException("Database error.", e);
+        }
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    protected AbstractOracleJobContext prepareJobContext(String filterPath)
+    {
+        return new OracleUpgradeJobContext();
+    }
+
+    private void exetcuteConvert(AbstractOracleJobContext jobContext,
+                                 String orgSchema, String dataPath) throws SQLException
+    {
+        Connection 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/context/AbstractDgnFileJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractDgnFileJobContext.java
new file mode 100644
index 0000000..8448eaf
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractDgnFileJobContext.java
@@ -0,0 +1,83 @@
+package com.ximple.eofms.jobs.context;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+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 Map 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 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;
+    }
+
+    public abstract Log getLogger();
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractOracleJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractOracleJobContext.java
new file mode 100644
index 0000000..19f757d
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/AbstractOracleJobContext.java
@@ -0,0 +1,286 @@
+package com.ximple.eofms.jobs.context;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.sql.Connection;
+
+import org.apache.commons.logging.Log;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.oracle.OracleDataStore;
+
+import oracle.jdbc.OracleConnection;
+
+public abstract class AbstractOracleJobContext
+{
+    /**
+     * 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
+     */
+    public 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;";
+
+    public 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";
+    protected OracleDataStore sourceDataStore;
+
+    protected String _dataPath;
+    protected Properties properties;
+    protected boolean _elementLogging;
+    private Connection connection = null;
+
+    public OracleDataStore getSourceDataStore()
+    {
+        return sourceDataStore;
+    }
+
+    public void setSourceDataStore(DataStore sourceDataStore)
+    {
+        if ((sourceDataStore != null) && (sourceDataStore instanceof OracleDataStore))
+        {
+            this.sourceDataStore = (OracleDataStore) sourceDataStore;
+        } else
+        {
+            assert sourceDataStore != null;
+            getLogger().warn("setSourceDataStore(datastore) need OracleDataStore but got " +
+                    sourceDataStore.getClass().getName());
+        }
+    }
+
+    public Connection getOracleConnection()
+    {
+        if (connection != null) return connection;
+        try
+        {
+            if (sourceDataStore != null)
+            {
+                connection = sourceDataStore.getConnection(Transaction.AUTO_COMMIT);
+            }
+
+        } catch (IOException e)
+        {
+            getLogger().warn(e.getMessage(), e);
+        }
+
+        return connection;
+    }
+
+    public void setDataPath(String dataPath)
+    {
+        _dataPath = dataPath;
+    }
+
+    public String getDataPath()
+    {
+        return _dataPath;
+    }
+
+    public boolean getElementLogging()
+    {
+        return _elementLogging;
+    }
+
+    public void setElementLogging(boolean elementLogging)
+    {
+        _elementLogging = elementLogging;
+    }
+
+    public abstract void startTransaction();
+
+    public abstract void commitTransaction();
+
+    public abstract void rollbackTransaction();
+
+    protected abstract Log getLogger();
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/OracleUpgradeJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/OracleUpgradeJobContext.java
new file mode 100644
index 0000000..29e1acf
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/OracleUpgradeJobContext.java
@@ -0,0 +1,23 @@
+package com.ximple.eofms.jobs.context;
+
+import org.apache.commons.logging.Log;
+
+public class OracleUpgradeJobContext extends AbstractOracleJobContext
+{
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+
+    protected Log getLogger()
+    {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractDgnToMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractDgnToMySQLJobContext.java
new file mode 100644
index 0000000..34b94ea
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractDgnToMySQLJobContext.java
@@ -0,0 +1,66 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+import java.io.IOException;
+import java.sql.Connection;
+
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.mysql.MySQLDataStore;
+import org.geotools.feature.FeatureType;
+
+import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+
+public abstract class AbstractDgnToMySQLJobContext extends AbstractDgnFileJobContext
+{
+    protected MySQLDataStore targetDataStore;
+
+    public AbstractDgnToMySQLJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath);
+        this.targetDataStore = (MySQLDataStore) targetDataStore;
+    }
+
+    public MySQLDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(MySQLDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (targetDataStore != null)
+        {
+            try
+            {
+                return targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+            } catch (IOException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+            }
+        }
+        return null;
+    }
+
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractOracleToMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractOracleToMySQLJobContext.java
new file mode 100644
index 0000000..8fb18fc
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/AbstractOracleToMySQLJobContext.java
@@ -0,0 +1,70 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+import java.io.IOException;
+import java.sql.Connection;
+
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.mysql.MySQLDataStore;
+import org.geotools.feature.FeatureType;
+
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+
+public abstract class AbstractOracleToMySQLJobContext extends AbstractOracleJobContext
+{
+    protected MySQLDataStore targetDataStore;
+
+    public AbstractOracleToMySQLJobContext(String dataPath, DataStore targetDataStore)
+    {
+        if ((targetDataStore != null) && (targetDataStore instanceof MySQLDataStore))
+        {
+            this.targetDataStore = (MySQLDataStore) targetDataStore;
+        } else
+        {
+            getLogger().info("targetDataStore has wrong.");
+        }
+        setDataPath(dataPath);
+    }
+
+    public MySQLDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(MySQLDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (targetDataStore != null)
+        {
+            try
+            {
+                return targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+            } catch (IOException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+            }
+        }
+        return null;
+    }
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/DummyFeatureConvertMySQlJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/DummyFeatureConvertMySQlJobContext.java
new file mode 100644
index 0000000..d67239c
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/DummyFeatureConvertMySQlJobContext.java
@@ -0,0 +1,304 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+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 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.DataStore;
+import org.geotools.data.Transaction;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.jobs.context.orasdo.AbstractDgnToOraSDOJobContext;
+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 DummyFeatureConvertMySQlJobContext extends AbstractDgnToMySQLJobContext
+{
+    static final Log logger = LogFactory.getLog(DummyFeatureConvertMySQlJobContext.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;
+    private boolean withIndex = false;
+
+    public DummyFeatureConvertMySQlJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 OracleSDO:" + featureType.getTypeName());
+            }
+            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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/FeatureDgnConvertMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/FeatureDgnConvertMySQLJobContext.java
new file mode 100644
index 0000000..3f5a0f6
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/FeatureDgnConvertMySQLJobContext.java
@@ -0,0 +1,268 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+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 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.DataStore;
+import org.geotools.data.Transaction;
+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.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class FeatureDgnConvertMySQLJobContext extends AbstractDgnToMySQLJobContext
+{
+    static final Log logger = LogFactory.getLog(FeatureDgnConvertMySQLJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public FeatureDgnConvertMySQLJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 OracleSDO:" + featureType.getTypeName());
+            }
+            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 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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java
new file mode 100644
index 0000000..de90663
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java
@@ -0,0 +1,521 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.util.Iterator;
+import java.util.List;
+import java.net.MalformedURLException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+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.geotools.feature.Feature;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.SimpleFeature;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Geometry;
+
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.TextElement;
+import com.ximple.io.dgn7.ShapeElement;
+import com.ximple.io.dgn7.LineStringElement;
+import com.ximple.io.dgn7.ArcElement;
+import com.ximple.io.dgn7.TextNodeElement;
+import com.ximple.io.dgn7.LineElement;
+import com.ximple.io.dgn7.EllipseElement;
+import com.ximple.io.dgn7.ComplexChainElement;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class GeneralDgnConvertMySQLJobContext  extends AbstractDgnToMySQLJobContext
+{
+    static final Log logger = LogFactory.getLog(GeneralDgnConvertMySQLJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public GeneralDgnConvertMySQLJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save into OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 into OracleSDO:" + featureType.getTypeName());
+            }
+            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 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 = FeatureTypeBuilderUtil.createNormalPointFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createArcFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalArcFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createEllipseFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalEllipseFeatureTypeBuilder(featureName);
+            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() + "_ML";
+            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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/IndexDgnConvertMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/IndexDgnConvertMySQLJobContext.java
new file mode 100644
index 0000000..c825407
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/IndexDgnConvertMySQLJobContext.java
@@ -0,0 +1,320 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.net.MalformedURLException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+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.geotools.feature.Feature;
+import org.geotools.feature.FeatureTypeBuilder;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.IllegalAttributeException;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.SimpleFeature;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.Coordinate;
+
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+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.TextElement;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class IndexDgnConvertMySQLJobContext extends AbstractDgnToMySQLJobContext
+{
+    static final Log logger = LogFactory.getLog(IndexDgnConvertMySQLJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    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 IndexDgnConvertMySQLJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.getFeatureWriterAppend(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 OracleSDO:" + featureType.getTypeName());
+            }
+            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 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 = FeatureTypeBuilderUtil.createNormalIndexFeatureTypeBuilder(featureName);
+        }
+        return typeBuilderRect.getFeatureType();
+    }
+
+    public FeatureType createFeatureElement2(String featureName) throws SchemaException
+    {
+        if (typeBuilderPnt == null)
+        {
+            typeBuilderPnt = FeatureTypeBuilderUtil.createNormalIndexTextFeatureTypeBuilder(featureName);
+        }
+        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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/OracleConvertMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/OracleConvertMySQLJobContext.java
new file mode 100644
index 0000000..890fced
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/OracleConvertMySQLJobContext.java
@@ -0,0 +1,311 @@
+package com.ximple.eofms.jobs.context.mysql;
+
+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 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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.mysql.MySQLDataStoreFactory;
+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.eofms.jobs.OracleElementLogger;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class OracleConvertMySQLJobContext extends AbstractOracleToMySQLJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertMySQLJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    static MySQLDataStoreFactory dataStoreFactory = new MySQLDataStoreFactory();
+
+    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 currentSchema = null;
+    private String mysqlCurrentSchema = null;
+    private boolean schemaChanged = false;
+    // private String _convertElementIn = null;
+
+    public OracleConvertMySQLJobContext(String dataPath, DataStore oraDS, String filterConfig)
+    {
+        super(dataPath, oraDS);
+        _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();
+                FeatureWriter writer = null;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    DataStore postGisDataStore = null;
+                    postGisDataStore = dataStoreFactory.createDataStore(properties);
+
+                    boolean existTable = isExistFeature(featureType);
+
+                    if (!existTable)
+                    {
+                        postGisDataStore.createSchema(featureType);
+                        writer = postGisDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = postGisDataStore.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 OracleSDO:" + featureType.getTypeName());
+            }
+            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();
+    }
+
+    protected OracleElementLogger getElementLogger()
+    {
+        if (elmLogger == null)
+        {
+            elmLogger = new OracleElementLogger(getOracleConnection());
+            elmLogger.setDataPath(this.getDataPath());
+        }
+        return elmLogger;
+    }
+
+    public boolean isSchemaChanged()
+    {
+        return schemaChanged;
+    }
+
+    public String getCurrentSchema()
+    {
+        return currentSchema;
+    }
+
+    public void setCurrentSchema(String querySchema)
+    {
+        this.currentSchema = querySchema;
+        this.schemaChanged = true;
+    }
+
+    protected Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractDgnToOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractDgnToOraSDOJobContext.java
new file mode 100644
index 0000000..2596fc8
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractDgnToOraSDOJobContext.java
@@ -0,0 +1,65 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.IOException;
+import java.sql.Connection;
+
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.oracle.OracleDataStore;
+import org.geotools.feature.FeatureType;
+
+import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+
+public abstract class AbstractDgnToOraSDOJobContext extends AbstractDgnFileJobContext
+{
+    protected OracleDataStore targetDataStore;
+
+    public AbstractDgnToOraSDOJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath);
+        this.targetDataStore = (OracleDataStore) targetDataStore;
+    }
+
+    public OracleDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(OracleDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (targetDataStore != null)
+        {
+            try
+            {
+                return targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+            } catch (IOException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+            }
+        }
+        return null;
+    }
+
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractOracleToOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractOracleToOraSDOJobContext.java
new file mode 100644
index 0000000..d535cb6
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/AbstractOracleToOraSDOJobContext.java
@@ -0,0 +1,72 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.IOException;
+import java.sql.Connection;
+
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.oracle.OracleDataStore;
+import org.geotools.feature.FeatureType;
+
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.jobs.context.mysql.OracleConvertMySQLJobContext;
+
+public abstract class AbstractOracleToOraSDOJobContext extends AbstractOracleJobContext
+{
+    protected OracleDataStore targetDataStore;
+
+    public AbstractOracleToOraSDOJobContext(String dataPath, DataStore targetDataStore)
+    {
+        if ((targetDataStore != null) && (targetDataStore instanceof OracleDataStore))
+        {
+            this.targetDataStore = (OracleDataStore) targetDataStore;
+        } else
+        {
+            getLogger().info("targetDataStore has wrong.");
+        }
+        setDataPath(dataPath);
+    }
+
+    public OracleDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(OracleDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (targetDataStore != null)
+        {
+            try
+            {
+                return targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+            } catch (IOException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+            }
+        }
+        return null;
+    }
+
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/DummyFeatureConvertOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/DummyFeatureConvertOraSDOJobContext.java
new file mode 100644
index 0000000..b892399
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/DummyFeatureConvertOraSDOJobContext.java
@@ -0,0 +1,303 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.ElementDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.TypeCompIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeIdDispatchableFilter;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class DummyFeatureConvertOraSDOJobContext extends AbstractDgnToOraSDOJobContext
+{
+    static final Log logger = LogFactory.getLog(DummyFeatureConvertOraSDOJobContext.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;
+    private boolean withIndex = false;
+
+    public DummyFeatureConvertOraSDOJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 OracleSDO:" + featureType.getTypeName());
+            }
+            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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/FeatureDgnConvertOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/FeatureDgnConvertOraSDOJobContext.java
new file mode 100644
index 0000000..19c149a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/FeatureDgnConvertOraSDOJobContext.java
@@ -0,0 +1,267 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class FeatureDgnConvertOraSDOJobContext extends AbstractDgnToOraSDOJobContext
+{
+    static final Log logger = LogFactory.getLog(FeatureDgnConvertOraSDOJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public FeatureDgnConvertOraSDOJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 OracleSDO:" + featureType.getTypeName());
+            }
+            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 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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/GeneralDgnConvertOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/GeneralDgnConvertOraSDOJobContext.java
new file mode 100644
index 0000000..fcdcece
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/GeneralDgnConvertOraSDOJobContext.java
@@ -0,0 +1,522 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.MalformedURLException;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 GeneralDgnConvertOraSDOJobContext extends AbstractDgnToOraSDOJobContext
+{
+    static final Log logger = LogFactory.getLog(GeneralDgnConvertOraSDOJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public GeneralDgnConvertOraSDOJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save into OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 into OracleSDO:" + featureType.getTypeName());
+            }
+            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 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 = FeatureTypeBuilderUtil.createNormalPointFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createArcFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalArcFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createEllipseFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalEllipseFeatureTypeBuilder(featureName);
+            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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/IndexDgnConvertOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/IndexDgnConvertOraSDOJobContext.java
new file mode 100644
index 0000000..5db125b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/IndexDgnConvertOraSDOJobContext.java
@@ -0,0 +1,320 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.MalformedURLException;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TPCLIDConverter;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 IndexDgnConvertOraSDOJobContext extends AbstractDgnToOraSDOJobContext
+{
+    static final Log logger = LogFactory.getLog(IndexDgnConvertOraSDOJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    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 IndexDgnConvertOraSDOJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save OracleSDO:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.getFeatureWriterAppend(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 OracleSDO:" + featureType.getTypeName());
+            }
+            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 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 = FeatureTypeBuilderUtil.createNormalIndexFeatureTypeBuilder(featureName);
+        }
+        return typeBuilderRect.getFeatureType();
+    }
+
+    public FeatureType createFeatureElement2(String featureName) throws SchemaException
+    {
+        if (typeBuilderPnt == null)
+        {
+            typeBuilderPnt = FeatureTypeBuilderUtil.createNormalIndexTextFeatureTypeBuilder(featureName);
+        }
+        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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/OracleConvertOraSDOJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/OracleConvertOraSDOJobContext.java
new file mode 100644
index 0000000..f51ec60
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/orasdo/OracleConvertOraSDOJobContext.java
@@ -0,0 +1,310 @@
+package com.ximple.eofms.jobs.context.orasdo;
+
+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 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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.oracle.OracleDataStoreFactory;
+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.eofms.jobs.OracleElementLogger;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class OracleConvertOraSDOJobContext extends AbstractOracleToOraSDOJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertOraSDOJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    static OracleDataStoreFactory dataStoreFactory = new OracleDataStoreFactory();
+
+    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 currentSchema = null;
+    private String oraCurrentSchema = null;
+    private boolean schemaChanged = false;
+    // private String _convertElementIn = null;
+
+    public OracleConvertOraSDOJobContext(String dataPath, DataStore oraDS, String filterConfig)
+    {
+        super(dataPath, oraDS);
+        _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();
+                FeatureWriter writer = null;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    DataStore postGisDataStore = null;
+                    postGisDataStore = dataStoreFactory.createDataStore(properties);
+
+                    boolean existTable = isExistFeature(featureType);
+
+                    if (!existTable)
+                    {
+                        postGisDataStore.createSchema(featureType);
+                        writer = postGisDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = postGisDataStore.getFeatureWriterAppend(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    }
+                    featuresWriterContext.put(featureType.getTypeName(), writer);
+                }
+
+                ArrayList features = (ArrayList) featuresContext.get(featureType);
+                for (Object feature1 : features)
+                {
+                    Feature feature = (Feature) feature1;
+                    ((SimpleFeature) writer.next()).setAttributes(feature.getAttributes(null));
+                }
+                //writer.close();
+                logger.debug("End Save OracleSDO:" + featureType.getTypeName());
+            }
+            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();
+    }
+
+    protected OracleElementLogger getElementLogger()
+    {
+        if (elmLogger == null)
+        {
+            elmLogger = new OracleElementLogger(getOracleConnection());
+            elmLogger.setDataPath(this.getDataPath());
+        }
+        return elmLogger;
+    }
+
+    public boolean isSchemaChanged()
+    {
+        return schemaChanged;
+    }
+
+    public String getCurrentSchema()
+    {
+        return currentSchema;
+    }
+
+    public void setCurrentSchema(String querySchema)
+    {
+        this.currentSchema = querySchema;
+        this.schemaChanged = true;
+    }
+
+    protected Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java
new file mode 100644
index 0000000..f919f16
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java
@@ -0,0 +1,760 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.geotools.data.DataSourceException;
+import org.geotools.data.DataStore;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.Transaction;
+import org.geotools.data.jdbc.JDBCUtils;
+import org.geotools.data.postgis.PostgisDataStore;
+import org.geotools.feature.AttributeType;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.GeometryAttributeType;
+import org.geotools.filter.LengthFunction;
+import org.geotools.referencing.NamedIdentifier;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.opengis.filter.BinaryComparisonOperator;
+import org.opengis.filter.Filter;
+import org.opengis.filter.PropertyIsLessThan;
+import org.opengis.filter.PropertyIsLessThanOrEqualTo;
+import org.opengis.filter.expression.Literal;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.io.WKBWriter;
+import com.vividsolutions.jts.io.WKTWriter;
+
+import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+import com.ximple.eofms.util.postjts.JtsBinaryWriter;
+
+public abstract class AbstractDgnToPostGISJobContext extends AbstractDgnFileJobContext
+{
+    private static Map<String, Class> GEOM_TYPE_MAP = new HashMap<String, Class>();
+    private static Map<String, Class> GEOM3D_TYPE_MAP = new HashMap<String, Class>();
+
+    static
+    {
+        GEOM_TYPE_MAP.put("GEOMETRY", Geometry.class);
+        GEOM_TYPE_MAP.put("POINT", Point.class);
+        GEOM_TYPE_MAP.put("LINESTRING", LineString.class);
+        GEOM_TYPE_MAP.put("POLYGON", Polygon.class);
+        GEOM_TYPE_MAP.put("MULTIPOINT", MultiPoint.class);
+        GEOM_TYPE_MAP.put("MULTILINESTRING", MultiLineString.class);
+        GEOM_TYPE_MAP.put("MULTIPOLYGON", MultiPolygon.class);
+        GEOM_TYPE_MAP.put("GEOMETRYCOLLECTION", GeometryCollection.class);
+
+        GEOM3D_TYPE_MAP.put("POINTM", Point.class);
+        GEOM3D_TYPE_MAP.put("LINESTRINGM", LineString.class);
+        GEOM3D_TYPE_MAP.put("POLYGONM", Polygon.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
+        GEOM3D_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
+        GEOM3D_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
+    }
+
+    private static Map<Class, String> CLASS_MAPPINGS = new HashMap<Class, String>();
+
+    static
+    {
+        CLASS_MAPPINGS.put(String.class, "VARCHAR");
+
+        CLASS_MAPPINGS.put(Boolean.class, "BOOLEAN");
+
+        CLASS_MAPPINGS.put(Short.class, "SMALLINT");
+        CLASS_MAPPINGS.put(Integer.class, "INTEGER");
+        CLASS_MAPPINGS.put(Long.class, "BIGINT");
+
+        CLASS_MAPPINGS.put(Float.class, "REAL");
+        CLASS_MAPPINGS.put(Double.class, "DOUBLE PRECISION");
+
+        CLASS_MAPPINGS.put(BigDecimal.class, "DECIMAL");
+
+        CLASS_MAPPINGS.put(java.sql.Date.class, "DATE");
+        CLASS_MAPPINGS.put(java.util.Date.class, "DATE");
+        CLASS_MAPPINGS.put(java.sql.Time.class, "TIME");
+        CLASS_MAPPINGS.put(java.sql.Timestamp.class, "TIMESTAMP");
+    }
+
+    private static Map<Class, String> GEOM_CLASS_MAPPINGS = new HashMap<Class, String>();
+
+    //why don't we just stick this in with the non-geom class mappings?
+    static
+    {
+        // init the inverse map
+        Set keys = GEOM_TYPE_MAP.keySet();
+
+        for (Object key : keys)
+        {
+            String name = (String) key;
+            Class geomClass = GEOM_TYPE_MAP.get(name);
+            GEOM_CLASS_MAPPINGS.put(geomClass, name);
+        }
+    }
+
+    /**
+     * Maximum string size for postgres
+     */
+    private static final int MAX_ALLOWED_VALUE = 10485760;
+
+    // protected static final int BATCHSIZE = 2048;
+    protected static final int BATCHSIZE = 256;
+
+    /**
+     * Well Known Text writer (from JTS).
+     */
+    protected static WKTWriter geometryWriter = new WKTWriter();
+    protected static JtsBinaryWriter binaryWriter = new JtsBinaryWriter();
+
+    protected PostgisDataStore targetDataStore;
+    private Connection connection;
+    protected boolean schemaEnabled = true;
+
+    public AbstractDgnToPostGISJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath);
+        this.targetDataStore = (PostgisDataStore) targetDataStore;
+        this.connection = null;
+    }
+
+    public PostgisDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(PostgisDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (connection != null) return connection;
+
+        if (targetDataStore != null)
+        {
+            try
+            {
+                connection = targetDataStore.getDataSource().getConnection();
+            } catch (SQLException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+                return null;
+            }
+        }
+        return connection;
+    }
+
+    public void closeConnection()
+    {
+        if (connection == null)
+        {
+            JDBCUtils.close(connection, Transaction.AUTO_COMMIT, null);
+            connection = null;
+        }
+    }
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null; // && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    protected void deleteTable(Connection conn, String tableName) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("DELETE FROM \"");
+        sb.append(targetDataStore.getDatabaseSchemaName());
+        sb.append("\".\"");
+        sb.append(tableName);
+        sb.append('\"');
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected void dropTable(Connection conn, String tableName) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("DROP TABLE \"");
+        sb.append(targetDataStore.getDatabaseSchemaName());
+        sb.append("\".\"");
+        sb.append(tableName);
+        sb.append("\" CASCADE");
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected void dropGeometryColumn(Connection conn, String tableName, String geomField) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT DropGeometryColumn('','");
+        sb.append(tableName);
+        sb.append("','");
+        sb.append(geomField);
+        sb.append("')");
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected String dropGeometryColumn(String dbSchema, String tableName, String geomField)
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT DropGeometryColumn('");
+        sb.append(dbSchema);
+        sb.append("','");
+        sb.append(tableName);
+        sb.append("','");
+        sb.append(geomField);
+        sb.append("')");
+        getLogger().info("Execute-" + sb.toString());
+        return sb.toString();
+    }
+
+    private String addGeometryColumn(String dbSchema, String tableName, GeometryAttributeType geometryAttribute, int srid)
+    {
+        StringBuilder sql;
+        String typeName = getGeometrySQLTypeName(geometryAttribute.getBinding());
+        if (typeName == null)
+        {
+            getLogger().warn("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+            throw new RuntimeException("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+        }
+
+        sql = new StringBuilder("SELECT AddGeometryColumn('");
+        sql.append(dbSchema);
+        sql.append("','");
+        sql.append(tableName);
+        sql.append("','");
+        sql.append(geometryAttribute.getLocalName());
+        sql.append("','");
+        sql.append(srid);
+        sql.append("','");
+        sql.append(typeName);
+        sql.append("', 2);");
+
+        //prints statement for later reuse
+        return sql.toString();
+    }
+
+    public ArrayList<String> createNewSchemaTexts(FeatureType featureType) throws IOException
+    {
+        String origintableName = featureType.getTypeName();
+        String tableName = origintableName.toLowerCase();
+
+        ArrayList<String> result = new ArrayList<String>();
+
+        AttributeType[] attributeType = featureType.getAttributeTypes();
+        // String dbSchema = targetDataStore.getDatabaseSchemaName();
+
+        Connection con = getConnection();
+
+        boolean shouldDrop = tablePresent(tableName, con);
+        if (shouldDrop)
+        {
+            String sqlStr = "DROP TABLE " + encodeSchemaTableName(tableName) + ";";
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+        }
+
+        StringBuffer sql = new StringBuffer("CREATE TABLE ");
+        sql.append(encodeSchemaTableName(tableName));
+        sql.append(" ( gid serial PRIMARY KEY, ");
+        sql.append(makeNonGeomSqlCreate(attributeType));
+        sql.append(");");
+
+        String sqlStr = sql.toString();
+        getLogger().info(sqlStr);
+        result.add(sqlStr);
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            if (!(anAttributeType instanceof GeometryAttributeType))
+            {
+                continue;
+            }
+            GeometryAttributeType geomAttribute = (GeometryAttributeType) anAttributeType;
+
+            if (shouldDrop)
+            {
+            sqlStr = dropGeometryColumn("", tableName, geomAttribute.getLocalName());
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+            }
+            
+            CoordinateReferenceSystem refSys = geomAttribute.getCoordinateSystem();
+            int SRID;
+
+            if (refSys != null)
+            {
+                try
+                {
+                    Set ident = refSys.getIdentifiers();
+                    if ((ident == null || ident.isEmpty()) && refSys == DefaultGeographicCRS.WGS84)
+                    {
+                        SRID = 4326;
+                    } else
+                    {
+                        String code = ((NamedIdentifier) ident.toArray()[0]).getCode();
+                        SRID = Integer.parseInt(code);
+                    }
+                } catch (Exception e)
+                {
+                    getLogger().warn("SRID could not be determined");
+                    SRID = -1;
+                }
+            } else
+            {
+                SRID = -1;
+            }
+
+            sqlStr = addGeometryColumn("", tableName, geomAttribute, SRID);
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+
+
+            String indexName = tableName.replace('-', '_');
+            //also build a spatial index on each geometry column.
+            sql = new StringBuffer("CREATE INDEX spatial_");
+            sql.append(indexName);
+            sql.append("_");
+            sql.append(anAttributeType.getLocalName().toLowerCase());
+            sql.append(" ON ");
+            sql.append(encodeSchemaTableName(tableName));
+            sql.append(" USING GIST (");
+            sql.append(encodeSchemaColumnName(anAttributeType.getLocalName()));
+            sql.append(" gist_geometry_ops);");
+
+            sqlStr = sql.toString();
+            getLogger().info(sqlStr);
+
+            result.add(sqlStr);
+        }
+
+        return result;
+    }
+
+    private boolean tablePresent(String table, Connection conn) throws IOException
+    {
+        final int TABLE_NAME_COL = 3;
+
+        try
+        {
+            conn = getConnection();
+
+            DatabaseMetaData meta = conn.getMetaData();
+            String[] tableType = {"TABLE"};
+            ResultSet tables = meta.getTables(null,
+                    targetDataStore.getDatabaseSchemaName(), "%", tableType);
+
+            while (tables.next())
+            {
+                String tableName = tables.getString(TABLE_NAME_COL);
+
+                if (allowTable(tableName) && (tableName != null)
+                        && (tableName.equalsIgnoreCase(table)))
+                {
+                    tables.close();
+                    return (true);
+                }
+            }
+
+            tables.close();
+            return false;
+        } catch (SQLException sqlException)
+        {
+            // JDBCUtils.close(conn, Transaction.AUTO_COMMIT, sqlException);
+            // conn = null;
+
+            String message = "Error querying database for list of tables:"
+                    + sqlException.getMessage();
+            throw new DataSourceException(message, sqlException);
+        } finally
+        {
+            // JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
+        }
+    }
+
+    protected boolean allowTable(String tablename)
+    {
+        if (tablename.equals("geometry_columns"))
+        {
+            return false;
+        } else if (tablename.startsWith("spatial_ref_sys"))
+        {
+            return false;
+        }
+
+        //others?
+        return true;
+    }
+
+
+    private StringBuffer makeNonGeomSqlCreate(AttributeType[] attributeType)
+            throws IOException
+    {
+        StringBuffer buf = new StringBuffer("");
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            String typeName;
+            typeName = CLASS_MAPPINGS.get(anAttributeType.getBinding());
+            if (typeName == null)
+            {
+                typeName = GEOM_CLASS_MAPPINGS.get(anAttributeType.getBinding());
+                if (typeName != null) continue;
+            }
+
+            if (typeName != null)
+            {
+                if (typeName.equals("VARCHAR"))
+                {
+                    int length = -1;
+                    Filter f = anAttributeType.getRestriction();
+                    if (f != null && f != Filter.EXCLUDE && f != Filter.INCLUDE &&
+                            (f instanceof PropertyIsLessThan || f instanceof PropertyIsLessThanOrEqualTo))
+                    {
+                        try
+                        {
+                            BinaryComparisonOperator cf = (BinaryComparisonOperator) f;
+                            if (cf.getExpression1() instanceof LengthFunction)
+                            {
+                                length = Integer.parseInt(((Literal) cf.getExpression2()).getValue().toString());
+                            } else
+                            {
+                                if (cf.getExpression2() instanceof LengthFunction)
+                                {
+                                    length = Integer.parseInt(((Literal) cf.getExpression1()).getValue().toString());
+                                }
+                            }
+                        } catch (NumberFormatException e)
+                        {
+                            length = 256;
+                        }
+                    } else
+                    {
+                        length = 256;
+                    }
+
+                    if (length < 1)
+                    {
+                        getLogger().warn("FeatureType did not specify string length; defaulted to 256");
+                        length = 256;
+                    } else if (length > MAX_ALLOWED_VALUE)
+                    {
+                        length = MAX_ALLOWED_VALUE;
+                    }
+                    typeName = typeName + "(" + length + ")";
+                }
+
+                if (!anAttributeType.isNillable())
+                {
+                    typeName = typeName + " NOT NULL";
+                }
+
+                //TODO review!!! Is toString() always OK???
+                Object defaultValue = anAttributeType.createDefaultValue();
+
+                if (defaultValue != null)
+                {
+                    typeName = typeName + " DEFAULT '"
+                            + defaultValue.toString() + "'";
+                }
+
+                buf.append(" \"").append(anAttributeType.getLocalName()).append("\" ").append(typeName).append(",");
+
+            } else
+            {
+                String msg;
+                if (anAttributeType == null)
+                {
+                    msg = "AttributeType was null!";
+                } else
+                {
+                    msg = "Type '" + anAttributeType.getBinding() + "' not supported!";
+                }
+                throw (new IOException(msg));
+            }
+        }
+
+        return buf.deleteCharAt(buf.length() - 1);
+    }
+
+    private String getGeometrySQLTypeName(Class type)
+    {
+        String res = GEOM_CLASS_MAPPINGS.get(type);
+
+        if (res == null)
+        {
+            throw new RuntimeException("Unknown type name for class " + type
+                    + " please update GEOMETRY_MAPPINGS");
+        }
+
+        return res;
+    }
+
+    protected String getGeometryInsertText(Geometry geom, int srid) // throws IOException
+    {
+        if (geom == null)
+        {
+            return "null";
+        }
+
+        if (targetDataStore.isWKBEnabled())
+        {
+            //String wkb = WKBEncoder.encodeGeometryHex(geom);
+            String wkb = WKBWriter.bytesToHex(new WKBWriter().write(geom));
+
+            if (targetDataStore.isByteaWKB())
+            {
+                return "setSRID('" + wkb + "'::geometry," + srid + ")";
+            } else
+            {
+                return "GeomFromWKB('" + wkb + "', " + srid + ")";
+            }
+        }
+
+        String geoText = geometryWriter.write(geom);
+
+        return "GeometryFromText('" + geoText + "', " + srid + ")";
+    }
+
+    protected String makeInsertSql(Feature feature, int srid) // throws IOException
+    {
+        FeatureType featureType = feature.getFeatureType();
+
+        String tableName = encodeSchemaTableName(featureType.getTypeName());
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+
+        String attrValue;
+
+        StringBuffer statementSQL = new StringBuffer("INSERT INTO " + tableName + " (");
+
+        // encode insertion for attributes, but remember to avoid auto-increment ones,
+        // they may be included in the feature type as well
+        for (AttributeType attributeType : attributeTypes)
+        {
+            String attName = attributeType.getLocalName();
+
+            if (feature.getAttribute(attName) != null)
+            {
+                String colName = encodeSchemaColumnName(attName);
+                statementSQL.append(colName).append(",");
+            }
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+        statementSQL.append(" VALUES (");
+
+        Object[] attributes = feature.getAttributes(null);
+
+        for (int i = 0; i < attributeTypes.length; i++)
+        {
+            attrValue = null;
+
+            if (attributeTypes[i] instanceof GeometryAttributeType)
+            {
+                // String geomName = attributeTypes[i].getLocalName();
+                // int srid = ftInfo.getSRID(geomName);
+                Geometry geometry = (Geometry) attributes[i];
+
+                if (geometry == null)
+                {
+                    attrValue = "NULL";
+                } else
+                {
+                    attrValue = getGeometryInsertText(geometry, srid);
+                }
+            } else
+            {
+                if (attributes[i] != null)
+                {
+                    attrValue = addQuotes(attributes[i]);
+                }
+            }
+
+            if (attrValue != null)
+            {
+                statementSQL.append(attrValue).append(",");
+            }
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+
+        return (statementSQL.toString());
+    }
+
+    protected String makePrepareInsertSql(FeatureType featureType)
+    {
+        String tableName = encodeSchemaTableName(featureType.getTypeName());
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+
+        String attrValue;
+
+        StringBuffer statementSQL = new StringBuffer("INSERT INTO " + tableName + " (");
+
+        // encode insertion for attributes, but remember to avoid auto-increment ones,
+        // they may be included in the feature type as well
+        for (AttributeType attributeType : attributeTypes)
+        {
+            String attName = attributeType.getLocalName();
+
+            String colName = encodeSchemaColumnName(attName);
+            statementSQL.append(colName).append(",");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+        statementSQL.append(" VALUES (");
+
+        for (AttributeType attributeType : attributeTypes)
+        {
+            statementSQL.append(" ? ,");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+
+        return (statementSQL.toString());
+    }
+
+    protected String addQuotes(Object value)
+    {
+        String retString;
+
+        if (value != null)
+        {
+            if (value instanceof Number)
+            {
+                retString = value.toString();
+            } else
+            {
+                retString = "'" + doubleQuote(value) + "'";
+            }
+        } else
+        {
+            retString = "null";
+        }
+
+        return retString;
+    }
+
+    String doubleQuote(Object obj)
+    {
+        return obj.toString().replaceAll("'", "''");
+    }
+
+    protected String encodeName(String tableName)
+    {
+        return tableName;
+    }
+
+    protected String encodeColumnName(String colName)
+    {
+        return encodeName(colName);
+    }
+
+    public String encodeSchemaTableName(String tableName)
+    {
+        return schemaEnabled ? ("\"" + targetDataStore.getDatabaseSchemaName() + "\".\"" + tableName + "\"")
+                : ("\"" + tableName + "\"");
+    }
+
+    public String encodeSchemaColumnName(String columnName)
+    {
+        return "\"" + columnName + "\"";
+    }
+
+    protected void bindFeatureParameters(PreparedStatement pstmt, Feature feature) throws SQLException
+    {
+        FeatureType featureType = feature.getFeatureType();
+
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+        Object[] attributes = feature.getAttributes(null);
+
+        for (int i = 0; i < attributeTypes.length; i++)
+        {
+            if (attributeTypes[i] instanceof GeometryAttributeType)
+            {
+                pstmt.setBytes(i + 1, binaryWriter.writeBinary((Geometry) attributes[i]));
+            } else
+            {
+                if (attributeTypes[i].getBinding().equals(Short.class))
+                {
+                    pstmt.setShort(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Integer.class))
+                {
+                    pstmt.setInt(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Long.class))
+                {
+                    pstmt.setLong(i + 1, (Long) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(String.class))
+                {
+                    pstmt.setString(i + 1, (String) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Float.class))
+                {
+                    pstmt.setFloat(i + 1, (Float) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Double.class))
+                {
+                    pstmt.setDouble(i + 1, (Double) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Boolean.class))
+                {
+                    pstmt.setBoolean(i + 1, (Boolean) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(BigDecimal.class))
+                {
+                    pstmt.setBigDecimal(i + 1, (BigDecimal) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Date.class))
+                {
+                    pstmt.setDate(i + 1, (java.sql.Date) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Time.class))
+                {
+                    pstmt.setTime(i + 1, (java.sql.Time) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Timestamp.class))
+                {
+                    pstmt.setTimestamp(i + 1, (java.sql.Timestamp) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.util.Date.class))
+                {
+                    java.sql.Date sDate = new java.sql.Date(((java.util.Date) attributes[i]).getTime());
+                    pstmt.setDate(i + 1, sDate);
+                }
+            }
+
+        }
+    }
+
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java
new file mode 100644
index 0000000..bd4d0c8
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java
@@ -0,0 +1,762 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.PreparedStatement;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.ArrayList;
+import java.math.BigDecimal;
+
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.SchemaNotFoundException;
+import org.geotools.data.DataSourceException;
+import org.geotools.data.jdbc.JDBCUtils;
+import org.geotools.data.postgis.PostgisDataStore;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.AttributeType;
+import org.geotools.feature.GeometryAttributeType;
+import org.geotools.feature.Feature;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.geotools.referencing.NamedIdentifier;
+import org.geotools.filter.LengthFunction;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.filter.Filter;
+import org.opengis.filter.PropertyIsLessThan;
+import org.opengis.filter.PropertyIsLessThanOrEqualTo;
+import org.opengis.filter.BinaryComparisonOperator;
+import org.opengis.filter.expression.Literal;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.io.WKTWriter;
+import com.vividsolutions.jts.io.WKBWriter;
+
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.util.postjts.JtsBinaryWriter;
+
+public abstract class AbstractOracleToPostGISJobContext extends AbstractOracleJobContext
+{
+    private static Map<String, Class> GEOM_TYPE_MAP = new HashMap<String, Class>();
+    private static Map<String, Class> GEOM3D_TYPE_MAP = new HashMap<String, Class>();
+
+    static
+    {
+        GEOM_TYPE_MAP.put("GEOMETRY", Geometry.class);
+        GEOM_TYPE_MAP.put("POINT", Point.class);
+        GEOM_TYPE_MAP.put("LINESTRING", LineString.class);
+        GEOM_TYPE_MAP.put("POLYGON", Polygon.class);
+        GEOM_TYPE_MAP.put("MULTIPOINT", MultiPoint.class);
+        GEOM_TYPE_MAP.put("MULTILINESTRING", MultiLineString.class);
+        GEOM_TYPE_MAP.put("MULTIPOLYGON", MultiPolygon.class);
+        GEOM_TYPE_MAP.put("GEOMETRYCOLLECTION", GeometryCollection.class);
+
+        GEOM3D_TYPE_MAP.put("POINTM", Point.class);
+        GEOM3D_TYPE_MAP.put("LINESTRINGM", LineString.class);
+        GEOM3D_TYPE_MAP.put("POLYGONM", Polygon.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
+        GEOM3D_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
+        GEOM3D_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
+    }
+
+    private static Map<Class, String> CLASS_MAPPINGS = new HashMap<Class, String>();
+
+    static
+    {
+        CLASS_MAPPINGS.put(String.class, "VARCHAR");
+
+        CLASS_MAPPINGS.put(Boolean.class, "BOOLEAN");
+
+        CLASS_MAPPINGS.put(Short.class, "SMALLINT");
+        CLASS_MAPPINGS.put(Integer.class, "INTEGER");
+        CLASS_MAPPINGS.put(Long.class, "BIGINT");
+
+        CLASS_MAPPINGS.put(Float.class, "REAL");
+        CLASS_MAPPINGS.put(Double.class, "DOUBLE PRECISION");
+
+        CLASS_MAPPINGS.put(BigDecimal.class, "DECIMAL");
+
+        CLASS_MAPPINGS.put(java.sql.Date.class, "DATE");
+        CLASS_MAPPINGS.put(java.util.Date.class, "DATE");
+        CLASS_MAPPINGS.put(java.sql.Time.class, "TIME");
+        CLASS_MAPPINGS.put(java.sql.Timestamp.class, "TIMESTAMP");
+    }
+
+    private static Map<Class, String> GEOM_CLASS_MAPPINGS = new HashMap<Class, String>();
+
+    //why don't we just stick this in with the non-geom class mappings?
+    static
+    {
+        // init the inverse map
+        Set keys = GEOM_TYPE_MAP.keySet();
+
+        for (Object key : keys)
+        {
+            String name = (String) key;
+            Class geomClass = GEOM_TYPE_MAP.get(name);
+            GEOM_CLASS_MAPPINGS.put(geomClass, name);
+        }
+    }
+
+    /**
+     * Maximum string size for postgres
+     */
+    private static final int MAX_ALLOWED_VALUE = 10485760;
+
+    protected static final int BATCHSIZE = 512;
+
+    /**
+     * Well Known Text writer (from JTS).
+     */
+    protected static WKTWriter geometryWriter = new WKTWriter();
+    protected static JtsBinaryWriter binaryWriter = new JtsBinaryWriter();
+
+    protected PostgisDataStore targetDataStore;
+    private Connection connection;
+    protected boolean schemaEnabled = true;
+
+    public AbstractOracleToPostGISJobContext(String dataPath, DataStore targetDataStore)
+    {
+        if ((targetDataStore != null) && (targetDataStore instanceof PostgisDataStore))
+        {
+            this.targetDataStore = (PostgisDataStore) targetDataStore;
+        } else
+        {
+            getLogger().info("targetDataStore has wrong.");
+        }
+        setDataPath(dataPath);
+    }
+
+    public PostgisDataStore getTargetDataStore()
+    {
+        return targetDataStore;
+    }
+
+    public void setTargetDataStore(PostgisDataStore targetDataStore)
+    {
+        this.targetDataStore = targetDataStore;
+    }
+
+    public Connection getConnection()
+    {
+        if (connection != null) return connection;
+
+        if (targetDataStore != null)
+        {
+            try
+            {
+                connection = targetDataStore.getDataSource().getConnection();
+            } catch (SQLException e)
+            {
+                getLogger().warn(e.getMessage(), e);
+                return null;
+            }
+        }
+        return connection;
+    }
+
+    public void closeConnection()
+    {
+        if (connection == null)
+        {
+            JDBCUtils.close(connection, Transaction.AUTO_COMMIT, null);
+            connection = null;
+        }
+    }
+
+    protected boolean isExistFeature(FeatureType featureType)
+    {
+        try
+        {
+            FeatureType existFeatureType = targetDataStore.getSchema(featureType.getTypeName());
+            return existFeatureType != null; // && existFeatureType.equals(featureType);
+        } catch (SchemaNotFoundException e)
+        {
+            return false;
+        } catch (IOException e)
+        {
+            getLogger().info(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    protected void deleteTable(Connection conn, String tableName) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("DELETE FROM \"");
+        sb.append(targetDataStore.getDatabaseSchemaName());
+        sb.append("\".\"");
+        sb.append(tableName);
+        sb.append('\"');
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected void dropTable(Connection conn, String tableName) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("DROP TABLE \"");
+        sb.append(targetDataStore.getDatabaseSchemaName());
+        sb.append("\".\"");
+        sb.append(tableName);
+        sb.append("\" CASCADE");
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected void dropGeometryColumn(Connection conn, String tableName, String geomField) throws SQLException
+    {
+        Statement stmt = conn.createStatement();
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT DropGeometryColumn('','");
+        sb.append(tableName);
+        sb.append("','");
+        sb.append(geomField);
+        sb.append("')");
+        getLogger().info("Execute-" + sb.toString());
+        stmt.execute(sb.toString());
+        stmt.close();
+        conn.commit();
+    }
+
+    protected String dropGeometryColumn(String dbSchema, String tableName, String geomField)
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT DropGeometryColumn('");
+        sb.append(dbSchema);
+        sb.append("','");
+        sb.append(tableName);
+        sb.append("','");
+        sb.append(geomField);
+        sb.append("')");
+        getLogger().info("Execute-" + sb.toString());
+        return sb.toString();
+    }
+
+    private String addGeometryColumn(String dbSchema, String tableName, GeometryAttributeType geometryAttribute, int srid)
+    {
+        StringBuilder sql;
+        String typeName = getGeometrySQLTypeName(geometryAttribute.getBinding());
+        if (typeName == null)
+        {
+            getLogger().warn("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+            throw new RuntimeException("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+        }
+
+        sql = new StringBuilder("SELECT AddGeometryColumn('");
+        sql.append(dbSchema);
+        sql.append("','");
+        sql.append(tableName);
+        sql.append("','");
+        sql.append(geometryAttribute.getLocalName());
+        sql.append("','");
+        sql.append(srid);
+        sql.append("','");
+        sql.append(typeName);
+        sql.append("', 2);");
+
+        //prints statement for later reuse
+        return sql.toString();
+    }
+
+    public ArrayList<String> createNewSchemaTexts(FeatureType featureType) throws IOException
+    {
+        String origintableName = featureType.getTypeName();
+        String tableName = origintableName.toLowerCase();
+
+        ArrayList<String> result = new ArrayList<String>();
+
+        AttributeType[] attributeType = featureType.getAttributeTypes();
+        // String dbSchema = targetDataStore.getDatabaseSchemaName();
+
+        Connection con = getConnection();
+
+        boolean shouldDrop = tablePresent(tableName, con);
+        if (shouldDrop)
+        {
+            String sqlStr = "DROP TABLE " + encodeSchemaTableName(tableName) + ";";
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+        }
+
+        StringBuffer sql = new StringBuffer("CREATE TABLE ");
+        sql.append(encodeSchemaTableName(tableName));
+        sql.append(" ( gid serial PRIMARY KEY, ");
+        sql.append(makeNonGeomSqlCreate(attributeType));
+        sql.append(");");
+
+        String sqlStr = sql.toString();
+        getLogger().info(sqlStr);
+        result.add(sqlStr);
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            if (!(anAttributeType instanceof GeometryAttributeType))
+            {
+                continue;
+            }
+            GeometryAttributeType geomAttribute = (GeometryAttributeType) anAttributeType;
+
+            if (shouldDrop)
+            {
+            sqlStr = dropGeometryColumn("", tableName, geomAttribute.getLocalName());
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+            }
+
+            CoordinateReferenceSystem refSys = geomAttribute.getCoordinateSystem();
+            int SRID;
+
+            if (refSys != null)
+            {
+                try
+                {
+                    Set ident = refSys.getIdentifiers();
+                    if ((ident == null || ident.isEmpty()) && refSys == DefaultGeographicCRS.WGS84)
+                    {
+                        SRID = 4326;
+                    } else
+                    {
+                        String code = ((NamedIdentifier) ident.toArray()[0]).getCode();
+                        SRID = Integer.parseInt(code);
+                    }
+                } catch (Exception e)
+                {
+                    getLogger().warn("SRID could not be determined");
+                    SRID = -1;
+                }
+            } else
+            {
+                SRID = -1;
+            }
+
+            sqlStr = addGeometryColumn("", tableName, geomAttribute, SRID);
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+
+
+            String indexName = tableName.replace('-', '_');
+            //also build a spatial index on each geometry column.
+            sql = new StringBuffer("CREATE INDEX spatial_");
+            sql.append(indexName);
+            sql.append("_");
+            sql.append(anAttributeType.getLocalName().toLowerCase());
+            sql.append(" ON ");
+            sql.append(encodeSchemaTableName(tableName));
+            sql.append(" USING GIST (");
+            sql.append(encodeSchemaColumnName(anAttributeType.getLocalName()));
+            sql.append(" gist_geometry_ops);");
+
+            sqlStr = sql.toString();
+            getLogger().info(sqlStr);
+
+            result.add(sqlStr);
+        }
+
+        return result;
+    }
+
+    private boolean tablePresent(String table, Connection conn) throws IOException
+    {
+        final int TABLE_NAME_COL = 3;
+
+        try
+        {
+            conn = getConnection();
+
+            DatabaseMetaData meta = conn.getMetaData();
+            String[] tableType = {"TABLE"};
+            ResultSet tables = meta.getTables(null,
+                    targetDataStore.getDatabaseSchemaName(), "%", tableType);
+
+            while (tables.next())
+            {
+                String tableName = tables.getString(TABLE_NAME_COL);
+
+                if (allowTable(tableName) && (tableName != null)
+                        && (tableName.equalsIgnoreCase(table)))
+                {
+                    tables.close();
+                    return true;
+                }
+            }
+
+            tables.close();
+            return false;
+        } catch (SQLException sqlException)
+        {
+            // JDBCUtils.close(conn, Transaction.AUTO_COMMIT, sqlException);
+            // conn = null;
+
+            String message = "Error querying database for list of tables:"
+                    + sqlException.getMessage();
+            throw new DataSourceException(message, sqlException);
+        } finally
+        {
+            // JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
+        }
+    }
+
+    protected boolean allowTable(String tablename)
+    {
+        if (tablename.equals("geometry_columns"))
+        {
+            return false;
+        } else if (tablename.startsWith("spatial_ref_sys"))
+        {
+            return false;
+        }
+
+        //others?
+        return true;
+    }
+
+
+    private StringBuffer makeNonGeomSqlCreate(AttributeType[] attributeType)
+            throws IOException
+    {
+        StringBuffer buf = new StringBuffer("");
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            String typeName;
+            typeName = CLASS_MAPPINGS.get(anAttributeType.getBinding());
+            if (typeName == null)
+            {
+                typeName = GEOM_CLASS_MAPPINGS.get(anAttributeType.getBinding());
+                if (typeName != null) continue;
+            }
+
+            if (typeName != null)
+            {
+                if (typeName.equals("VARCHAR"))
+                {
+                    int length = -1;
+                    Filter f = anAttributeType.getRestriction();
+                    if (f != null && f != Filter.EXCLUDE && f != Filter.INCLUDE &&
+                            (f instanceof PropertyIsLessThan || f instanceof PropertyIsLessThanOrEqualTo))
+                    {
+                        try
+                        {
+                            BinaryComparisonOperator cf = (BinaryComparisonOperator) f;
+                            if (cf.getExpression1() instanceof LengthFunction)
+                            {
+                                length = Integer.parseInt(((Literal) cf.getExpression2()).getValue().toString());
+                            } else
+                            {
+                                if (cf.getExpression2() instanceof LengthFunction)
+                                {
+                                    length = Integer.parseInt(((Literal) cf.getExpression1()).getValue().toString());
+                                }
+                            }
+                        } catch (NumberFormatException e)
+                        {
+                            length = 256;
+                        }
+                    } else
+                    {
+                        length = 256;
+                    }
+
+                    if (length < 1)
+                    {
+                        getLogger().warn("FeatureType did not specify string length; defaulted to 256");
+                        length = 256;
+                    } else if (length > MAX_ALLOWED_VALUE)
+                    {
+                        length = MAX_ALLOWED_VALUE;
+                    }
+                    typeName = typeName + "(" + length + ")";
+                }
+
+                if (!anAttributeType.isNillable())
+                {
+                    typeName = typeName + " NOT NULL";
+                }
+
+                //TODO review!!! Is toString() always OK???
+                Object defaultValue = anAttributeType.createDefaultValue();
+
+                if (defaultValue != null)
+                {
+                    typeName = typeName + " DEFAULT '"
+                            + defaultValue.toString() + "'";
+                }
+
+                buf.append(" \"").append(anAttributeType.getLocalName()).append("\" ").append(typeName).append(",");
+
+            } else
+            {
+                String msg;
+                if (anAttributeType == null)
+                {
+                    msg = "AttributeType was null!";
+                } else
+                {
+                    msg = "Type '" + anAttributeType.getBinding() + "' not supported!";
+                }
+                throw (new IOException(msg));
+            }
+        }
+
+        return buf.deleteCharAt(buf.length() - 1);
+    }
+
+    private String getGeometrySQLTypeName(Class type)
+    {
+        String res = GEOM_CLASS_MAPPINGS.get(type);
+
+        if (res == null)
+        {
+            throw new RuntimeException("Unknown type name for class " + type
+                    + " please update GEOMETRY_MAPPINGS");
+        }
+
+        return res;
+    }
+
+    protected String getGeometryInsertText(Geometry geom, int srid) // throws IOException
+    {
+        if (geom == null)
+        {
+            return "null";
+        }
+
+        if (targetDataStore.isWKBEnabled())
+        {
+            //String wkb = WKBEncoder.encodeGeometryHex(geom);
+            String wkb = WKBWriter.bytesToHex(new WKBWriter().write(geom));
+
+            if (targetDataStore.isByteaWKB())
+            {
+                return "setSRID('" + wkb + "'::geometry," + srid + ")";
+            } else
+            {
+                return "GeomFromWKB('" + wkb + "', " + srid + ")";
+            }
+        }
+
+        String geoText = geometryWriter.write(geom);
+
+        return "GeometryFromText('" + geoText + "', " + srid + ")";
+    }
+
+    protected String makeInsertSql(Feature feature, int srid) // throws IOException
+    {
+        FeatureType featureType = feature.getFeatureType();
+
+        String tableName = encodeSchemaTableName(featureType.getTypeName());
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+
+        String attrValue;
+
+        StringBuilder statementSQL = new StringBuilder(512);
+        statementSQL.append("INSERT INTO ").append(tableName).append(" (");
+
+        // encode insertion for attributes, but remember to avoid auto-increment ones,
+        // they may be included in the feature type as well
+        for (AttributeType attributeType : attributeTypes)
+        {
+            String attName = attributeType.getLocalName();
+
+            if (feature.getAttribute(attName) != null)
+            {
+                String colName = encodeSchemaColumnName(attName);
+                statementSQL.append(colName).append(",");
+            }
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+        statementSQL.append(" VALUES (");
+
+        Object[] attributes = feature.getAttributes(null);
+
+        for (int i = 0; i < attributeTypes.length; i++)
+        {
+            attrValue = null;
+
+            if (attributeTypes[i] instanceof GeometryAttributeType)
+            {
+                // String geomName = attributeTypes[i].getLocalName();
+                // int srid = ftInfo.getSRID(geomName);
+                Geometry geometry = (Geometry) attributes[i];
+
+                if (geometry == null)
+                {
+                    attrValue = "NULL";
+                } else
+                {
+                    attrValue = getGeometryInsertText(geometry, srid);
+                }
+            } else
+            {
+                if (attributes[i] != null)
+                {
+                    attrValue = addQuotes(attributes[i]);
+                }
+            }
+
+            if (attrValue != null)
+            {
+                statementSQL.append(attrValue).append(",");
+            }
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+
+        return (statementSQL.toString());
+    }
+
+    protected String makePrepareInsertSql(FeatureType featureType)
+    {
+        String tableName = encodeSchemaTableName(featureType.getTypeName());
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+
+        String attrValue;
+
+        StringBuffer statementSQL = new StringBuffer("INSERT INTO " + tableName + " (");
+
+        // encode insertion for attributes, but remember to avoid auto-increment ones,
+        // they may be included in the feature type as well
+        for (AttributeType attributeType : attributeTypes)
+        {
+            String attName = attributeType.getLocalName();
+
+            String colName = encodeSchemaColumnName(attName);
+            statementSQL.append(colName).append(",");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+        statementSQL.append(" VALUES (");
+
+        for (AttributeType attributeType : attributeTypes)
+        {
+            statementSQL.append(" ? ,");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+
+        return (statementSQL.toString());
+    }
+
+    protected String addQuotes(Object value)
+    {
+        String retString;
+
+        if (value != null)
+        {
+            if (value instanceof Number)
+            {
+                retString = value.toString();
+            } else
+            {
+                retString = "'" + doubleQuote(value) + "'";
+            }
+        } else
+        {
+            retString = "null";
+        }
+
+        return retString;
+    }
+
+    String doubleQuote(Object obj)
+    {
+        return obj.toString().replaceAll("'", "''");
+    }
+
+    protected String encodeName(String tableName)
+    {
+        return tableName;
+    }
+
+    protected String encodeColumnName(String colName)
+    {
+        return encodeName(colName);
+    }
+
+    public String encodeSchemaTableName(String tableName) {
+        return schemaEnabled ? ("\"" + targetDataStore.getDatabaseSchemaName() + "\".\"" + tableName + "\"")
+                             : ("\"" + tableName + "\"");
+    }
+
+    public String encodeSchemaColumnName(String columnName) {
+        return "\"" + columnName + "\"";
+    }
+
+    protected void bindFeatureParameters(PreparedStatement pstmt, Feature feature) throws SQLException
+    {
+        FeatureType featureType = feature.getFeatureType();
+
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+        Object[] attributes = feature.getAttributes(null);
+
+        for (int i = 0; i < attributeTypes.length; i++)
+        {
+            if (attributeTypes[i] instanceof GeometryAttributeType)
+            {
+                pstmt.setBytes(i + 1, binaryWriter.writeBinary((Geometry) attributes[i]));
+            } else
+            {
+                if (attributeTypes[i].getBinding().equals(Short.class))
+                {
+                    pstmt.setShort(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Integer.class))
+                {
+                    pstmt.setInt(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Long.class))
+                {
+                    pstmt.setLong(i + 1, (Long) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(String.class))
+                {
+                    pstmt.setString(i + 1, (String) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Float.class))
+                {
+                    pstmt.setFloat(i + 1, (Float) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Double.class))
+                {
+                    pstmt.setDouble(i + 1, (Double) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Boolean.class))
+                {
+                    pstmt.setBoolean(i + 1, (Boolean) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(BigDecimal.class))
+                {
+                    pstmt.setBigDecimal(i + 1, (BigDecimal) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Date.class))
+                {
+                    pstmt.setDate(i + 1, (java.sql.Date) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Time.class))
+                {
+                    pstmt.setTime(i + 1, (java.sql.Time) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Timestamp.class))
+                {
+                    pstmt.setTimestamp(i + 1, (java.sql.Timestamp) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.util.Date.class))
+                {
+                    java.sql.Date sDate = new java.sql.Date(((java.util.Date) attributes[i]).getTime());
+                    pstmt.setDate(i + 1, sDate);
+                }
+            }
+
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/DummyFeatureConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/DummyFeatureConvertPostGISJobContext.java
new file mode 100644
index 0000000..7c48de5
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/DummyFeatureConvertPostGISJobContext.java
@@ -0,0 +1,284 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.ElementDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.TypeCompIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeIdDispatchableFilter;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class DummyFeatureConvertPostGISJobContext extends AbstractDgnToPostGISJobContext
+{
+    static final Log logger = LogFactory.getLog(DummyFeatureConvertPostGISJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public DummyFeatureConvertPostGISJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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();
+                logger.debug("Begin Save PostGIS:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(), Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 PostGIS:" + featureType.getTypeName());
+            }
+            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 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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/FeatureDgnConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/FeatureDgnConvertPostGISJobContext.java
new file mode 100644
index 0000000..2ac1836
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/FeatureDgnConvertPostGISJobContext.java
@@ -0,0 +1,266 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+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.DataStore;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+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.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class FeatureDgnConvertPostGISJobContext extends AbstractDgnToPostGISJobContext
+{
+    static final Log logger = LogFactory.getLog(FeatureDgnConvertPostGISJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    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;
+    private boolean withIndex = false;
+
+    public FeatureDgnConvertPostGISJobContext(String dataPath, DataStore targetDataStore, String filterConfig)
+    {
+        super(dataPath, targetDataStore);
+        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<Feature> arrayList = (ArrayList<Feature>) 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();
+                logger.debug("Begin Save PostGIS:" + featureType.getTypeName());
+
+                FeatureWriter writer;
+                if (featuresWriterContext.containsKey(featureType.getTypeName()))
+                {
+                    writer = featuresWriterContext.get(featureType.getTypeName());
+                } else
+                {
+                    if (!isExistFeature(featureType))
+                    {
+                        targetDataStore.createSchema(featureType);
+                        writer = targetDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        writer = targetDataStore.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 PostGIS:" + featureType.getTypeName());
+            }
+            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 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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java
new file mode 100644
index 0000000..9e0e019
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java
@@ -0,0 +1,613 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+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.postgresql.util.PSQLException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+import com.ximple.eofms.util.DefaultColorTable;
+import com.ximple.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 GeneralDgnConvertPostGISJobContext extends AbstractDgnToPostGISJobContext
+{
+    static final Log logger = LogFactory.getLog(GeneralDgnConvertPostGISJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+
+    private HashMap<FeatureType, ArrayList<Feature>> txFeaturesContext = new HashMap<FeatureType, ArrayList<Feature>>();
+
+    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;
+    private boolean dropTableMode = true;
+
+    private int accumulate = 0;
+
+    public GeneralDgnConvertPostGISJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+        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 = txFeaturesContext.get(feature.getFeatureType());
+            if (feature.getDefaultGeometry() != null && !feature.getDefaultGeometry().isEmpty())
+            {
+                arrayList.add(feature);
+                accumulate++;
+            }
+        } else
+        {
+            logger.info("Unknown Element :" + element.getType() + ", lv=" + element.getLevelIndex());
+        }
+
+        if (accumulate > BATCHSIZE)
+        {
+            commitTransaction();
+        }
+    }
+
+    // private Transaction transaction;
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!txFeaturesContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+
+    private void updateDataStore()
+    {
+        Iterator<FeatureType> it = txFeaturesContext.keySet().iterator();
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = it.next();
+                logger.debug("Begin Save into PostGIS:" + featureType.getTypeName());
+
+                String bindingStmt = makePrepareInsertSql(featureType);
+                ArrayList<Feature> features = txFeaturesContext.get(featureType);
+                Connection conn = getConnection();
+                boolean autoCommit = conn.getAutoCommit();
+                conn.setAutoCommit(true);
+                PreparedStatement pstmt = conn.prepareStatement(bindingStmt);
+
+                for (Feature feature : features)
+                {
+                    // currentStmt = feature;
+                    // Statement stmt = conn.createStatement();
+                    try
+                    {
+                        // stmt.execute(feature);
+                        bindFeatureParameters(pstmt, feature);
+                        pstmt.execute();
+                    } catch (PSQLException e)
+                    {
+                        if (bindingStmt != null)
+                        {
+                            logger.error("Execute:" + bindingStmt);
+                        }
+                        logger.error(e.getServerErrorMessage());
+                        logger.error(e.getMessage(), e);
+                    }
+                }
+
+                pstmt.close();
+                features.clear();
+                conn.setAutoCommit(autoCommit);
+                logger.debug("End Save into PostGIS:" + featureType.getTypeName());
+            }
+            accumulate = 0;
+        } catch (SQLException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public void closeFeatureWriter() throws IOException
+    {
+        txFeaturesContext.clear();
+        /*
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+        */
+    }
+
+    public FeatureType createPointFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalPointFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createPolygonFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalPolygonFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createMultiLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalMultiLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createArcFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalArcFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createEllipseFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalEllipseFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(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();
+            content = content.replace('\u0000', ' ');
+            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");
+                String content = text.replace('\u0000', ' ');
+                sb.append(content);
+            }
+
+            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";
+            typeName = typeName.toLowerCase();
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createPointFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof TextNodeElement)
+        {
+            typeName = getFeatureBaseName() + "_P";
+            typeName = typeName.toLowerCase();
+            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";
+                typeName = typeName.toLowerCase();
+                if (!featureTypes.containsKey(typeName))
+                {
+                    featureTypes.put(typeName, createPolygonFeatureElement(typeName));
+                }
+                return featureTypes.get(typeName);
+            } else
+            {
+                typeName = getFeatureBaseName() + "_L";
+                typeName = typeName.toLowerCase();
+                if (!featureTypes.containsKey(typeName))
+                {
+                    featureTypes.put(typeName, createLineFeatureElement(typeName));
+                }
+                return featureTypes.get(typeName);
+            }
+        } else if (element instanceof LineElement)
+        {
+            typeName = getFeatureBaseName() + "_L";
+            typeName = typeName.toLowerCase();
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createLineFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof ComplexChainElement)
+        {
+            typeName = getFeatureBaseName() + "_ML";
+            typeName = typeName.toLowerCase();
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createMultiLineFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof ArcElement)
+        {
+            typeName = getFeatureBaseName() + "_A";
+            typeName = typeName.toLowerCase();
+            if (!featureTypes.containsKey(typeName))
+            {
+                featureTypes.put(typeName, createArcFeatureElement(typeName));
+            }
+            return featureTypes.get(typeName);
+        } else if (element instanceof EllipseElement)
+        {
+            typeName = getFeatureBaseName() + "_R";
+            typeName = typeName.toLowerCase();
+            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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    public boolean isDropTableMode()
+    {
+        return dropTableMode;
+    }
+
+    public void setDropTableMode(boolean dropTableMode)
+    {
+        this.dropTableMode = dropTableMode;
+    }
+
+    protected void clearFeatureData(FeatureTypeBuilder typeBuilder) throws SchemaException
+    {
+        String featureName = typeBuilder.getName();
+        if (isExistFeature(typeBuilder.getFeatureType()))
+        {
+            try
+            {
+                Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                if (dropTableMode)
+                {
+                    dropGeometryColumn(conn, featureName,
+                            typeBuilder.getFeatureType().getDefaultGeometry().getLocalName());
+                    dropTable(conn, featureName);
+
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilder.getFeatureType());
+                    for (String stmtText : schemaTexts)
+                    {
+                        Statement stmt = conn.createStatement();
+                        stmt.execute(stmtText);
+                        stmt.close();
+                    }
+                } else {
+                    deleteTable(conn, featureName);
+                }
+                conn.close();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+            } catch (SQLException e)
+            {
+                logger.warn(e.getMessage(), e);
+            }
+        } else
+        {
+            try
+            {
+                Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilder.getFeatureType());
+                for (String stmtText : schemaTexts)
+                {
+                    Statement stmt = conn.createStatement();
+                    stmt.execute(stmtText);
+                    stmt.close();
+                }
+                conn.close();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+            } catch (SQLException e)
+            {
+                logger.warn(e.getMessage(), e);
+            }
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java
new file mode 100644
index 0000000..523d151
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java
@@ -0,0 +1,468 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+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.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+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.postgresql.util.PSQLException;
+
+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.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TPCLIDConverter;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 IndexDgnConvertPostGISJobContext extends AbstractDgnToPostGISJobContext
+{
+    static final Log logger = LogFactory.getLog(IndexDgnConvertPostGISJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+
+    private HashMap<FeatureType, ArrayList<Feature>> txFeaturesContext = new HashMap<FeatureType, ArrayList<Feature>>();
+
+    private FeatureTypeBuilder typeBuilderPnt = null;
+    private FeatureTypeBuilder typeBuilderRect = null;
+    private FeatureType featureType = null;
+    private FeatureType featureType2 = null;
+
+    private boolean dropTableMode = true;
+    private int accumulate = 0;
+
+    public IndexDgnConvertPostGISJobContext(String dataPath, DataStore targetDataStore)
+    {
+        super(dataPath, targetDataStore);
+    }
+
+    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<Feature>());
+        }
+        ArrayList<Feature> 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<Feature>());
+        }
+        arrayList = txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+        accumulate++;
+
+        if (accumulate > BATCHSIZE)
+        {
+            commitTransaction();
+        }
+    }
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!txFeaturesContext.isEmpty())
+        {
+            updateDataStore();
+        }
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+
+    private void updateDataStore()
+    {
+        Iterator<FeatureType> it = txFeaturesContext.keySet().iterator();
+        try
+        {
+            while (it.hasNext())
+            {
+                FeatureType featureType = it.next();
+                logger.debug("Begin Save PostGIS:" + featureType.getTypeName());
+
+                String bindingStmt = makePrepareInsertSql(featureType);
+                ArrayList<Feature> features = txFeaturesContext.get(featureType);
+                Connection conn = getConnection();
+                boolean autoCommit = conn.getAutoCommit();
+                conn.setAutoCommit(true);
+                PreparedStatement pstmt = conn.prepareStatement(bindingStmt);
+
+                for (Feature feature : features)
+                {
+                    // currentStmt = feature;
+                    // Statement stmt = conn.createStatement();
+                    try
+                    {
+                        // stmt.execute(feature);
+                        bindFeatureParameters(pstmt, feature);
+                        pstmt.execute();
+                    } catch (PSQLException e)
+                    {
+                        if (bindingStmt != null)
+                        {
+                            logger.error("Execute:" + bindingStmt);
+                        }
+                        logger.error(e.getServerErrorMessage());
+                        logger.error(e.getMessage(), e);
+                        /*
+                    } finally {
+                        stmt.close();
+                        */
+                    }
+                }
+                /*
+                if ((i % BATCHSIZE) != 0)
+                {
+                    stmt.executeBatch();
+                }
+                stmt.close();
+                */
+
+                pstmt.close();
+                features.clear();
+
+                conn.setAutoCommit(autoCommit);
+                logger.debug("End Save PostGIS:" + featureType.getTypeName());
+            }
+            accumulate = 0;
+        } catch (PSQLException e)
+        {
+            logger.error(e.getServerErrorMessage());
+            logger.error(e.getMessage(), e);
+        } catch (SQLException e)
+        {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    public void closeFeatureWriter() throws IOException
+    {
+        txFeaturesContext.clear();
+        /*
+        for (FeatureWriter featureWriter : this.featuresWriterContext.values())
+        {
+            featureWriter.close();
+        }
+
+        this.featuresWriterContext.clear();
+        */
+    }
+
+    public FeatureType createFeatureElement(String featureName) throws SchemaException
+    {
+        if (typeBuilderRect == null)
+        {
+            typeBuilderRect = FeatureTypeBuilderUtil.createNormalIndexFeatureTypeBuilder(featureName);
+            if (isExistFeature(typeBuilderRect.getFeatureType()))
+            {
+                try
+                {
+                    Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                    if (dropTableMode)
+                    {
+                        try
+                        {
+                            dropGeometryColumn(conn, featureName,
+                                    typeBuilderRect.getFeatureType().getDefaultGeometry().getLocalName());
+                        } catch (PSQLException e)
+                        {
+                            logger.debug(e.getMessage(), e);
+                        }
+                        try
+                        {
+                            dropTable(conn, featureName);
+                        } catch (PSQLException e)
+                        {
+                            logger.debug(e.getMessage(), e);
+                        }
+                        ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderRect.getFeatureType());
+                        for (String stmtText : schemaTexts)
+                        {
+                            Statement stmt = conn.createStatement();
+                            stmt.execute(stmtText);
+                            stmt.close();
+                        }
+                    } else
+                    {
+                        deleteTable(conn, featureName);
+                    }
+                    conn.close();
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                } catch (SQLException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                }
+            } else
+            {
+                try
+                {
+                    Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderRect.getFeatureType());
+                    for (String stmtText : schemaTexts)
+                    {
+                        Statement stmt = conn.createStatement();
+                        stmt.execute(stmtText);
+                        stmt.close();
+                    }
+                    conn.close();
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                } catch (SQLException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                }
+            }
+        }
+        return typeBuilderRect.getFeatureType();
+    }
+
+    public FeatureType createFeatureElement2(String featureName) throws SchemaException
+    {
+        if (typeBuilderPnt == null)
+        {
+            typeBuilderPnt = FeatureTypeBuilderUtil.createNormalIndexTextFeatureTypeBuilder(featureName);
+            if (isExistFeature(typeBuilderPnt.getFeatureType()))
+            {
+                try
+                {
+                    Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                    if (dropTableMode)
+                    {
+                        dropGeometryColumn(conn, featureName,
+                                typeBuilderPnt.getFeatureType().getDefaultGeometry().getLocalName());
+                        dropTable(conn, featureName);
+
+                        ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderPnt.getFeatureType());
+                        for (String stmtText : schemaTexts)
+                        {
+                            Statement stmt = conn.createStatement();
+                            stmt.execute(stmtText);
+                            stmt.close();
+                        }
+                    } else
+                    {
+                        deleteTable(conn, featureName);
+                    }
+                    conn.close();
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                } catch (SQLException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                }
+            } else
+            {
+                try
+                {
+                    Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderPnt.getFeatureType());
+                    for (String stmtText : schemaTexts)
+                    {
+                        Statement stmt = conn.createStatement();
+                        stmt.execute(stmtText);
+                        stmt.close();
+                    }
+                    conn.close();
+                } catch (IOException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                } catch (SQLException e)
+                {
+                    logger.warn(e.getMessage(), e);
+                }
+            }
+        }
+        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.createPolygon(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())),
+                    }), null);
+
+            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)
+        {
+            Feature feature = null;
+            TextElement txtElement = (TextElement) element;
+            double angle = txtElement.getRotationAngle();
+            angle = BigDecimal.valueOf(angle).setScale(3, RoundingMode.HALF_UP).doubleValue();
+            convertDecorator.setConverter(txtElement);
+            Geometry gobj = convertDecorator.toGeometry(geometryFactory);
+            if (gobj != null)
+            feature = featureType.create(new Object[]{
+                    gobj,
+                    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.toLowerCase());
+        }
+        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.toLowerCase());
+        }
+        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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+
+    public boolean isDropTableMode()
+    {
+        return dropTableMode;
+    }
+
+    public void setDropTableMode(boolean dropTableMode)
+    {
+        this.dropTableMode = dropTableMode;
+    }
+
+    public void clearOutputDatabase()
+    {
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/OracleConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/OracleConvertPostGISJobContext.java
new file mode 100644
index 0000000..348d2b3
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/OracleConvertPostGISJobContext.java
@@ -0,0 +1,390 @@
+package com.ximple.eofms.jobs.context.postgis;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+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.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.DataStore;
+import org.geotools.data.Transaction;
+import org.geotools.data.postgis.PostgisDataStoreFactory;
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.SchemaException;
+import org.postgresql.util.PSQLException;
+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.CreateFeatureTypeEventListener;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.FeatureTypeEvent;
+import com.ximple.eofms.jobs.OracleElementLogger;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+public class OracleConvertPostGISJobContext extends AbstractOracleToPostGISJobContext
+        implements CreateFeatureTypeEventListener
+{
+    static Log logger = LogFactory.getLog(OracleConvertPostGISJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    static PostgisDataStoreFactory dataStoreFactory = new PostgisDataStoreFactory();
+
+    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<FeatureType, ArrayList<Feature>> txFeaturesContext = new HashMap<FeatureType, ArrayList<Feature>>();
+
+    private JobExecutionContext executionContext;
+
+    private String currentSchema = null;
+    private boolean schemaChanged = false;
+    private boolean dropTableMode = true;
+    private int accumulate = 0;
+
+    public OracleConvertPostGISJobContext(String dataPath, DataStore pgDS, String filterConfig)
+    {
+        super(dataPath, pgDS);
+        _filterConfig = filterConfig;
+        elementDispatcher = createElementDispatcher();
+        elementDispatcher.addCreateFeatureTypeEventListener(this);
+        // 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<Feature>());
+        }
+        ArrayList<Feature> arrayList = txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
+        accumulate++;
+        if (accumulate > BATCHSIZE)
+        {
+            commitTransaction();
+        }
+    }
+
+    public void startTransaction()
+    {
+    }
+
+    public void commitTransaction()
+    {
+        if (!txFeaturesContext.isEmpty())
+        {
+            logger.debug("Transaction size = " + txFeaturesContext.size());
+            //txFeaturesContext.commitTransaction();
+        } else
+        {
+            logger.debug("Transaction is empty.");
+        }
+
+        if (!txFeaturesContext.isEmpty())
+        {
+            updateDataStore();
+        }
+
+        if (this.getElementLogger() != null)
+            this.getElementLogger().flashLogging();
+    }
+
+    public void rollbackTransaction()
+    {
+    }
+
+    public void resetFeatureContext()
+    {
+        txFeaturesContext.clear();
+    }
+
+    private void updateDataStore()
+    {
+        Iterator<FeatureType> it = txFeaturesContext.keySet().iterator();
+        try
+        {
+            Connection conn = getConnection();
+            boolean autoCommit = conn.getAutoCommit();
+            conn.setAutoCommit(true);
+            while (it.hasNext())
+            {
+                FeatureType featureType = it.next();
+                logger.debug("Begin Save into PostGIS:" + featureType.getTypeName());
+
+                String bindingStmt = makePrepareInsertSql(featureType);
+                ArrayList<Feature> features = txFeaturesContext.get(featureType);
+                PreparedStatement pstmt = conn.prepareStatement(bindingStmt);
+
+                for (Feature feature : features)
+                {
+                    try
+                    {
+                        // stmt.execute(feature);
+                        bindFeatureParameters(pstmt, feature);
+                        pstmt.execute();
+                    } catch (PSQLException e)
+                    {
+                        if (bindingStmt != null)
+                        {
+                            logger.error("Execute:" + bindingStmt);
+                        }
+                        logger.error(e.getServerErrorMessage());
+                        logger.error(e.getMessage(), e);
+                    } catch (ClassCastException e)
+                    {
+                        if (bindingStmt != null)
+                        {
+                            logger.error("Execute:" + bindingStmt);
+                        }
+                        for (int i = 0; i < feature.getNumberOfAttributes(); i++)
+                        {
+                            logger.info("attr[" + i + "]-" + ((feature.getAttribute(i) == null) ? " NULL" :
+                                    feature.getAttribute(i).toString()));
+                        }
+                        logger.error(e.getMessage(), e);
+                    }
+                }
+
+                pstmt.close();
+                features.clear();
+                logger.debug("End Save into PostGIS:" + featureType.getTypeName());
+            }
+            conn.setAutoCommit(autoCommit);
+            accumulate = 0;
+        } catch (SQLException 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
+    {
+    }
+
+    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;
+    }
+
+    protected Log getLogger()
+    {
+        return logger;
+    }
+
+    public boolean isDropTableMode()
+    {
+        return dropTableMode;
+    }
+
+    public void setDropTableMode(boolean dropTableMode)
+    {
+        this.dropTableMode = dropTableMode;
+    }
+
+    public void createFeatureTypeOccurred(FeatureTypeEvent evt)
+    {
+        try
+        {
+            clearFeatureData(evt.getFeatureType());
+        } catch (SchemaException e)
+        {
+            logger.warn(e.getMessage(), e);
+        }
+    }
+
+    protected void clearFeatureData(FeatureType featureType) throws SchemaException
+    {
+        String featureName = featureType.getTypeName();
+        if (isExistFeature(featureType))
+        {
+            try
+            {
+                Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                if (dropTableMode)
+                {
+                    dropGeometryColumn(conn, featureName,
+                            featureType.getDefaultGeometry().getLocalName());
+                    dropTable(conn, featureName);
+
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(featureType);
+                    for (String stmtText : schemaTexts)
+                    {
+                        Statement stmt = conn.createStatement();
+                        stmt.execute(stmtText);
+                        stmt.close();
+                    }
+                } else
+                {
+                    deleteTable(conn, featureName);
+                }
+                conn.close();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+            } catch (SQLException e)
+            {
+                logger.warn(e.getMessage(), e);
+            }
+        } else
+        {
+            try
+            {
+                Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
+                ArrayList<String> schemaTexts = createNewSchemaTexts(featureType);
+                for (String stmtText : schemaTexts)
+                {
+                    Statement stmt = conn.createStatement();
+                    stmt.execute(stmtText);
+                    stmt.close();
+                }
+                conn.close();
+            } catch (IOException e)
+            {
+                logger.warn(e.getMessage(), e);
+            } catch (SQLException e)
+            {
+                logger.warn(e.getMessage(), e);
+            }
+        }
+    }
+
+    public boolean isSchemaChanged()
+    {
+        return schemaChanged;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/AbstractDgnToShapefileJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/AbstractDgnToShapefileJobContext.java
new file mode 100644
index 0000000..395adc2
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/AbstractDgnToShapefileJobContext.java
@@ -0,0 +1,13 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+
+public abstract class AbstractDgnToShapefileJobContext extends AbstractDgnFileJobContext
+{
+    public AbstractDgnToShapefileJobContext(String dataPath)
+    {
+        super(dataPath);
+    }
+
+    public abstract String getDataOutPath();
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/DummyFeatureConvertShpJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/DummyFeatureConvertShpJobContext.java
new file mode 100644
index 0000000..fda158f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/DummyFeatureConvertShpJobContext.java
@@ -0,0 +1,320 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+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.ElementDispatchableFilter;
+import com.ximple.eofms.filter.ElementDispatcher;
+import com.ximple.eofms.filter.TypeCompIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeCompLevelIdDispatchableFilter;
+import com.ximple.eofms.filter.TypeIdDispatchableFilter;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class DummyFeatureConvertShpJobContext extends AbstractDgnToShapefileJobContext
+{
+    static final Log logger = LogFactory.getLog(DummyFeatureConvertShpJobContext.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;
+    private boolean withIndex = false;
+
+    public DummyFeatureConvertShpJobContext(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 = null;
+                    boolean existFile = sfile.exists();
+
+                    if (!withIndex)
+                    {
+                        shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                                true, Charset.forName("UTF-8"));
+                    } else
+                    {
+                        shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                    }
+
+                    if (!existFile)
+                    {
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/FeatureDgnConvertShpJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/FeatureDgnConvertShpJobContext.java
new file mode 100644
index 0000000..638c328
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/FeatureDgnConvertShpJobContext.java
@@ -0,0 +1,303 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+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.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+import com.ximple.io.dgn7.UserAttributeData;
+
+public class FeatureDgnConvertShpJobContext extends AbstractDgnToShapefileJobContext
+{
+    static final Log logger = LogFactory.getLog(FeatureDgnConvertShpJobContext.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;
+    private boolean withIndex = false;
+
+    public FeatureDgnConvertShpJobContext(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 = null;
+                    boolean existFile = sfile.exists();
+
+                    if (!withIndex)
+                    {
+                        shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                                true, Charset.forName("UTF-8"));
+                    } else
+                    {
+                        shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                    }
+
+                    if (!existFile)
+                    {
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
+
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/GeneralDgnConvertShpJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/GeneralDgnConvertShpJobContext.java
new file mode 100644
index 0000000..0da1bc0
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/GeneralDgnConvertShpJobContext.java
@@ -0,0 +1,556 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+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.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.eofms.util.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 GeneralDgnConvertShpJobContext extends AbstractDgnToShapefileJobContext
+{
+    static final Log logger = LogFactory.getLog(GeneralDgnConvertShpJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    public 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;
+    private boolean withIndex = false;
+
+    public GeneralDgnConvertShpJobContext(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 = null;
+                    boolean existFile = sfile.exists();
+
+                    if (!withIndex)
+                    {
+                        shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                                true, Charset.forName("UTF-8"));
+                    } else
+                    {
+                        shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                    }
+
+                    if (!existFile)
+                    {
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        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 = FeatureTypeBuilderUtil.createNormalPointFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createArcFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalArcFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createEllipseFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalEllipseFeatureTypeBuilder(featureName);
+            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;
+    }
+
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/IndexDgnConvertShpJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/IndexDgnConvertShpJobContext.java
new file mode 100644
index 0000000..c1806cf
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/IndexDgnConvertShpJobContext.java
@@ -0,0 +1,343 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+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.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.FeatureTypeBuilderUtil;
+import com.ximple.eofms.util.TPCLIDConverter;
+import com.ximple.eofms.util.TWD97GeometryConverterDecorator;
+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 IndexDgnConvertShpJobContext extends AbstractDgnToShapefileJobContext
+{
+    static final Log logger = LogFactory.getLog(IndexDgnConvertShpJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+    static final GeometryFactory geometryFactory = new GeometryFactory();
+    TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
+    public 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 IndexDgnConvertShpJobContext(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 = FeatureTypeBuilderUtil.createNormalIndexFeatureTypeBuilder(featureName);
+        }
+        return typeBuilderRect.getFeatureType();
+    }
+
+    public FeatureType createFeatureElement2(String featureName) throws SchemaException
+    {
+        if (typeBuilderPnt == null)
+        {
+            typeBuilderPnt = FeatureTypeBuilderUtil.createNormalIndexTextFeatureTypeBuilder(featureName);
+        }
+        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;
+    }
+
+    public Log getLogger()
+    {
+        return logger;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/OracleConvertShapefilesJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/OracleConvertShapefilesJobContext.java
new file mode 100644
index 0000000..697ed62
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/shapefile/OracleConvertShapefilesJobContext.java
@@ -0,0 +1,371 @@
+package com.ximple.eofms.jobs.context.shapefile;
+
+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.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.xmlrules.DigesterLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.memory.PessimisticMapWrapper;
+import org.apache.commons.transaction.util.CommonsLoggingLogger;
+import org.apache.commons.transaction.util.LoggerFacade;
+import org.geotools.data.FeatureWriter;
+import org.geotools.data.Transaction;
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.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.eofms.jobs.OracleElementLogger;
+import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+import com.ximple.io.dgn7.ComplexElement;
+import com.ximple.io.dgn7.Element;
+import com.ximple.io.dgn7.FrammeAttributeData;
+
+
+public class OracleConvertShapefilesJobContext extends AbstractOracleJobContext
+{
+    static Log logger = LogFactory.getLog(OracleConvertShapefilesJobContext.class);
+    static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
+
+    public 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 _convertElementIn = null;
+    private String currentSchema = null;
+    private boolean schemaChanged = false;
+    private boolean withIndex = false;
+
+    public OracleConvertShapefilesJobContext(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 = null;
+                    boolean existFile = sfile.exists();
+
+                    if (!withIndex)
+                    {
+                        shapefileDataStore = new ShapefileDataStore(sfile.toURI().toURL(),
+                                true, Charset.forName("UTF-8"));
+                    } else
+                    {
+                        shapefileDataStore = new IndexedShapefileDataStore(sfile.toURI().toURL(),
+                                null, true, true, IndexedShapefileDataStore.TREE_QIX, Charset.forName("UTF-8"));
+                    }
+
+                    if (!existFile)
+                    {
+                        shapefileDataStore.createSchema(featureType);
+                        writer = shapefileDataStore.getFeatureWriter(featureType.getTypeName(),
+                                Transaction.AUTO_COMMIT);
+                    } else
+                    {
+                        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;
+    }
+
+    public boolean isWithIndex()
+    {
+        return withIndex;
+    }
+
+    public void setWithIndex(boolean withIndex)
+    {
+        this.withIndex = withIndex;
+    }
+
+    protected Log getLogger()
+    {
+        return logger;
+    }
+}
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..a93c23e
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/ColorTableMapping.java
@@ -0,0 +1,15 @@
+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..36c1a5f
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/DefaultColorTable.java
@@ -0,0 +1,368 @@
+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/FeatureTypeBuilderUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java
new file mode 100644
index 0000000..4fb6712
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java
@@ -0,0 +1,237 @@
+package com.ximple.eofms.util;
+
+import org.geotools.feature.AttributeTypeFactory;
+import org.geotools.feature.FeatureTypeBuilder;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiLineString;
+
+public final class FeatureTypeBuilderUtil
+{
+    protected static GeometryFactory _geomFactory = new GeometryFactory();
+
+    public static FeatureTypeBuilder createNormalPointFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("font", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("just", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("height", Float.class, false, 1, (float) 1.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("width", Float.class, false, 1, (float) 1.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("angle", Float.class, false, 1, (float) 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("context", String.class, false, 254, ""));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", LineString.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalMultiLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", MultiLineString.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalPolygonFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalArcFeatureTypeBuilder(String featureName)
+    {
+        return createNormalLineFeatureTypeBuilder(featureName);
+        /*
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+        */
+    }
+
+    public static FeatureTypeBuilder createNormalEllipseFeatureTypeBuilder(String featureName)
+    {
+        return createNormalPolygonFeatureTypeBuilder(featureName);
+        /*
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+        */
+    }
+
+    public static FeatureTypeBuilder createNormalIndexFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("x1", Double.class, false, 1, 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("y1", Double.class, false, 1, 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("x2", Double.class, false, 1, 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("y2", Double.class, false, 1, 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tpclid", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalIndexTextFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("just", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("height", Float.class, false, 1, (float) 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("width", Float.class, false, 1, (float) 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("angle", Float.class, false, 1, (float) 0.0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tpclid", String.class, false, 12, ""));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createPointFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("just", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("height", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("width", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("angle", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("context", String.class, true, 254));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createSymbolFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("just", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("height", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("width", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("angle", Float.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symbol", String.class, false, 20));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createPolygonFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", LineString.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createMultiLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", MultiLineString.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createEllipseFeatureTypeBuilder(String featureName)
+    {
+        return createPolygonFeatureTypeBuilder(featureName);
+        /*
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+        */
+    }
+
+    public static FeatureTypeBuilder createArcFeatureTypeBuilder(String featureName)
+    {
+        return createLineFeatureTypeBuilder(featureName);
+        /*
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+        */
+    }
+}
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..8cd89e4
--- /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..8aeac8e
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/PrintfFormat.java
@@ -0,0 +1,4830 @@
+//
+//(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>
+ * <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>
+ * <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>
+ * <p/>
+ * <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/>
+ * <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.
+ * <p/>
+ * <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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+     * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * @throws 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * The precision, if set, is the minimum number of
+         * digits to appear after the radix character.
+         * Padding is with trailing 0s.
+         * <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 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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'.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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.
+         * <p/>
+         * 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..5821070
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/StringUtils.java
@@ -0,0 +1,2885 @@
+package com.ximple.eofms.util;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.net.URLEncoder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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..a4989d8
--- /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/TWD97GeometryConverterDecorator.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWD97GeometryConverterDecorator.java
new file mode 100644
index 0000000..0cb12d5
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWD97GeometryConverterDecorator.java
@@ -0,0 +1,68 @@
+package com.ximple.eofms.util;
+
+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.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/TWDDatumConverter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
new file mode 100644
index 0000000..0971069
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java
@@ -0,0 +1,509 @@
+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/java/com/ximple/eofms/util/postjts/JTSShape.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java
new file mode 100644
index 0000000..ebbcd74
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java
@@ -0,0 +1,339 @@
+package com.ximple.eofms.util.postjts;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence;
+
+public class JTSShape implements Shape
+{
+    static GeometryFactory fac = new GeometryFactory();
+
+    Geometry geom;
+
+    final static LinearRing[] NOSHELLS = {};
+
+    public JTSShape(Geometry _geom)
+    {
+        this.geom = _geom;
+    }
+
+    public JTSShape(JtsGeometry _geom)
+    {
+        this(_geom.getGeometry());
+    }
+
+    public boolean contains(Point2D p)
+    {
+        return contains(p.getX(), p.getY());
+    }
+
+    public boolean contains(double x, double y)
+    {
+        Coordinate c = new Coordinate(x, y);
+        Point p = fac.createPoint(c);
+        return geom.contains(p);
+    }
+
+    public boolean contains(Rectangle2D r)
+    {
+        return contains(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+    }
+
+    public boolean contains(double x, double y, double w, double h)
+    {
+        Polygon p = createRect(x, y, w, h);
+        return geom.contains(p);
+    }
+
+    protected Polygon createRect(double x, double y, double w, double h)
+    {
+        double[] arr = {x, y, x + w, y, x + w, y + h, x, y + h, x, y};
+        PackedCoordinateSequence shell = new PackedCoordinateSequence.Double(arr, 2);
+        Polygon p = fac.createPolygon(fac.createLinearRing(shell), NOSHELLS);
+        return p;
+    }
+
+    public Rectangle2D getBounds2D()
+    {
+        Envelope env = geom.getEnvelopeInternal();
+        return new Rectangle2D.Double(env.getMinX(), env.getMaxX(), env.getWidth(), env.getHeight());
+    }
+
+    public Rectangle getBounds()
+    {
+        // We deal simple code for efficiency here, the getBounds() rounding
+        // rules are ugly...
+        return getBounds2D().getBounds();
+    }
+
+    public PathIterator getPathIterator(AffineTransform at)
+    {
+        return getPathIterator(geom, at);
+    }
+
+    public PathIterator getPathIterator(AffineTransform at, double flatness)
+    {
+        // we don't have much work here, as we only have linear segments, no
+        // "flattening" necessary.
+        return getPathIterator(at);
+    }
+
+    public boolean intersects(Rectangle2D r)
+    {
+        return intersects(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+    }
+
+    public boolean intersects(double x, double y, double w, double h)
+    {
+        Polygon p = createRect(x, y, w, h);
+        return geom.intersects(p);
+    }
+
+    public static GeometryPathIterator getPathIterator(Geometry geometry, AffineTransform _at)
+    {
+        if (geometry instanceof Point)
+        {
+            return new PointPathIterator((Point) geometry, _at);
+        } else if (geometry instanceof LineString)
+        {
+            return new LineStringPathIterator((LineString) geometry, _at);
+        } else if (geometry instanceof Polygon)
+        {
+            return new PolygonPathIterator((Polygon) geometry, _at);
+        } else
+        {
+            return new GeometryCollectionPathIterator((GeometryCollection) geometry, _at);
+        }
+    }
+
+    public static abstract class GeometryPathIterator implements PathIterator
+    {
+
+        protected final AffineTransform at;
+        protected int index = 0;
+
+        GeometryPathIterator(AffineTransform _at)
+        {
+            this.at = _at;
+        }
+
+        public final int getWindingRule()
+        {
+            return PathIterator.WIND_EVEN_ODD;
+        }
+
+        public void next()
+        {
+            index++;
+        }
+    }
+
+    public static class PointPathIterator extends GeometryPathIterator
+    {
+        final Point p;
+
+        public PointPathIterator(Point _p, AffineTransform _at)
+        {
+            super(_at);
+            p = _p;
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            switch (index)
+            {
+            case 0:
+                coords[0] = (float) p.getX();
+                coords[1] = (float) p.getY();
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            case 1:
+                return PathIterator.SEG_CLOSE;
+            default:
+                throw new IllegalStateException();
+            }
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            switch (index)
+            {
+            case 0:
+                coords[0] = p.getX();
+                coords[1] = p.getY();
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            case 1:
+                return PathIterator.SEG_CLOSE;
+            default:
+                throw new IllegalStateException();
+            }
+        }
+
+        public boolean isDone()
+        {
+            return index > 1;
+        }
+    }
+
+    public static class LineStringPathIterator extends GeometryPathIterator
+    {
+        CoordinateSequence cs;
+
+        final boolean isRing;
+
+        public LineStringPathIterator(LineString ls, AffineTransform _at)
+        {
+            super(_at);
+            cs = ls.getCoordinateSequence();
+            isRing = ls instanceof LinearRing;
+        }
+
+        /**
+         * only to be called from PolygonPathIterator subclass
+         */
+        protected void reInit(CoordinateSequence _cs)
+        {
+            cs = _cs;
+            index = 0;
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            if (index == 0)
+            {
+                coords[0] = (float) cs.getOrdinate(index, 0);
+                coords[1] = (float) cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            } else if (index < cs.size())
+            {
+                coords[0] = (float) cs.getOrdinate(index, 0);
+                coords[1] = (float) cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_LINETO;
+            } else if (isRing && index == cs.size())
+            {
+                return PathIterator.SEG_CLOSE;
+            } else
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            if (index == 0)
+            {
+                coords[0] = cs.getOrdinate(index, 0);
+                coords[1] = cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            } else if (index < cs.size())
+            {
+                coords[0] = cs.getOrdinate(index, 0);
+                coords[1] = cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_LINETO;
+            } else if (isRing && index == cs.size())
+            {
+                return PathIterator.SEG_CLOSE;
+            } else
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        public boolean isDone()
+        {
+            return isRing ? index > cs.size() : index >= cs.size();
+        }
+    }
+
+    public static class PolygonPathIterator extends LineStringPathIterator
+    {
+        final Polygon pg;
+        int outerindex = -1;
+
+        public PolygonPathIterator(Polygon _pg, AffineTransform _at)
+        {
+            super(_pg.getExteriorRing(), _at);
+            pg = _pg;
+            index = -1;
+        }
+
+        public boolean isDone()
+        {
+            return outerindex >= pg.getNumInteriorRing();
+        }
+
+        public void next()
+        {
+            super.next();
+            if (super.isDone())
+            {
+                outerindex++;
+                if (outerindex < pg.getNumInteriorRing())
+                {
+                    super.reInit(pg.getInteriorRingN(outerindex).getCoordinateSequence());
+                }
+            }
+        }
+    }
+
+    public static class GeometryCollectionPathIterator extends GeometryPathIterator
+    {
+        final GeometryCollection coll;
+        GeometryPathIterator current;
+
+        public GeometryCollectionPathIterator(GeometryCollection _coll, AffineTransform _at)
+        {
+            super(_at);
+            coll = _coll;
+            current = getPathIterator(coll.getGeometryN(index), _at);
+        }
+
+        public boolean isDone()
+        {
+            return index > coll.getNumGeometries();
+        }
+
+        public void next()
+        {
+            current.next();
+            if (current.isDone())
+            {
+                index++;
+                if (index < coll.getNumGeometries())
+                {
+                    current = getPathIterator(coll.getGeometryN(index), at);
+                }
+            }
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            return current.currentSegment(coords);
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            return current.currentSegment(coords);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java
new file mode 100644
index 0000000..1ee359a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java
@@ -0,0 +1,287 @@
+/*
+ * JtsBinaryParser.java
+ *
+ * Binary Parser for JTS - relies on org.postgis V1.0.0+ package.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+package com.ximple.eofms.util.postjts;
+
+import org.postgis.binary.ByteGetter;
+import org.postgis.binary.ByteGetter.BinaryByteGetter;
+import org.postgis.binary.ByteGetter.StringByteGetter;
+import org.postgis.binary.ValueGetter;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence;
+
+/**
+ * Parse binary representation of geometries. Currently, only text rep (hexed)
+ * implementation is tested.
+ * <p/>
+ * It should be easy to add char[] and CharSequence ByteGetter instances,
+ * although the latter one is not compatible with older jdks.
+ * <p/>
+ * I did not implement real unsigned 32-bit integers or emulate them with long,
+ * as both java Arrays and Strings currently can have only 2^31-1 elements
+ * (bytes), so we cannot even get or build Geometries with more than approx.
+ * 2^28 coordinates (8 bytes each).
+ *
+ * @author Markus Schaber, markus.schaber@logix-tt.com
+ */
+public class JtsBinaryParser
+{
+
+    /**
+     * Get the appropriate ValueGetter for my endianness
+     *
+     * @param bytes The appropriate Byte Getter
+     * @return the ValueGetter
+     */
+    public static ValueGetter valueGetterForEndian(ByteGetter bytes)
+    {
+        if (bytes.get(0) == ValueGetter.XDR.NUMBER)
+        { // XDR
+            return new ValueGetter.XDR(bytes);
+        } else if (bytes.get(0) == ValueGetter.NDR.NUMBER)
+        {
+            return new ValueGetter.NDR(bytes);
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Endian type:" + bytes.get(0));
+        }
+    }
+
+    /**
+     * Parse a hex encoded geometry
+     */
+    public Geometry parse(String value)
+    {
+        StringByteGetter bytes = new ByteGetter.StringByteGetter(value);
+        return parseGeometry(valueGetterForEndian(bytes));
+    }
+
+    /**
+     * Parse a binary encoded geometry.
+     */
+    public Geometry parse(byte[] value)
+    {
+        BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
+        return parseGeometry(valueGetterForEndian(bytes));
+    }
+
+    /**
+     * Parse a geometry starting at offset.
+     */
+    protected Geometry parseGeometry(ValueGetter data)
+    {
+        return parseGeometry(data, 0, false);
+    }
+
+    /**
+     * Parse with a known geometry factory
+     */
+    protected Geometry parseGeometry(ValueGetter data, int srid, boolean inheritSrid)
+    {
+        byte endian = data.getByte(); // skip and test endian flag
+        if (endian != data.endian)
+        {
+            throw new IllegalArgumentException("Endian inconsistency!");
+        }
+        int typeword = data.getInt();
+
+        int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
+
+        boolean haveZ = (typeword & 0x80000000) != 0;
+        boolean haveM = (typeword & 0x40000000) != 0;
+        boolean haveS = (typeword & 0x20000000) != 0;
+
+        if (haveS)
+        {
+            int newsrid = data.getInt();
+            if (inheritSrid && newsrid != srid)
+            {
+                throw new IllegalArgumentException("Inconsistent srids in complex geometry: " + srid + ", " + newsrid);
+            } else
+            {
+                srid = newsrid;
+            }
+        } else if (!inheritSrid)
+        {
+            srid = -1;
+        }
+
+        Geometry result;
+        switch (realtype)
+        {
+        case org.postgis.Geometry.POINT:
+            result = parsePoint(data, haveZ, haveM);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            result = parseLineString(data, haveZ, haveM);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            result = parsePolygon(data, haveZ, haveM, srid);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            result = parseMultiPoint(data, srid);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            result = parseMultiLineString(data, srid);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            result = parseMultiPolygon(data, srid);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            result = parseCollection(data, srid);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type!");
+        }
+
+        result.setSRID(srid);
+
+        return result;
+    }
+
+    private Point parsePoint(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        double X = data.getDouble();
+        double Y = data.getDouble();
+        Point result;
+        if (haveZ)
+        {
+            double Z = data.getDouble();
+            result = JtsGeometry.geofac.createPoint(new Coordinate(X, Y, Z));
+        } else
+        {
+            result = JtsGeometry.geofac.createPoint(new Coordinate(X, Y));
+        }
+
+        if (haveM)
+        { // skip M value
+            data.getDouble();
+        }
+
+        return result;
+    }
+
+    /**
+     * Parse an Array of "full" Geometries
+     */
+    private void parseGeometryArray(ValueGetter data, Geometry[] container, int srid)
+    {
+        for (int i = 0; i < container.length; i++)
+        {
+            container[i] = parseGeometry(data, srid, true);
+        }
+    }
+
+    /**
+     * Parse an Array of "slim" Points (without endianness and type, part of
+     * LinearRing and Linestring, but not MultiPoint!
+     *
+     * @param haveZ
+     * @param haveM
+     */
+    private CoordinateSequence parseCS(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        int count = data.getInt();
+        int dims = haveZ ? 3 : 2;
+        CoordinateSequence cs = new PackedCoordinateSequence.Double(count, dims);
+
+        for (int i = 0; i < count; i++)
+        {
+            for (int d = 0; d < dims; d++)
+            {
+                cs.setOrdinate(i, d, data.getDouble());
+            }
+            if (haveM)
+            { // skip M value
+                data.getDouble();
+            }
+        }
+        return cs;
+    }
+
+    private MultiPoint parseMultiPoint(ValueGetter data, int srid)
+    {
+        Point[] points = new Point[data.getInt()];
+        parseGeometryArray(data, points, srid);
+        return JtsGeometry.geofac.createMultiPoint(points);
+    }
+
+    private LineString parseLineString(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        return JtsGeometry.geofac.createLineString(parseCS(data, haveZ, haveM));
+    }
+
+    private LinearRing parseLinearRing(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        return JtsGeometry.geofac.createLinearRing(parseCS(data, haveZ, haveM));
+    }
+
+    private Polygon parsePolygon(ValueGetter data, boolean haveZ, boolean haveM, int srid)
+    {
+        int holecount = data.getInt() - 1;
+        LinearRing[] rings = new LinearRing[holecount];
+        LinearRing shell = parseLinearRing(data, haveZ, haveM);
+        shell.setSRID(srid);
+        for (int i = 0; i < holecount; i++)
+        {
+            rings[i] = parseLinearRing(data, haveZ, haveM);
+            rings[i].setSRID(srid);
+        }
+        return JtsGeometry.geofac.createPolygon(shell, rings);
+    }
+
+    private MultiLineString parseMultiLineString(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        LineString[] strings = new LineString[count];
+        parseGeometryArray(data, strings, srid);
+        return JtsGeometry.geofac.createMultiLineString(strings);
+    }
+
+    private MultiPolygon parseMultiPolygon(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        Polygon[] polys = new Polygon[count];
+        parseGeometryArray(data, polys, srid);
+        return JtsGeometry.geofac.createMultiPolygon(polys);
+    }
+
+    private GeometryCollection parseCollection(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        Geometry[] geoms = new Geometry[count];
+        parseGeometryArray(data, geoms, srid);
+        return JtsGeometry.geofac.createGeometryCollection(geoms);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java
new file mode 100644
index 0000000..36a9d7b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java
@@ -0,0 +1,469 @@
+/*
+ * JtsBinaryWriter.java
+ *
+ * PostGIS extension for PostgreSQL JDBC driver - Binary Writer
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+package com.ximple.eofms.util.postjts;
+
+import org.postgis.binary.ByteSetter;
+import org.postgis.binary.ValueSetter;
+
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+
+/**
+ * Create binary representation of geometries. Currently, only text rep (hexed)
+ * implementation is tested. Supports only 2 dimensional geometries.
+ * <p/>
+ * It should be easy to add char[] and CharSequence ByteGetter instances,
+ * although the latter one is not compatible with older jdks.
+ * <p/>
+ * I did not implement real unsigned 32-bit integers or emulate them with long,
+ * as both java Arrays and Strings currently can have only 2^31-1 elements
+ * (bytes), so we cannot even get or build Geometries with more than approx.
+ * 2^28 coordinates (8 bytes each).
+ *
+ * @author markus.schaber@logi-track.com
+ */
+public class JtsBinaryWriter
+{
+
+    /**
+     * Get the appropriate ValueGetter for my endianness
+     *
+     * @param bytes The appropriate Byte Getter
+     * @return the ValueGetter
+     */
+    public static ValueSetter valueSetterForEndian(ByteSetter bytes, byte endian)
+    {
+        if (endian == ValueSetter.XDR.NUMBER)
+        { // XDR
+            return new ValueSetter.XDR(bytes);
+        } else if (endian == ValueSetter.NDR.NUMBER)
+        {
+            return new ValueSetter.NDR(bytes);
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Endian type:" + endian);
+        }
+    }
+
+    /**
+     * Write a hex encoded geometry
+     * <p/>
+     * Currently, geometries with more than 2 dimensions and measures are not
+     * cleanly supported, but SRID is honored.
+     */
+    public String writeHexed(Geometry geom, byte REP)
+    {
+        int length = estimateBytes(geom);
+        ByteSetter.StringByteSetter bytes = new ByteSetter.StringByteSetter(length);
+        writeGeometry(geom, valueSetterForEndian(bytes, REP));
+        return bytes.result();
+    }
+
+    public String writeHexed(Geometry geom)
+    {
+        return writeHexed(geom, ValueSetter.NDR.NUMBER);
+    }
+
+    /**
+     * Write a binary encoded geometry.
+     * <p/>
+     * Currently, geometries with more than 2 dimensions and measures are not
+     * cleanly supported, but SRID is honored.
+     */
+    public byte[] writeBinary(Geometry geom, byte REP)
+    {
+        int length = estimateBytes(geom);
+        ByteSetter.BinaryByteSetter bytes = new ByteSetter.BinaryByteSetter(length);
+        writeGeometry(geom, valueSetterForEndian(bytes, REP));
+        return bytes.result();
+    }
+
+    public byte[] writeBinary(Geometry geom)
+    {
+        return writeBinary(geom, ValueSetter.NDR.NUMBER);
+    }
+
+    /**
+     * Parse a geometry starting at offset.
+     */
+    protected void writeGeometry(Geometry geom, ValueSetter dest)
+    {
+        final int dimension;
+        if (geom == null)
+        {
+            throw new NullPointerException();
+        } else if (geom.isEmpty())
+        {
+            // don't set any flag bits
+            dimension = 0;
+        } else
+        {
+            dimension = getCoordDim(geom);
+            if (dimension < 2 || dimension > 4)
+            {
+                throw new IllegalArgumentException("Unsupported geometry dimensionality: " + dimension);
+            }
+        }
+        // write endian flag
+        dest.setByte(dest.endian);
+
+        // write typeword
+        final int plaintype = getWKBType(geom);
+        int typeword = plaintype;
+        if (dimension == 3 || dimension == 4)
+        {
+            typeword |= 0x80000000;
+        }
+        if (dimension == 4)
+        {
+            typeword |= 0x40000000;
+        }
+
+        final boolean haveSrid = checkSrid(geom);
+        if (haveSrid)
+        {
+            typeword |= 0x20000000;
+        }
+
+        dest.setInt(typeword);
+
+        if (haveSrid)
+        {
+            dest.setInt(geom.getSRID());
+        }
+
+        switch (plaintype)
+        {
+        case org.postgis.Geometry.POINT:
+            writePoint((Point) geom, dest);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            writeLineString((LineString) geom, dest);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            writePolygon((Polygon) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            writeMultiPoint((MultiPoint) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            writeMultiLineString((MultiLineString) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            writeMultiPolygon((MultiPolygon) geom, dest);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            writeCollection((GeometryCollection) geom, dest);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type: " + plaintype);
+        }
+    }
+
+    public static int getWKBType(Geometry geom)
+    {
+        // We always write emtpy geometries as emtpy collections - for OpenGIS
+        // conformance
+        if (geom.isEmpty())
+        {
+            return org.postgis.Geometry.GEOMETRYCOLLECTION;
+        } else if (geom instanceof Point)
+        {
+            return org.postgis.Geometry.POINT;
+        } else if (geom instanceof com.vividsolutions.jts.geom.LineString)
+        {
+            return org.postgis.Geometry.LINESTRING;
+        } else if (geom instanceof com.vividsolutions.jts.geom.Polygon)
+        {
+            return org.postgis.Geometry.POLYGON;
+        } else if (geom instanceof MultiPoint)
+        {
+            return org.postgis.Geometry.MULTIPOINT;
+        } else if (geom instanceof MultiLineString)
+        {
+            return org.postgis.Geometry.MULTILINESTRING;
+        } else if (geom instanceof com.vividsolutions.jts.geom.MultiPolygon)
+        {
+            return org.postgis.Geometry.MULTIPOLYGON;
+        }
+        if (geom instanceof com.vividsolutions.jts.geom.GeometryCollection)
+        {
+            return org.postgis.Geometry.GEOMETRYCOLLECTION;
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Geometry Type: " + geom.getClass().getName());
+        }
+    }
+
+    /**
+     * Writes a "slim" Point (without endiannes, srid ant type, only the
+     * ordinates and measure. Used by writeGeometry.
+     */
+    private void writePoint(Point geom, ValueSetter dest)
+    {
+        writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest);
+    }
+
+    /**
+     * Write a Coordinatesequence, part of LinearRing and Linestring, but not
+     * MultiPoint!
+     */
+    private void writeCoordinates(CoordinateSequence seq, int dims, ValueSetter dest)
+    {
+        for (int i = 0; i < seq.size(); i++)
+        {
+            for (int d = 0; d < dims; d++)
+            {
+                dest.setDouble(seq.getOrdinate(i, d));
+            }
+        }
+    }
+
+    private void writeMultiPoint(MultiPoint geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumPoints());
+        for (int i = 0; i < geom.getNumPoints(); i++)
+        {
+            writeGeometry(geom.getGeometryN(i), dest);
+        }
+    }
+
+    private void writeLineString(LineString geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumPoints());
+        writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest);
+    }
+
+    private void writePolygon(Polygon geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumInteriorRing() + 1);
+        writeLineString(geom.getExteriorRing(), dest);
+        for (int i = 0; i < geom.getNumInteriorRing(); i++)
+        {
+            writeLineString(geom.getInteriorRingN(i), dest);
+        }
+    }
+
+    private void writeMultiLineString(MultiLineString geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeMultiPolygon(MultiPolygon geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeCollection(GeometryCollection geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeGeometryArray(Geometry geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumGeometries());
+        for (int i = 0; i < geom.getNumGeometries(); i++)
+        {
+            writeGeometry(geom.getGeometryN(i), dest);
+        }
+    }
+
+    /**
+     * Estimate how much bytes a geometry will need in WKB.
+     */
+    protected int estimateBytes(Geometry geom)
+    {
+        int result = 0;
+
+        // write endian flag
+        result += 1;
+
+        // write typeword
+        result += 4;
+
+        if (checkSrid(geom))
+        {
+            result += 4;
+        }
+
+        switch (getWKBType(geom))
+        {
+        case org.postgis.Geometry.POINT:
+            result += estimatePoint((Point) geom);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            result += estimateLineString((LineString) geom);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            result += estimatePolygon((Polygon) geom);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            result += estimateMultiPoint((MultiPoint) geom);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            result += estimateMultiLineString((MultiLineString) geom);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            result += estimateMultiPolygon((MultiPolygon) geom);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            result += estimateCollection((GeometryCollection) geom);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type: " + getWKBType(geom));
+        }
+        return result;
+    }
+
+    private boolean checkSrid(Geometry geom)
+    {
+        final int srid = geom.getSRID();
+        // SRID is default 0 with jts geometries
+        return (srid != -1) && (srid != 0);
+    }
+
+    private int estimatePoint(Point geom)
+    {
+        return 8 * getCoordDim(geom);
+    }
+
+    /**
+     * Write an Array of "full" Geometries
+     */
+    private int estimateGeometryArray(Geometry container)
+    {
+        int result = 0;
+        for (int i = 0; i < container.getNumGeometries(); i++)
+        {
+            result += estimateBytes(container.getGeometryN(i));
+        }
+        return result;
+    }
+
+    /**
+     * Estimate an array of "fat" Points
+     */
+    private int estimateMultiPoint(MultiPoint geom)
+    {
+        // int size
+        int result = 4;
+        if (geom.getNumGeometries() > 0)
+        {
+            // We can shortcut here, compared to estimateGeometryArray, as all
+            // subgeoms have the same fixed size
+            result += geom.getNumGeometries() * estimateBytes(geom.getGeometryN(0));
+        }
+        return result;
+    }
+
+    private int estimateLineString(LineString geom)
+    {
+        if (geom == null || geom.getNumGeometries() == 0)
+        {
+            return 0;
+        } else
+        {
+            return 4 + 8 * getCoordSequenceDim(geom.getCoordinateSequence()) * geom.getCoordinateSequence().size();
+        }
+    }
+
+    private int estimatePolygon(Polygon geom)
+    {
+        // int length
+        int result = 4;
+        result += estimateLineString(geom.getExteriorRing());
+        for (int i = 0; i < geom.getNumInteriorRing(); i++)
+        {
+            result += estimateLineString(geom.getInteriorRingN(i));
+        }
+        return result;
+    }
+
+    private int estimateMultiLineString(MultiLineString geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    private int estimateMultiPolygon(MultiPolygon geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    private int estimateCollection(GeometryCollection geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    public static final int getCoordDim(Geometry geom)
+    {
+        if (geom.isEmpty())
+        {
+            return 0;
+        }
+        if (geom instanceof Point)
+        {
+            return getCoordSequenceDim(((Point) geom).getCoordinateSequence());
+        } else if (geom instanceof LineString)
+        {
+            return getCoordSequenceDim(((LineString) geom).getCoordinateSequence());
+        } else if (geom instanceof Polygon)
+        {
+            return getCoordSequenceDim(((Polygon) geom).getExteriorRing().getCoordinateSequence());
+        } else
+        {
+            return getCoordDim(geom.getGeometryN(0));
+        }
+    }
+
+    public static final int getCoordSequenceDim(CoordinateSequence coords)
+    {
+        if (coords == null || coords.size() == 0)
+            return 0;
+        // JTS has a really strange way to handle dimensions!
+        // Just have a look at PackedCoordinateSequence and
+        // CoordinateArraySequence
+        int dimensions = coords.getDimension();
+        if (dimensions == 3)
+        {
+            // CoordinateArraySequence will always return 3, so we have to
+            // check, if
+            // the third ordinate contains NaN, then the geom is actually
+            // 2-dimensional
+            return Double.isNaN(coords.getOrdinate(0, CoordinateSequence.Z)) ? 2 : 3;
+        } else
+        {
+            return dimensions;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java
new file mode 100644
index 0000000..18a6295
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java
@@ -0,0 +1,187 @@
+/*
+ * JtsGeometry.java
+ *
+ * Wrapper for PostgreSQL JDBC driver to allow transparent reading and writing
+ * of JTS geometries
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.SQLException;
+
+import org.postgresql.util.PGobject;
+
+import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.PrecisionModel;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
+import com.vividsolutions.jts.io.WKTReader;
+
+/**
+ * JTS Geometry SQL wrapper. Supports PostGIS 1.x (lwgeom hexwkb) for writing
+ * and both PostGIS 0.x (EWKT) and 1.x (lwgeom hexwkb) for reading.
+ *
+ * @author Markus Schaber
+ */
+
+public class JtsGeometry extends PGobject
+{
+    /* JDK 1.5 Serialization */
+    private static final long serialVersionUID = 0x100;
+
+    Geometry geom;
+
+    final static JtsBinaryParser bp = new JtsBinaryParser();
+
+    final static JtsBinaryWriter bw = new JtsBinaryWriter();
+
+    final static PrecisionModel prec = new PrecisionModel();
+
+    final static CoordinateSequenceFactory csfac = PackedCoordinateSequenceFactory.DOUBLE_FACTORY;
+
+    final static GeometryFactory geofac = new GeometryFactory(prec, 0, csfac);
+
+    static final WKTReader reader = new WKTReader(geofac);
+
+    /**
+     * Constructor called by JDBC drivers
+     */
+    public JtsGeometry()
+    {
+        setType("geometry");
+    }
+
+    public JtsGeometry(Geometry geom)
+    {
+        this();
+        this.geom = geom;
+    }
+
+    public JtsGeometry(String value) throws SQLException
+    {
+        this();
+        setValue(value);
+    }
+
+    public void setValue(String value) throws SQLException
+    {
+        geom = geomFromString(value);
+    }
+
+    public static Geometry geomFromString(String value) throws SQLException
+    {
+        try
+        {
+            value = value.trim();
+            if (value.startsWith("00") || value.startsWith("01"))
+            {
+                return bp.parse(value);
+            } else
+            {
+                Geometry result;
+                // no srid := 0 in JTS world
+                int srid = 0;
+                // break up geometry into srid and wkt
+                if (value.startsWith("SRID="))
+                {
+                    String[] temp = value.split(";");
+                    value = temp[1].trim();
+                    srid = Integer.parseInt(temp[0].substring(5));
+                }
+
+                result = reader.read(value);
+                setSridRecurse(result, srid);
+                return result;
+            }
+        } catch (Exception E)
+        {
+            E.printStackTrace();
+            throw new SQLException("Error parsing SQL data:" + E);
+        }
+    }
+
+    /**
+     * Recursively set a srid for the geometry and all subgeometries
+     */
+    public static void setSridRecurse(final Geometry geom, final int srid)
+    {
+        geom.setSRID(srid);
+        if (geom instanceof GeometryCollection)
+        {
+            final int subcnt = geom.getNumGeometries();
+            for (int i = 0; i < subcnt; i++)
+            {
+                setSridRecurse(geom.getGeometryN(i), srid);
+            }
+        } else if (geom instanceof Polygon)
+        {
+            Polygon poly = (Polygon) geom;
+            poly.getExteriorRing().setSRID(srid);
+            final int subcnt = poly.getNumInteriorRing();
+            for (int i = 0; i < subcnt; i++)
+            {
+                poly.getInteriorRingN(i).setSRID(srid);
+            }
+        }
+    }
+
+    public Geometry getGeometry()
+    {
+        return geom;
+    }
+
+    public String toString()
+    {
+        return geom.toString();
+    }
+
+    public String getValue()
+    {
+        return bw.writeHexed(getGeometry());
+    }
+
+    public Object clone()
+    {
+        JtsGeometry obj = new JtsGeometry(geom);
+        obj.setType(type);
+        return obj;
+    }
+
+    public boolean equals(Object obj)
+    {
+        if ((obj != null) && (obj instanceof JtsGeometry))
+        {
+            Geometry other = ((JtsGeometry) obj).geom;
+            if (this.geom == other)
+            { // handles identity as well as both
+                // ==null
+                return true;
+            } else if (this.geom != null && other != null)
+            {
+                return other.equals(this.geom);
+            }
+        }
+        return false;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java
new file mode 100644
index 0000000..1af43b4
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java
@@ -0,0 +1,173 @@
+/*
+ * JtsWrapper.java
+ *
+ * Allows transparent usage of JTS Geometry classes via PostgreSQL JDBC driver
+ * connected to a PostGIS enabled PostgreSQL server.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.postgresql.Driver;
+import org.postgresql.PGConnection;
+
+/**
+ * JtsGisWrapper
+ * <p/>
+ * Wraps the PostGreSQL Driver to add the JTS/PostGIS Object Classes.
+ * <p/>
+ * This method currently works with J2EE DataSource implementations, and with
+ * DriverManager framework.
+ * <p/>
+ * Simply replace the "jdbc:postgresql:" with a "jdbc:postgresql_JTS" in the
+ * jdbc URL.
+ *
+ * @author markus.schaber@logix-tt.com
+ */
+public class JtsGisWrapper extends Driver
+{
+
+    private static final String POSTGRES_PROTOCOL = "jdbc:postgresql:";
+    private static final String POSTGIS_PROTOCOL = "jdbc:postgresql_JTS:";
+    public static final String REVISION = "$Revision: 1977 $";
+
+    public JtsGisWrapper()
+    {
+        super();
+    }
+
+    static
+    {
+        try
+        {
+            // Analogy to org.postgresql.Driver
+            java.sql.DriverManager.registerDriver(new JtsGisWrapper());
+        } catch (SQLException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Creates a postgresql connection, and then adds the PostGIS data types to
+     * it calling addpgtypes()
+     *
+     * @param url  the URL of the database to connect to
+     * @param info a list of arbitrary tag/value pairs as connection arguments
+     * @return a connection to the URL or null if it isnt us
+     * @throws SQLException if a database access error occurs
+     * @see java.sql.Driver#connect
+     * @see org.postgresql.Driver
+     */
+    public java.sql.Connection connect(String url, Properties info) throws SQLException
+    {
+        url = mangleURL(url);
+        Connection result = super.connect(url, info);
+        addGISTypes((PGConnection) result);
+        return result;
+    }
+
+    /**
+     * adds the JTS/PostGIS Data types to a PG Connection.
+     *
+     * @param pgconn
+     * @throws SQLException
+     */
+    public static void addGISTypes(PGConnection pgconn) throws SQLException
+    {
+        pgconn.addDataType("geometry", JtsGeometry.class);
+        pgconn.addDataType("box3d", org.postgis.PGbox3d.class);
+        pgconn.addDataType("box2d", org.postgis.PGbox2d.class);
+    }
+
+    /**
+     * Mangles the PostGIS URL to return the original PostGreSQL URL
+     * @param url url
+     * @return string
+     * @throws java.sql.SQLException error
+     */
+    public static String mangleURL(String url) throws SQLException
+    {
+        if (url.startsWith(POSTGIS_PROTOCOL))
+        {
+            return POSTGRES_PROTOCOL + url.substring(POSTGIS_PROTOCOL.length());
+        } else
+        {
+            throw new SQLException("Unknown protocol or subprotocol in url " + url);
+        }
+    }
+
+    /**
+     * Returns true if the driver thinks it can open a connection to the given
+     * URL. Typically, drivers will return true if they understand the
+     * subprotocol specified in the URL and false if they don't. Our protocols
+     * start with jdbc:postgresql_postGIS:
+     *
+     * @param url the URL of the driver
+     * @return true if this driver accepts the given URL
+     * @throws SQLException if a database-access error occurs (Dont know why
+     *                      it would *shrug*)
+     * @see java.sql.Driver#acceptsURL
+     */
+    public boolean acceptsURL(String url) throws SQLException
+    {
+        try
+        {
+            url = mangleURL(url);
+        } catch (SQLException e)
+        {
+            return false;
+        }
+        return super.acceptsURL(url);
+    }
+
+    /**
+     * Gets the underlying drivers major version number
+     *
+     * @return the drivers major version number
+     */
+
+    public int getMajorVersion()
+    {
+        return super.getMajorVersion();
+    }
+
+    /**
+     * Get the underlying drivers minor version number
+     *
+     * @return the drivers minor version number
+     */
+    public int getMinorVersion()
+    {
+        return super.getMinorVersion();
+    }
+
+    /**
+     * Returns our own CVS version plus postgres Version
+     */
+    public static String getVersion()
+    {
+        return "JtsGisWrapper " + REVISION + ", wrapping " + Driver.getVersion();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java
new file mode 100644
index 0000000..b87f551
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java
@@ -0,0 +1,180 @@
+/*
+ * JtsWrapper.java
+ *
+ * Allows transparent usage of JTS Geometry classes via PostgreSQL JDBC driver
+ * connected to a PostGIS enabled PostgreSQL server.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.postgresql.Driver;
+import org.postgresql.PGConnection;
+
+/**
+ * DriverWrapper
+ * <p/>
+ * Wraps the PostGreSQL Driver to add the JTS/PostGIS Object Classes.
+ * <p/>
+ * This method currently works with J2EE DataSource implementations, and with
+ * DriverManager framework.
+ * <p/>
+ * Simply replace the "jdbc:postgresql:" with a "jdbc:postgres_jts:" in the jdbc
+ * URL.
+ * <p/>
+ * When using the drivermanager, you need to initialize JtsWrapper instead of
+ * (or in addition to) org.postgresql.Driver. When using a J2EE DataSource
+ * implementation, set the driver class property in the datasource config, the
+ * following works for jboss:
+ * <p/>
+ * &lt;driver-class&gt;org.postgis.jts.PostGisWrapper&lt;/driver-class&gt;
+ *
+ * @author markus.schaber@logix-tt.com
+ */
+public class JtsWrapper extends Driver
+{
+
+    protected static final Logger logger = Logger.getLogger("org.postgis.DriverWrapper");
+
+    private static final String POSTGRES_PROTOCOL = "jdbc:postgresql:";
+    private static final String POSTGIS_PROTOCOL = "jdbc:postgres_jts:";
+    public static final String REVISION = "$Revision: 2570 $";
+
+    public JtsWrapper()
+    {
+        super();
+    }
+
+    static
+    {
+        try
+        {
+            // Try to register ourself to the DriverManager
+            java.sql.DriverManager.registerDriver(new JtsWrapper());
+        } catch (SQLException e)
+        {
+            logger.log(Level.WARNING, "Error registering PostgreSQL Jts Wrapper Driver", e);
+        }
+    }
+
+    /**
+     * Creates a postgresql connection, and then adds the JTS GIS data types to
+     * it calling addpgtypes()
+     *
+     * @param url  the URL of the database to connect to
+     * @param info a list of arbitrary tag/value pairs as connection arguments
+     * @return a connection to the URL or null if it isnt us
+     * @throws SQLException if a database access error occurs
+     * @see java.sql.Driver#connect
+     * @see org.postgresql.Driver
+     */
+    public java.sql.Connection connect(String url, Properties info) throws SQLException
+    {
+        url = mangleURL(url);
+        Connection result = super.connect(url, info);
+        addGISTypes((PGConnection) result);
+        return result;
+    }
+
+    /**
+     * adds the JTS/PostGIS Data types to a PG Connection.
+     *
+     * @param pgconn  postgres connection
+     * @throws SQLException error
+     */
+    public static void addGISTypes(PGConnection pgconn) throws SQLException
+    {
+        pgconn.addDataType("geometry", JtsGeometry.class);
+    }
+
+    /**
+     * Mangles the PostGIS URL to return the original PostGreSQL URL
+     * @param url url
+     * @return string
+     * @throws java.sql.SQLException erroe
+     */
+    public static String mangleURL(String url) throws SQLException
+    {
+        if (url.startsWith(POSTGIS_PROTOCOL))
+        {
+            return POSTGRES_PROTOCOL + url.substring(POSTGIS_PROTOCOL.length());
+        } else
+        {
+            throw new SQLException("Unknown protocol or subprotocol in url " + url);
+        }
+    }
+
+    /**
+     * Check whether the driver thinks he can handle the given URL.
+     *
+     * @param url the URL of the driver
+     * @return true if this driver accepts the given URL
+     * @throws SQLException Passed through from the underlying PostgreSQL
+     *                      driver, should not happen.
+     * @see java.sql.Driver#acceptsURL
+     */
+    public boolean acceptsURL(String url) throws SQLException
+    {
+        try
+        {
+            url = mangleURL(url);
+        } catch (SQLException e)
+        {
+            return false;
+        }
+        return super.acceptsURL(url);
+    }
+
+    /**
+     * Gets the underlying drivers major version number
+     *
+     * @return the drivers major version number
+     */
+
+    public int getMajorVersion()
+    {
+        return super.getMajorVersion();
+    }
+
+    /**
+     * Get the underlying drivers minor version number
+     *
+     * @return the drivers minor version number
+     */
+    public int getMinorVersion()
+    {
+        return super.getMinorVersion();
+    }
+
+    /**
+     * Returns our own CVS version plus postgres Version
+     * @return version
+     */
+    public static String getVersion()
+    {
+        return "JtsGisWrapper " + REVISION + ", wrapping " + Driver.getVersion();
+    }
+}
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..9686501
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml
@@ -0,0 +1,176 @@
+<?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 value="ComplexChainCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateComplexChainStrategy"/>
+        <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 value="ComplexChainCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateComplexChainStrategy"/>
+        <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 value="ComplexChainCreateStrategy">
+        <object-create-rule classname="com.ximple.eofms.filter.CreateComplexChainStrategy"/>
+        <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/com/ximple/eofms/filter/FeatureClassificationRules.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/FeatureClassificationRules.xml
new file mode 100644
index 0000000..e925ce6
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/FeatureClassificationRules.xml
@@ -0,0 +1,16 @@
+<?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="FeatureClassificationRules">
+    <object-create-rule classname="com.ximple.eofms.collector.FeatureClassification"/>
+    <set-properties-rule/>
+    <pattern value="FeatureTypeCollector">
+      <object-create-rule classname="com.ximple.eofms.collector.FeatureTypeCollector"/>
+      <set-next-rule methodname="addCollector" paramtype="com.ximple.eofms.collector.FeatureTypeCollector"/>
+      <set-properties-rule/>
+      <bean-property-setter-rule pattern="name"/>
+      <bean-property-setter-rule pattern="description"/>
+      <bean-property-setter-rule pattern="featuretypeList"/>
+    </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/main/resources/conf/DefaultMapGroups.xml b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultMapGroups.xml
new file mode 100644
index 0000000..27626b3
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/resources/conf/DefaultMapGroups.xml
@@ -0,0 +1,91 @@
+<FeatureClassificationRules>
+  <FeatureTypeCollector name="22KVSUBHV">
+    <featuretypeList>
+      tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-503.C-0_1_2,tpc:FSC-411.C-0_27_1,tpc:FSC-411.C-1_37_1,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-2_24_1,tpc:FSC-114.C-0_24_1,tpc:FSC-114.C-1_34_1,tpc:FSC-106.C-2_4_1,tpc:FSC-106.C-0_4_1,tpc:FSC-115.C-6_48_1,tpc:FSC-115.C-0_48_1,tpc:FSC-122.C-0_24_1,tpc:FSC-122.C-1_34_1,tpc:FSC-105.C-0_39_1,tpc:FSC-106.C-0_3_1,tpc:FSC-115.C-6_48_1,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-1_56_1,tpc:FSC-411.C-0_8_1,tpc:FSC-411.C-1_18_1,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-2_36_1,tpc:FSC-120.C-0_52_1,tpc:FSC-101.C-0_51_0,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-9_10_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="11KVHVOH">
+    <featuretypeList>tpc:FSC-115.C-0_46_1,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-1_46_1,tpc:FSC-115.C-1_46_2,tpc:FSC-106.C-2_1_1,tpc:FSC-106.C-2_1_2,tpc:FSC-106.C-4_1_1,tpc:FSC-106.C-4_1_2,tpc:FSC-106.C-0_1_1,tpc:FSC-106.C-0_1_2,tpc:FSC-140.C-2_51_1,tpc:FSC-140.C-2_51_2,tpc:FSC-140.C-0_51_1,tpc:FSC-140.C-0_51_2,tpc:FSC-130.C-0_11_1,tpc:FSC-130.C-0_11_2,tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-108.C-0_44_2,tpc:FSC-108.C-1_54_2,tpc:FSC-119.C-0_61_2,tpc:FSC-118.C-0_49_2,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-8_9_2,tpc:FSC-407.C-9_9_1,tpc:FSC-407.C-9_9_2,tpc:FSC-407.C-10_9_1,tpc:FSC-407.C-11_9_1,tpc:FSC-114.C-0_21_1,tpc:FSC-114.C-0_21_2,tpc:FSC-114.C-2_21_0,tpc:FSC-114.C-2_21_1,tpc:FSC-114.C-2_21_2,tpc:FSC-107.C-2_37_1,tpc:FSC-107.C-0_27_1,tpc:FSC-117.C-0_27_2,tpc:FSC-120.C-0_42_1,tpc:FSC-120.C-0_42_2,tpc:FSC-131.C-0_12_1,tpc:FSC-131.C-0_12_2,tpc:FSC-109.C-0_13_1,tpc:FSC-109.C-0_13_2,tpc:FSC-109.C-0_13_3,tpc:FSC-116.C-0_14_1,tpc:FSC-116.C-0_14_2,tpc:FSC-102.C-0_30_2,tpc:FSC-102.C-0_40_1,tpc:FSC-100.C-0_59_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-503.C-0_1_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="CLS">
+    <featuretypeList>tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-0_37_1,tpc:FSC-203.C-1_38_1,tpc:FSC-203.C-0_38_1,tpc:FSC-311.C-0_45_1,tpc:FSC-311.C-1_45_0,tpc:FSC-311.C-1_45_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="LVOH">
+    <featuretypeList>tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-1_9_1,tpc:FSC-407.C-1_9_2,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-8_9_2,tpc:FSC-407.C-9_10_1,tpc:FSC-407.C-9_9_1,tpc:FSC-407.C-9_9_2,tpc:FSC-115.C-2_50_2,tpc:FSC-115.C-3_50_1,tpc:FSC-115.C-7_58_1,tpc:FSC-200.C-0_28_1,tpc:FSC-200.C-0_30_1,tpc:FSC-202.C-1_36_0,tpc:FSC-202.C-0_36_1,tpc:FSC-203.C-1_38_1,tpc:FSC-204.C-0_49_1,tpc:FSC-205.C-2_41_1,tpc:FSC-205.C-0_39_1,tpc:FSC-208.C-0_44_1,tpc:FSC-209.C-0_46_1,tpc:FSC-211.C-0_32_1,tpc:FSC-212.C-1_34_0,tpc:FSC-303.C-0_29_1,tpc:FSC-305.C-0_34_1,tpc:FSC-306.C-0_36_1,tpc:FSC-307.C-0_38_1,tpc:FSC-308.C-0_40_1,tpc:FSC-314.C-0_26_1,tpc:FSC-314.C-1_28_0,tpc:FSC-315.C-0_26_1,tpc:FSC-315.C-1_28_0,tpc:FSC-316.C-0_54_1,tpc:FSC-317.C-0_32_1,tpc:FSC-317.C-1_32_1,tpc:FSC-318.C-0_54_4,tpc:FSC-503.C-0_1_2,tpc:FSC-502.C-0_1_1,tpc:FSC-502.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-999.C-0_62_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="HICUSTOMER">
+    <featuretypeList>tpc:FSC-107.C-0_25_1,tpc:FSC-107.C-0_25_2,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-0_26_2,tpc:FSC-107.C-0_27_1,tpc:FSC-107.C-2_0_1,tpc:FSC-107.C-2_25_1,tpc:FSC-107.C-2_26_1,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-2_36_1,tpc:FSC-107.C-2_36_2,tpc:FSC-107.C-2_37_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="22KVHVUG">
+    <featuretypeList>tpc:FSC-113.C-0_4_1,tpc:FSC-403.C-2_14_1,tpc:FSC-403.C-2_15_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-403.C-1_4_1,tpc:FSC-403.C-1_13_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-101.C-0_51_0,tpc:FSC-101.C-0_51_1,tpc:FSC-140.C-1_54_0,tpc:FSC-140.C-1_54_1,tpc:FSC-140.C-2_54_1,tpc:FSC-140.C-2_54_2,tpc:FSC-140.C-0_54_1,tpc:FSC-140.C-0_54_2,tpc:FSC-140.C-3_54_0,tpc:FSC-140.C-4_54_1,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-411.C-0_27_1,tpc:FSC-411.C-0_27_2,tpc:FSC-411.C-0_8_1,tpc:FSC-411.C-0_8_2,tpc:FSC-411.C-1_37_1,tpc:FSC-411.C-1_37_2,tpc:FSC-411.C-1_18_1,tpc:FSC-411.C-1_18_2,tpc:FSC-118.C-0_50_2,tpc:FSC-115.C-0_48_1,tpc:FSC-115.C-0_48_2,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-3_60_2,tpc:FSC-114.C-2_24_1,tpc:FSC-114.C-2_24_2,tpc:FSC-114.C-0_24_1,tpc:FSC-114.C-0_24_2,tpc:FSC-114.C-1_34_1,tpc:FSC-114.C-1_34_2,tpc:FSC-122.C-0_24_1,tpc:FSC-122.C-1_34_1,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-0_26_2,tpc:FSC-117.C-0_27_2,tpc:FSC-105.C-0_39_1,tpc:FSC-105.C-0_39_2,tpc:FSC-120.C-0_52_1,tpc:FSC-120.C-0_52_2,tpc:FSC-108.C-0_45_2,tpc:FSC-108.C-1_55_2,tpc:FSC-119.C-0_62_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="11KVSUBHV">
+    <featuretypeList>tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-503.C-0_1_2,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-2_23_1,tpc:FSC-114.C-0_23_1,tpc:FSC-114.C-1_33_1,tpc:FSC-106.C-2_2_1,tpc:FSC-106.C-0_2_1,tpc:FSC-106.C-2_1_1,tpc:FSC-106.C-0_1_1,tpc:FSC-106.C-4_1_1,tpc:FSC-115.C-0_47_1,tpc:FSC-115.C-1_57_1,tpc:FSC-122.C-0_23_1,tpc:FSC-122.C-1_33_1,tpc:FSC-105.C-0_29_1,tpc:FSC-114.C-0_21_1,tpc:FSC-115.C-6_47_1,tpc:FSC-115.C-0_46_1,tpc:FSC-115.C-1_46_1,tpc:FSC-411.C-0_7_1,tpc:FSC-411.C-1_17_1,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-0_25_1,tpc:FSC-107.C-2_37_1,tpc:FSC-107.C-0_27_1,tpc:FSC-116.C-0_14_1,tpc:FSC-120.C-0_42_1,tpc:FSC-101.C-0_41_0,tpc:FSC-109.C-0_13_1,tpc:FSC-102.C-0_40_1,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-9_9_1,tpc:FSC-407.C-10_9_1,tpc:FSC-407.C-11_9_1,tpc:FSC-130.C-0_11_1,tpc:FSC-131.C-0_12_1,tpc:FSC-411.C-0_27_1,tpc:FSC-411.C-1_37_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="FIBER">
+    <featuretypeList>tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-1_16_2,tpc:FSC-300.C-3_11_1,tpc:FSC-300.C-2_11_1,tpc:FSC-300.C-0_10_2,tpc:FSC-319.C-0_12_1,tpc:FSC-320.C-0_13_1,tpc:FSC-323.C-0_1_1,tpc:FSC-324.C-1_2_1,tpc:FSC-324.C-0_1_1,tpc:FSC-403.C-6_45_1,tpc:FSC-403.C-7_45_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="LINEFEATURE">
+    <featuretypeList>tpc:FSC-115.C-0_46_1,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-1_46_1,tpc:FSC-115.C-1_46_2,tpc:FSC-115.C-1_56_1,tpc:FSC-115.C-0_47_1,tpc:FSC-115.C-0_47_2,tpc:FSC-115.C-0_48_1,tpc:FSC-115.C-0_48_2,tpc:FSC-102.C-0_30_2,tpc:FSC-102.C-0_40_1,tpc:FSC-117.C-0_27_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="22KVMAINHV">
+    <featuretypeList>tpc:FSC-117.C-0_27_2,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-503.C-0_1_2,tpc:FSC-118.C-0_50_2,tpc:FSC-114.C-3_60_2,tpc:FSC-114.C-2_24_2,tpc:FSC-114.C-0_24_2,tpc:FSC-114.C-1_34_2,tpc:FSC-106.C-0_4_2,tpc:FSC-115.C-0_48_2,tpc:FSC-105.C-0_39_2,tpc:FSC-411.C-0_8_2,tpc:FSC-411.C-1_18_2,tpc:FSC-107.C-0_26_2,tpc:FSC-107.C-2_36_2,tpc:FSC-120.C-0_52_2,tpc:FSC-101.C-0_51_1,tpc:FSC-119.C-0_62_2,tpc:FSC-108.C-0_45_2,tpc:FSC-108.C-1_55_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-1_37_2,tpc:FSC-100.C-0_60_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="SLS">
+    <featuretypeList>tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-115.C-3_50_1,tpc:FSC-115.C-3_51_1,tpc:FSC-115.C-2_50_2,tpc:FSC-115.C-2_51_2,tpc:FSC-115.C-7_55_1,tpc:FSC-115.C-7_58_1,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-8_9_2,tpc:FSC-203.C-0_37_1,tpc:FSC-203.C-0_38_1,tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-1_38_1,tpc:FSC-303.C-0_29_1,tpc:FSC-303.C-1_43_0,tpc:FSC-303.C-3_43_0,tpc:FSC-301.C-0_21_1,tpc:FSC-301.C-1_23_0,tpc:FSC-301.C-3_23_0,tpc:FSC-305.C-0_33_1,tpc:FSC-305.C-0_34_1,tpc:FSC-306.C-0_35_1,tpc:FSC-306.C-0_36_1,tpc:FSC-307.C-0_46_1,tpc:FSC-307.C-0_37_1,tpc:FSC-307.C-0_38_1,tpc:FSC-308.C-0_39_1,tpc:FSC-308.C-0_40_1,tpc:FSC-314.C-0_26_1,tpc:FSC-314.C-1_28_0,tpc:FSC-315.C-0_26_1,tpc:FSC-315.C-1_28_0,tpc:FSC-316.C-0_53_1,tpc:FSC-316.C-0_54_1,tpc:FSC-317.C-0_31_1,tpc:FSC-317.C-0_32_1,tpc:FSC-318.C-0_53_4,tpc:FSC-318.C-0_54_4,tpc:FSC-302.C-0_25_1,tpc:FSC-302.C-1_27_0,tpc:FSC-317.C-1_31_1,tpc:FSC-317.C-1_32_1,tpc:FSC-403.C-4_44_1,tpc:FSC-403.C-5_44_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-0_1_1,tpc:FSC-502.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="OMS">
+    <featuretypeList>tpc:FSC-101.C-0_41_0,tpc:FSC-503.C-0_1_2,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-0_1_1,tpc:FSC-411.C-1_37_2,tpc:FSC-411.C-1_37_1,tpc:FSC-411.C-1_18_2,tpc:FSC-411.C-1_18_1,tpc:FSC-411.C-1_17_2,tpc:FSC-411.C-1_17_1,tpc:FSC-411.C-0_8_2,tpc:FSC-411.C-0_8_1,tpc:FSC-411.C-0_7_2,tpc:FSC-411.C-0_7_1,tpc:FSC-411.C-0_27_2,tpc:FSC-411.C-0_27_1,tpc:FSC-407.C-8_9_2,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_10_1,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-1_27_2,tpc:FSC-402.C-1_16_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-0_27_2,tpc:FSC-131.C-0_12_2,tpc:FSC-131.C-0_12_1,tpc:FSC-130.C-0_11_2,tpc:FSC-130.C-0_11_1,tpc:FSC-122.C-1_34_1,tpc:FSC-122.C-1_33_2,tpc:FSC-122.C-1_33_1,tpc:FSC-122.C-0_24_1,tpc:FSC-122.C-0_23_2,tpc:FSC-122.C-0_23_1,tpc:FSC-120.C-0_52_2,tpc:FSC-120.C-0_52_1,tpc:FSC-120.C-0_42_2,tpc:FSC-120.C-0_42_1,tpc:FSC-119.C-0_62_2,tpc:FSC-119.C-0_61_2,tpc:FSC-118.C-0_50_2,tpc:FSC-118.C-0_49_2,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-0_48_2,tpc:FSC-115.C-0_48_1,tpc:FSC-115.C-0_47_2,tpc:FSC-115.C-0_47_1,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-0_46_1,tpc:FSC-114.C-3_60_2,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-2_24_2,tpc:FSC-114.C-2_24_1,tpc:FSC-114.C-2_23_2,tpc:FSC-114.C-2_23_1,tpc:FSC-114.C-2_21_2,tpc:FSC-114.C-2_21_1,tpc:FSC-114.C-2_21_0,tpc:FSC-114.C-1_34_2,tpc:FSC-114.C-1_34_1,tpc:FSC-114.C-1_33_2,tpc:FSC-114.C-1_33_1,tpc:FSC-114.C-0_24_2,tpc:FSC-114.C-0_24_1,tpc:FSC-114.C-0_23_2,tpc:FSC-114.C-0_23_1,tpc:FSC-114.C-0_21_2,tpc:FSC-114.C-0_21_1,tpc:FSC-109.C-0_13_3,tpc:FSC-109.C-0_13_2,tpc:FSC-109.C-0_13_1,tpc:FSC-108.C-1_55_2,tpc:FSC-108.C-1_54_2,tpc:FSC-108.C-0_45_2,tpc:FSC-108.C-0_44_2,tpc:FSC-107.C-2_36_2,tpc:FSC-107.C-2_36_1,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-0_27_1,tpc:FSC-107.C-0_26_2,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-0_25_2,tpc:FSC-107.C-0_25_1,tpc:FSC-106.C-0_4_1,tpc:FSC-106.C-0_3_1,tpc:FSC-106.C-0_2_2,tpc:FSC-106.C-0_2_1,tpc:FSC-106.C-0_1_2,tpc:FSC-106.C-0_1_1,tpc:FSC-102.C-0_40_1,tpc:FSC-102.C-0_30_2,tpc:FSC-101.C-0_51_1,tpc:FSC-101.C-0_51_0,tpc:FSC-101.C-0_41_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="LVPIPE">
+    <featuretypeList>tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-411.C-2_7_1,tpc:FSC-411.C-2_7_2,tpc:FSC-411.C-2_8_1,tpc:FSC-411.C-2_8_2,tpc:FSC-411.C-3_17_1,tpc:FSC-411.C-3_17_2,tpc:FSC-411.C-3_37_1,tpc:FSC-411.C-3_37_2,tpc:FSC-411.C-3_18_1,tpc:FSC-411.C-3_18_2,tpc:FSC-411.C-7_7_1,tpc:FSC-411.C-7_7_2,tpc:FSC-411.C-7_8_1,tpc:FSC-407.C-2_9_1,tpc:FSC-407.C-2_9_2,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-3_19_1,tpc:FSC-407.C-3_19_2,tpc:FSC-407.C-3_20_1,tpc:FSC-407.C-7_10_1,tpc:FSC-407.C-7_9_1,tpc:FSC-407.C-7_9_2,tpc:FSC-202.C-1_35_0,tpc:FSC-202.C-0_35_1,tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-0_37_1,tpc:FSC-206.C-2_1_0,tpc:FSC-206.C-1_1_1,tpc:FSC-206.C-0_1_1,tpc:FSC-207.C-1_1_0,tpc:FSC-207.C-0_1_1,tpc:FSC-209.C-0_45_1,tpc:FSC-216.C-2_1_0,tpc:FSC-216.C-1_1_1,tpc:FSC-216.C-0_1_1,tpc:FSC-401.C-1_11_0,tpc:FSC-401.C-0_11_1,tpc:FSC-401.C-2_11_1,tpc:FSC-401.C-3_11_0,tpc:FSC-403.C-0_11_1,tpc:FSC-403.C-1_11_1,tpc:FSC-403.C-0_3_1,tpc:FSC-403.C-1_3_1,tpc:FSC-403.C-0_22_1,tpc:FSC-403.C-0_23_1,tpc:FSC-403.C-0_24_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-420.C-0_54_3,tpc:FSC-420.C-1_2_1,tpc:FSC-421.C-1_2_1,tpc:FSC-421.C-0_1_3,tpc:FSC-424.C-0_1_1,tpc:FSC-503.C-0_1_2,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="POLE">
+    <featuretypeList>tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-1_9_1,tpc:FSC-407.C-1_9_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="22KVHVOH">
+    <featuretypeList>tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-1_56_1,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-9_10_1,tpc:FSC-117.C-0_27_2,tpc:FSC-118.C-0_50_2,tpc:FSC-108.C-1_55_2,tpc:FSC-119.C-0_62_2,tpc:FSC-100.C-0_60_2,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="11KVHVUG">
+    <featuretypeList>tpc:FSC-113.C-0_4_1,tpc:FSC-122.C-0_23_1,tpc:FSC-122.C-0_23_2,tpc:FSC-122.C-1_33_1,tpc:FSC-122.C-1_33_2,tpc:FSC-140.C-1_52_0,tpc:FSC-140.C-1_52_1,tpc:FSC-140.C-2_52_2,tpc:FSC-140.C-2_52_1,tpc:FSC-140.C-0_52_2,tpc:FSC-140.C-0_52_1,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-411.C-1_17_1,tpc:FSC-411.C-1_17_2,tpc:FSC-411.C-1_37_1,tpc:FSC-411.C-1_37_2,tpc:FSC-411.C-0_7_1,tpc:FSC-411.C-0_7_1,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-0_25_1,tpc:FSC-107.C-0_25_2,tpc:FSC-107.C-0_27_1,tpc:FSC-105.C-0_29_1,tpc:FSC-105.C-0_29_2,tpc:FSC-101.C-0_41_0,tpc:FSC-101.C-0_41_1,tpc:FSC-130.C-0_11_1,tpc:FSC-130.C-0_11_2,tpc:FSC-108.C-0_44_2,tpc:FSC-108.C-1_54_2,tpc:FSC-119.C-0_61_2,tpc:FSC-115.C-0_47_1,tpc:FSC-115.C-0_47_2,tpc:FSC-118.C-0_49_2,tpc:FSC-403.C-2_14_1,tpc:FSC-403.C-2_15_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-403.C-1_4_1,tpc:FSC-403.C-1_13_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-114.C-1_33_1,tpc:FSC-114.C-1_33_2,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-3_60_2,tpc:FSC-114.C-2_23_1,tpc:FSC-114.C-0_23_1,tpc:FSC-114.C-0_23_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="11KVMAINHV">
+    <featuretypeList>tpc:FSC-117.C-0_27_2,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-503.C-0_1_2,tpc:FSC-118.C-0_49_2,tpc:FSC-114.C-3_60_2,tpc:FSC-114.C-2_23_2,tpc:FSC-114.C-1_33_2,tpc:FSC-106.C-2_2_2,tpc:FSC-106.C-0_2_2,tpc:FSC-115.C-0_47_2,tpc:FSC-122.C-0_23_2,tpc:FSC-122.C-1_33_2,tpc:FSC-105.C-0_29_2,tpc:FSC-114.C-2_21_2,tpc:FSC-114.C-0_21_2,tpc:FSC-106.C-2_1_2,tpc:FSC-106.C-0_1_2,tpc:FSC-106.C-4_1_2,tpc:FSC-115.C-6_47_2,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-1_46_2,tpc:FSC-411.C-0_7_2,tpc:FSC-411.C-1_17_2,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-0_25_2,tpc:FSC-116.C-0_14_2,tpc:FSC-120.C-0_42_2,tpc:FSC-101.C-0_41_1,tpc:FSC-109.C-0_13_2,tpc:FSC-102.C-0_30_2,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-8_9_2,tpc:FSC-407.C-9_9_2,tpc:FSC-119.C-0_61_2,tpc:FSC-130.C-0_11_2,tpc:FSC-131.C-0_12_2,tpc:FSC-108.C-0_44_2,tpc:FSC-108.C-1_54_2,tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-100.C-0_59_1,tpc:FSC-411.C-0_27_2,tpc:FSC-411.C-1_37_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="ALL">
+    <featuretypeList>tpc:FSC-100.C-0_59_1,tpc:FSC-100.C-0_60_2,tpc:FSC-101.C-0_41_0,tpc:FSC-101.C-0_41_1,tpc:FSC-101.C-0_51_0,tpc:FSC-101.C-0_51_1,tpc:FSC-102.C-0_30_2,tpc:FSC-102.C-0_40_1,tpc:FSC-105.C-0_29_1,tpc:FSC-105.C-0_29_2,tpc:FSC-105.C-0_39_1,tpc:FSC-105.C-0_39_2,tpc:FSC-105.C-0_60_2,tpc:FSC-106.C-0_19_2,tpc:FSC-106.C-0_1_1,tpc:FSC-106.C-0_1_2,tpc:FSC-106.C-0_24_2,tpc:FSC-106.C-0_2_1,tpc:FSC-106.C-0_2_2,tpc:FSC-106.C-0_3_1,tpc:FSC-106.C-0_4_1,tpc:FSC-106.C-0_4_2,tpc:FSC-106.C-2_1_1,tpc:FSC-106.C-2_1_2,tpc:FSC-106.C-2_2_1,tpc:FSC-106.C-2_2_2,tpc:FSC-106.C-2_3_1,tpc:FSC-106.C-2_4_1,tpc:FSC-106.C-4_1_1,tpc:FSC-106.C-4_1_2,tpc:FSC-107.C-0_25_1,tpc:FSC-107.C-0_25_2,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-0_26_2,tpc:FSC-107.C-0_27_1,tpc:FSC-107.C-2_0_1,tpc:FSC-107.C-2_25_1,tpc:FSC-107.C-2_26_1,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-2_36_1,tpc:FSC-107.C-2_36_2,tpc:FSC-107.C-2_37_1,tpc:FSC-108.C-0_44_2,tpc:FSC-108.C-0_45_2,tpc:FSC-108.C-1_0_2,tpc:FSC-108.C-1_54_2,tpc:FSC-108.C-1_55_2,tpc:FSC-109.C-0_13_1,tpc:FSC-109.C-0_13_2,tpc:FSC-109.C-0_13_3,tpc:FSC-113.C-0_4_1,tpc:FSC-114.C-0_21_1,tpc:FSC-114.C-0_21_2,tpc:FSC-114.C-0_23_1,tpc:FSC-114.C-0_23_2,tpc:FSC-114.C-0_24_1,tpc:FSC-114.C-0_24_2,tpc:FSC-114.C-1_23_1,tpc:FSC-114.C-1_23_2,tpc:FSC-114.C-1_24_1,tpc:FSC-114.C-1_24_2,tpc:FSC-114.C-1_33_1,tpc:FSC-114.C-1_33_2,tpc:FSC-114.C-1_34_1,tpc:FSC-114.C-1_34_2,tpc:FSC-114.C-2_21_0,tpc:FSC-114.C-2_21_1,tpc:FSC-114.C-2_21_2,tpc:FSC-114.C-2_23_1,tpc:FSC-114.C-2_23_2,tpc:FSC-114.C-2_24_1,tpc:FSC-114.C-2_24_2,tpc:FSC-114.C-2_60_1,tpc:FSC-114.C-2_60_2,tpc:FSC-114.C-3_33_1,tpc:FSC-114.C-3_33_2,tpc:FSC-114.C-3_34_1,tpc:FSC-114.C-3_34_2,tpc:FSC-114.C-3_60_1,tpc:FSC-114.C-3_60_2,tpc:FSC-115.C-0_46_1,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-0_47_1,tpc:FSC-115.C-0_47_2,tpc:FSC-115.C-0_48_1,tpc:FSC-115.C-0_48_2,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-1_46_1,tpc:FSC-115.C-1_46_2,tpc:FSC-115.C-1_56_1,tpc:FSC-115.C-1_57_1,tpc:FSC-115.C-2_50_2,tpc:FSC-115.C-2_51_2,tpc:FSC-115.C-3_50_1,tpc:FSC-115.C-3_51_1,tpc:FSC-115.C-4_13_2,tpc:FSC-115.C-4_14_2,tpc:FSC-115.C-4_51_2,tpc:FSC-115.C-4_60_2,tpc:FSC-115.C-5_13_1,tpc:FSC-115.C-5_14_1,tpc:FSC-115.C-5_51_1,tpc:FSC-115.C-5_60_1,tpc:FSC-115.C-6_13_1,tpc:FSC-115.C-6_14_1,tpc:FSC-115.C-6_47_1,tpc:FSC-115.C-6_47_2,tpc:FSC-115.C-6_48_1,tpc:FSC-115.C-6_51_1,tpc:FSC-115.C-6_56_1,tpc:FSC-115.C-6_58_1,tpc:FSC-115.C-6_60_1,tpc:FSC-115.C-7_55_1,tpc:FSC-115.C-7_58_1,tpc:FSC-115.C-8_47_1,tpc:FSC-115.C-8_47_2,tpc:FSC-115.C-8_48_1,tpc:FSC-116.C-0_14_1,tpc:FSC-116.C-0_14_2,tpc:FSC-117.C-0_27_2,tpc:FSC-118.C-0_49_2,tpc:FSC-118.C-0_50_2,tpc:FSC-119.C-0_61_2,tpc:FSC-119.C-0_62_2,tpc:FSC-120.C-0_42_1,tpc:FSC-120.C-0_42_2,tpc:FSC-120.C-0_52_1,tpc:FSC-120.C-0_52_2,tpc:FSC-122.C-0_23_1,tpc:FSC-122.C-0_23_2,tpc:FSC-122.C-0_24_1,tpc:FSC-122.C-1_23_1,tpc:FSC-122.C-1_24_1,tpc:FSC-122.C-1_33_1,tpc:FSC-122.C-1_33_2,tpc:FSC-122.C-1_34_1,tpc:FSC-130.C-0_11_1,tpc:FSC-130.C-0_11_2,tpc:FSC-131.C-0_12_1,tpc:FSC-131.C-0_12_2,tpc:FSC-140.C-0_51_1,tpc:FSC-140.C-0_51_2,tpc:FSC-140.C-0_52_1,tpc:FSC-140.C-0_52_2,tpc:FSC-140.C-0_54_1,tpc:FSC-140.C-0_54_2,tpc:FSC-140.C-0_60_1,tpc:FSC-140.C-0_60_2,tpc:FSC-140.C-1_52_0,tpc:FSC-140.C-1_52_1,tpc:FSC-140.C-1_54_0,tpc:FSC-140.C-1_54_1,tpc:FSC-140.C-2_51_1,tpc:FSC-140.C-2_51_2,tpc:FSC-140.C-2_52_1,tpc:FSC-140.C-2_52_2,tpc:FSC-140.C-2_54_1,tpc:FSC-140.C-2_54_2,tpc:FSC-140.C-3_54_0,tpc:FSC-140.C-4_54_1,tpc:FSC-200.C-0_28_1,tpc:FSC-200.C-0_29_1,tpc:FSC-200.C-0_30_1,tpc:FSC-200.C-0_60_1,tpc:FSC-201.C-0_31_1,tpc:FSC-201.C-0_40_1,tpc:FSC-201.C-1_33_0,tpc:FSC-201.C-1_40_0,tpc:FSC-202.C-0_35_1,tpc:FSC-202.C-0_36_1,tpc:FSC-202.C-0_56_1,tpc:FSC-202.C-1_13_0,tpc:FSC-202.C-1_35_0,tpc:FSC-202.C-1_36_0,tpc:FSC-202.C-1_53_0,tpc:FSC-202.C-1_56_0,tpc:FSC-202.C-1_60_0,tpc:FSC-203.C-0_37_1,tpc:FSC-203.C-0_38_1,tpc:FSC-203.C-1_13_1,tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-1_38_1,tpc:FSC-204.C-0_49_1,tpc:FSC-205.C-0_39_1,tpc:FSC-205.C-2_41_1,tpc:FSC-206.C-0_1_1,tpc:FSC-206.C-1_1_1,tpc:FSC-206.C-2_1_0,tpc:FSC-207.C-0_1_1,tpc:FSC-207.C-1_1_0,tpc:FSC-207.C-2_1_1,tpc:FSC-208.C-0_43_1,tpc:FSC-208.C-0_44_1,tpc:FSC-209.C-0_45_1,tpc:FSC-209.C-0_46_1,tpc:FSC-209.C-1_45_1,tpc:FSC-209.C-1_46_1,tpc:FSC-210.C-4_42_0,tpc:FSC-211.C-0_32_1,tpc:FSC-212.C-1_34_0,tpc:FSC-213.C-0_31_1,tpc:FSC-213.C-1_33_0,tpc:FSC-215.C-0_45_1,tpc:FSC-216.C-0_1_1,tpc:FSC-216.C-1_1_1,tpc:FSC-216.C-2_1_0,tpc:FSC-217.C-0_11_1,tpc:FSC-300.C-0_10_2,tpc:FSC-300.C-2_11_1,tpc:FSC-300.C-3_11_1,tpc:FSC-301.C-0_21_1,tpc:FSC-301.C-0_60_1,tpc:FSC-301.C-1_23_0,tpc:FSC-301.C-1_60_0,tpc:FSC-301.C-3_23_0,tpc:FSC-301.C-3_60_0,tpc:FSC-302.C-0_25_1,tpc:FSC-302.C-1_27_0,tpc:FSC-303.C-0_29_1,tpc:FSC-303.C-1_43_0,tpc:FSC-303.C-3_43_0,tpc:FSC-305.C-0_33_1,tpc:FSC-305.C-0_34_1,tpc:FSC-306.C-0_35_1,tpc:FSC-306.C-0_36_1,tpc:FSC-307.C-0_37_1,tpc:FSC-307.C-0_38_1,tpc:FSC-307.C-0_46_1,tpc:FSC-308.C-0_39_1,tpc:FSC-308.C-0_40_1,tpc:FSC-311.C-0_45_1,tpc:FSC-311.C-0_60_1,tpc:FSC-311.C-0_63_1,tpc:FSC-311.C-1_45_0,tpc:FSC-311.C-1_45_1,tpc:FSC-311.C-1_60_1,tpc:FSC-311.C-1_63_1,tpc:FSC-314.C-0_26_1,tpc:FSC-314.C-1_28_0,tpc:FSC-315.C-0_26_1,tpc:FSC-315.C-1_28_0,tpc:FSC-316.C-0_53_1,tpc:FSC-316.C-0_54_1,tpc:FSC-317.C-0_31_1,tpc:FSC-317.C-0_32_1,tpc:FSC-317.C-1_31_1,tpc:FSC-317.C-1_32_1,tpc:FSC-318.C-0_53_4,tpc:FSC-318.C-0_54_4,tpc:FSC-319.C-0_12_1,tpc:FSC-320.C-0_13_1,tpc:FSC-323.C-0_1_1,tpc:FSC-324.C-0_1_1,tpc:FSC-324.C-1_2_1,tpc:FSC-401.C-0_11_1,tpc:FSC-401.C-0_4_2,tpc:FSC-401.C-0_60_1,tpc:FSC-401.C-1_11_0,tpc:FSC-401.C-1_4_0,tpc:FSC-401.C-2_11_1,tpc:FSC-401.C-2_4_1,tpc:FSC-401.C-3_11_0,tpc:FSC-401.C-3_4_1,tpc:FSC-401.C-5_4_1,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-1_16_2,tpc:FSC-402.C-1_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-1_5_2,tpc:FSC-402.C-1_6_2,tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-2_37_2,tpc:FSC-403.C-0_11_1,tpc:FSC-403.C-0_13_1,tpc:FSC-403.C-0_22_1,tpc:FSC-403.C-0_23_1,tpc:FSC-403.C-0_24_1,tpc:FSC-403.C-0_3_1,tpc:FSC-403.C-0_4_1,tpc:FSC-403.C-1_11_1,tpc:FSC-403.C-1_13_1,tpc:FSC-403.C-1_14_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-403.C-1_3_1,tpc:FSC-403.C-1_4_1,tpc:FSC-403.C-1_60_1,tpc:FSC-403.C-2_13_1,tpc:FSC-403.C-2_14_1,tpc:FSC-403.C-2_15_1,tpc:FSC-403.C-2_20_1,tpc:FSC-403.C-2_60_1,tpc:FSC-403.C-4_11_1,tpc:FSC-403.C-4_44_1,tpc:FSC-403.C-5_11_1,tpc:FSC-403.C-5_44_1,tpc:FSC-403.C-5_60_1,tpc:FSC-403.C-6_45_1,tpc:FSC-403.C-7_45_1,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-10_9_1,tpc:FSC-407.C-11_9_1,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-1_9_1,tpc:FSC-407.C-1_9_2,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-2_9_1,tpc:FSC-407.C-2_9_2,tpc:FSC-407.C-3_19_1,tpc:FSC-407.C-3_19_2,tpc:FSC-407.C-3_20_1,tpc:FSC-407.C-7_10_1,tpc:FSC-407.C-7_9_1,tpc:FSC-407.C-7_9_2,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-8_9_2,tpc:FSC-407.C-9_10_1,tpc:FSC-407.C-9_9_1,tpc:FSC-407.C-9_9_2,tpc:FSC-411.C-0_27_1,tpc:FSC-411.C-0_27_2,tpc:FSC-411.C-0_7_1,tpc:FSC-411.C-0_7_2,tpc:FSC-411.C-0_8_1,tpc:FSC-411.C-0_8_2,tpc:FSC-411.C-1_17_1,tpc:FSC-411.C-1_17_2,tpc:FSC-411.C-1_18_1,tpc:FSC-411.C-1_18_2,tpc:FSC-411.C-1_37_1,tpc:FSC-411.C-1_37_2,tpc:FSC-411.C-1_7_1,tpc:FSC-411.C-1_7_2,tpc:FSC-411.C-1_8_1,tpc:FSC-411.C-1_8_2,tpc:FSC-411.C-2_27_1,tpc:FSC-411.C-2_27_2,tpc:FSC-411.C-2_7_1,tpc:FSC-411.C-2_7_2,tpc:FSC-411.C-2_8_1,tpc:FSC-411.C-2_8_2,tpc:FSC-411.C-3_17_1,tpc:FSC-411.C-3_17_2,tpc:FSC-411.C-3_18_1,tpc:FSC-411.C-3_18_2,tpc:FSC-411.C-3_37_1,tpc:FSC-411.C-3_37_2,tpc:FSC-411.C-7_7_1,tpc:FSC-411.C-7_7_2,tpc:FSC-411.C-7_8_1,tpc:FSC-420.C-0_54_3,tpc:FSC-420.C-1_2_1,tpc:FSC-421.C-0_1_3,tpc:FSC-421.C-1_2_1,tpc:FSC-423.C-1_4_1,tpc:FSC-424.C-0_1_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-502.C-1_2_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="LVUG">
+    <featuretypeList>tpc:FSC-217.C-0_11_1,tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-2_9_1,tpc:FSC-407.C-2_9_2,tpc:FSC-407.C-3_19_1,tpc:FSC-407.C-3_19_2,tpc:FSC-407.C-3_20_1,tpc:FSC-407.C-7_10_1,tpc:FSC-407.C-7_9_1,tpc:FSC-407.C-7_9_2,tpc:FSC-411.C-7_7_1,tpc:FSC-411.C-7_7_2,tpc:FSC-411.C-7_8_1,tpc:FSC-115.C-8_47_1,tpc:FSC-115.C-8_47_2,tpc:FSC-115.C-8_48_1,tpc:FSC-115.C-6_47_1,tpc:FSC-115.C-6_47_2,tpc:FSC-115.C-2_51_2,tpc:FSC-115.C-3_51_1,tpc:FSC-115.C-4_51_2,tpc:FSC-115.C-5_51_1,tpc:FSC-115.C-6_56_1,tpc:FSC-115.C-7_55_1,tpc:FSC-200.C-0_28_1,tpc:FSC-200.C-0_29_1,tpc:FSC-201.C-0_31_1,tpc:FSC-201.C-1_33_0,tpc:FSC-202.C-1_35_0,tpc:FSC-202.C-0_35_1,tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-0_37_1,tpc:FSC-206.C-2_1_0,tpc:FSC-206.C-1_1_1,tpc:FSC-206.C-0_1_1,tpc:FSC-207.C-1_1_0,tpc:FSC-207.C-0_1_1,tpc:FSC-208.C-0_43_1,tpc:FSC-209.C-0_45_1,tpc:FSC-210.C-4_42_0,tpc:FSC-213.C-0_31_1,tpc:FSC-213.C-1_33_0,tpc:FSC-215.C-0_45_1,tpc:FSC-216.C-2_1_0,tpc:FSC-216.C-0_1_1,tpc:FSC-216.C-1_1_1,tpc:FSC-301.C-1_23_0,tpc:FSC-301.C-0_21_1,tpc:FSC-301.C-1_23_0,tpc:FSC-301.C-3_23_0,tpc:FSC-303.C-0_29_1,tpc:FSC-303.C-1_43_0,tpc:FSC-303.C-3_43_0,tpc:FSC-307.C-0_46_1,tpc:FSC-307.C-0_37_1,tpc:FSC-302.C-0_25_1,tpc:FSC-302.C-1_27_0,tpc:FSC-305.C-0_33_1,tpc:FSC-306.C-0_35_1,tpc:FSC-308.C-0_39_1,tpc:FSC-316.C-0_53_1,tpc:FSC-317.C-1_31_1,tpc:FSC-318.C-0_53_4,tpc:FSC-403.C-2_20_1,tpc:FSC-403.C-2_15_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-501.C-0_1_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="OHS">
+    <featuretypeList>tpc:FSC-106.C-2_1_1,tpc:FSC-106.C-2_1_2,tpc:FSC-106.C-4_1_1,tpc:FSC-106.C-4_1_2,tpc:FSC-106.C-0_1_1,tpc:FSC-106.C-0_1_2,tpc:FSC-106.C-2_3_1,tpc:FSC-106.C-0_3_1,tpc:FSC-140.C-2_51_1,tpc:FSC-140.C-2_51_2,tpc:FSC-140.C-0_51_1,tpc:FSC-140.C-0_51_2,tpc:FSC-402.C-2_15_2,tpc:FSC-403.C-2_15_1,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-407.C-0_9_1,tpc:FSC-407.C-0_9_2,tpc:FSC-407.C-0_10_1,tpc:FSC-407.C-1_19_1,tpc:FSC-407.C-1_19_2,tpc:FSC-407.C-1_20_1,tpc:FSC-407.C-9_9_1,tpc:FSC-407.C-9_9_2,tpc:FSC-407.C-9_10_1,tpc:FSC-407.C-10_9_1,tpc:FSC-407.C-11_9_1,tpc:FSC-118.C-0_49_2,tpc:FSC-118.C-0_50_2,tpc:FSC-407.C-8_9_1,tpc:FSC-407.C-8_9_2,tpc:FSC-114.C-0_21_1,tpc:FSC-114.C-0_21_2,tpc:FSC-114.C-2_21_0,tpc:FSC-114.C-2_21_1,tpc:FSC-114.C-2_21_2,tpc:FSC-115.C-0_46_1,tpc:FSC-115.C-0_46_2,tpc:FSC-115.C-0_56_1,tpc:FSC-115.C-1_46_1,tpc:FSC-115.C-1_46_2,tpc:FSC-115.C-1_56_1,tpc:FSC-115.C-2_50_2,tpc:FSC-115.C-2_51_2,tpc:FSC-115.C-3_51_1,tpc:FSC-115.C-3_50_1,tpc:FSC-115.C-7_58_1,tpc:FSC-115.C-7_55_1,tpc:FSC-107.C-2_35_1,tpc:FSC-107.C-2_35_2,tpc:FSC-107.C-0_25_1,tpc:FSC-107.C-0_25_2,tpc:FSC-107.C-2_36_1,tpc:FSC-107.C-2_36_2,tpc:FSC-107.C-0_26_1,tpc:FSC-107.C-0_26_2,tpc:FSC-107.C-2_37_1,tpc:FSC-107.C-0_27_1,tpc:FSC-117.C-0_27_2,tpc:FSC-120.C-0_42_1,tpc:FSC-120.C-0_42_2,tpc:FSC-120.C-0_52_1,tpc:FSC-120.C-0_52_2,tpc:FSC-130.C-0_11_1,tpc:FSC-130.C-0_11_2,tpc:FSC-131.C-0_12_1,tpc:FSC-131.C-0_12_2,tpc:FSC-109.C-0_13_1,tpc:FSC-109.C-0_13_2,tpc:FSC-109.C-0_13_3,tpc:FSC-116.C-0_14_1,tpc:FSC-116.C-0_14_2,tpc:FSC-102.C-0_30_2,tpc:FSC-100.C-0_59_1,tpc:FSC-100.C-0_60_2,tpc:FSC-502.C-0_1_1,tpc:FSC-502.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-503.C-0_1_2,tpc:FSC-999.C-0_62_1,tpc:FSC-200.C-0_30_1,tpc:FSC-200.C-0_28_1,tpc:FSC-202.C-1_36_0,tpc:FSC-202.C-0_36_1,tpc:FSC-203.C-1_38_1,tpc:FSC-203.C-0_38_1,tpc:FSC-205.C-2_41_1,tpc:FSC-205.C-0_39_1,tpc:FSC-208.C-0_44_1,tpc:FSC-209.C-0_46_1,tpc:FSC-211.C-0_32_1,tpc:FSC-212.C-1_34_0,tpc:FSC-303.C-1_43_0,tpc:FSC-303.C-0_29_1,tpc:FSC-303.C-3_43_0,tpc:FSC-305.C-0_34_1,tpc:FSC-306.C-0_36_1,tpc:FSC-307.C-0_46_1,tpc:FSC-307.C-0_38_1,tpc:FSC-308.C-0_40_1,tpc:FSC-314.C-0_26_1,tpc:FSC-314.C-1_28_0,tpc:FSC-315.C-0_26_1,tpc:FSC-315.C-1_28_0,tpc:FSC-316.C-0_54_1,tpc:FSC-317.C-0_32_1,tpc:FSC-317.C-1_32_1,tpc:FSC-318.C-0_54_4</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="HVPIPE">
+    <featuretypeList>tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-411.C-2_7_1,tpc:FSC-411.C-2_7_2,tpc:FSC-411.C-2_27_1,tpc:FSC-411.C-2_27_2,tpc:FSC-411.C-2_8_1,tpc:FSC-411.C-2_8_2,tpc:FSC-411.C-3_17_1,tpc:FSC-411.C-3_17_2,tpc:FSC-411.C-3_37_1,tpc:FSC-411.C-3_37_2,tpc:FSC-411.C-3_18_1,tpc:FSC-411.C-3_18_2,tpc:FSC-411.C-7_7_1,tpc:FSC-411.C-7_7_2,tpc:FSC-411.C-7_8_1,tpc:FSC-407.C-2_9_1,tpc:FSC-407.C-2_9_2,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-3_19_1,tpc:FSC-407.C-3_19_2,tpc:FSC-407.C-3_20_1,tpc:FSC-407.C-7_10_1,tpc:FSC-407.C-7_9_1,tpc:FSC-407.C-7_9_2,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-1_2_1,tpc:FSC-502.C-0_1_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-420.C-0_54_3,tpc:FSC-420.C-1_2_1,tpc:FSC-423.C-1_4_1,tpc:FSC-401.C-1_4_0,tpc:FSC-401.C-3_4_1,tpc:FSC-401.C-0_4_2,tpc:FSC-401.C-2_4_1,tpc:FSC-401.C-5_4_1,tpc:FSC-403.C-0_4_1,tpc:FSC-403.C-0_13_1,tpc:FSC-403.C-0_3_1,tpc:FSC-403.C-1_3_1,tpc:FSC-403.C-1_4_1,tpc:FSC-403.C-1_13_1,tpc:FSC-403.C-0_22_1,tpc:FSC-403.C-0_23_1,tpc:FSC-403.C-0_24_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-503.C-0_1_2,tpc:FSC-421.C-0_1_3,tpc:FSC-421.C-1_2_1,tpc:FSC-424.C-0_1_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+  <FeatureTypeCollector name="HLVPIPE">
+    <featuretypeList>tpc:FSC-402.C-2_15_2,tpc:FSC-402.C-0_5_2,tpc:FSC-402.C-1_15_2,tpc:FSC-402.C-2_37_2,tpc:FSC-402.C-0_27_2,tpc:FSC-402.C-1_37_2,tpc:FSC-402.C-2_16_2,tpc:FSC-402.C-0_6_2,tpc:FSC-402.C-1_16_2,tpc:FSC-411.C-2_7_1,tpc:FSC-411.C-2_7_2,tpc:FSC-411.C-2_27_1,tpc:FSC-411.C-2_27_2,tpc:FSC-411.C-2_8_1,tpc:FSC-411.C-2_8_2,tpc:FSC-411.C-3_17_1,tpc:FSC-411.C-3_17_2,tpc:FSC-411.C-3_37_1,tpc:FSC-411.C-3_37_2,tpc:FSC-411.C-3_18_1,tpc:FSC-411.C-3_18_2,tpc:FSC-411.C-7_7_1,tpc:FSC-411.C-7_7_2,tpc:FSC-411.C-7_8_1,tpc:FSC-407.C-2_9_1,tpc:FSC-407.C-2_9_2,tpc:FSC-407.C-2_10_1,tpc:FSC-407.C-3_19_1,tpc:FSC-407.C-3_19_2,tpc:FSC-407.C-3_20_1,tpc:FSC-407.C-7_10_1,tpc:FSC-407.C-7_9_1,tpc:FSC-407.C-7_9_2,tpc:FSC-202.C-1_35_0,tpc:FSC-202.C-0_35_1,tpc:FSC-203.C-1_37_1,tpc:FSC-203.C-0_37_1,tpc:FSC-206.C-2_1_0,tpc:FSC-206.C-1_1_1,tpc:FSC-206.C-0_1_1,tpc:FSC-207.C-1_1_0,tpc:FSC-209.C-0_45_1,tpc:FSC-216.C-2_1_0,tpc:FSC-216.C-1_1_1,tpc:FSC-216.C-0_1_1,tpc:FSC-401.C-0_11_1,tpc:FSC-401.C-0_4_2,tpc:FSC-401.C-1_11_0,tpc:FSC-401.C-3_11_0,tpc:FSC-401.C-2_11_1,tpc:FSC-401.C-1_4_0,tpc:FSC-401.C-3_4_1,tpc:FSC-401.C-2_4_1,tpc:FSC-401.C-5_4_1,tpc:FSC-403.C-0_3_1,tpc:FSC-403.C-0_4_1,tpc:FSC-403.C-0_13_1,tpc:FSC-403.C-1_3_1,tpc:FSC-403.C-1_11_1,tpc:FSC-403.C-1_4_1,tpc:FSC-403.C-1_13_1,tpc:FSC-403.C-0_22_1,tpc:FSC-403.C-0_23_1,tpc:FSC-403.C-0_24_1,tpc:FSC-403.C-1_22_1,tpc:FSC-403.C-1_23_1,tpc:FSC-403.C-1_24_1,tpc:FSC-421.C-0_1_3,tpc:FSC-421.C-1_2_1,tpc:FSC-423.C-1_4_1,tpc:FSC-424.C-0_1_1,tpc:FSC-999.C-0_62_1,tpc:FSC-502.C-0_1_1,tpc:FSC-502.C-1_2_1,tpc:FSC-501.C-0_1_1,tpc:FSC-501.C-1_2_1,tpc:FSC-420.C-0_54_3,tpc:FSC-420.C-1_2_1</featuretypeList>
+    <featuretypeList/>
+  </FeatureTypeCollector>
+</FeatureClassificationRules>
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