[osmosis] 05/28: Imported Upstream version 0.43.1

Bas Couwenberg sebastic at xs4all.nl
Sun Apr 6 01:47:06 UTC 2014


This is an automated email from the git hooks/post-receive script.

sebastic-guest pushed a commit to branch master
in repository osmosis.

commit f0f4c3d46956328f6f3069ebf353b0d652469035
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sun Mar 16 15:15:55 2014 +0100

    Imported Upstream version 0.43.1
---
 .gitignore                                         |    3 +-
 README                                             |   51 +-
 apidb/.checkstyle                                  |   10 -
 apidb/.classpath                                   |   40 -
 apidb/.gitignore                                   |    5 -
 apidb/.project                                     |   23 -
 apidb/build.xml                                    |    7 -
 apidb/ivy.xml                                      |   40 -
 .../osmosis/apidb/common/DatabaseContext2.java     |  398 -
 .../common/PostgresqlIdentityValueLoader2.java     |   57 -
 .../osmosis/apidb/v0_6/ApidbChangeReader.java      |  124 -
 .../osmosis/apidb/v0_6/ApidbChangeWriter.java      |   83 -
 .../osmosis/apidb/v0_6/ApidbCurrentReader.java     |  109 -
 .../osmosis/apidb/v0_6/ApidbFileReplicator.java    |   96 -
 .../apidb/v0_6/ApidbFileReplicatorFactory.java     |   47 -
 .../osmosis/apidb/v0_6/ApidbReader.java            |  114 -
 .../osmosis/apidb/v0_6/ApidbVersionConstants.java  |   31 -
 .../osmosis/apidb/v0_6/ApidbWriter.java            | 1235 ---
 .../osmosis/apidb/v0_6/impl/EntityDao.java         |  541 --
 .../v0_6/impl/FileReplicationDestination.java      |  188 -
 .../v0_6/impl/FileReplicationStatePersistor.java   |   57 -
 .../apidb/v0_6/impl/ReplicationDestination.java    |   12 -
 .../osmosis/apidb/v0_6/impl/ReplicationState.java  |  326 -
 .../apidb/v0_6/impl/ReplicationStatePersister.java |   33 -
 .../osmosis/apidb/v0_6/impl/Replicator.java        |  317 -
 .../osmosis/apidb/v0_6/impl/TimeDao.java           |   37 -
 .../osmosis/apidb/v0_6/impl/TransactionDao.java    |   53 -
 .../apidb/v0_6/impl/TransactionSnapshotLoader.java |   15 -
 .../apidb/v0_6/ApidbFileReplicatorTest.java        |  111 -
 .../v0_6/impl/MockReplicationDestination.java      |  104 -
 .../v0_6/impl/MockTransactionSnapshotLoader.java   |   33 -
 .../osmosis/apidb/v0_6/impl/ReplicatorTest.java    |  306 -
 .../data/template/v0_6/db-changeset-expected.osm   |   44 -
 .../resources/data/template/v0_6/db-snapshot-b.osm |   46 -
 .../resources/data/template/v0_6/db-snapshot.osm   |   46 -
 areafilter/.checkstyle                             |   10 -
 areafilter/.classpath                              |   28 -
 areafilter/.gitignore                              |    5 -
 areafilter/.project                                |   23 -
 areafilter/build.xml                               |    7 -
 areafilter/ivy.xml                                 |   38 -
 .../osmosis/areafilter/v0_6/AreaFilter.java        |  650 --
 .../v0_6/AreaFilterTaskManagerFactory.java         |   48 -
 .../areafilter/v0_6/BoundingBoxFilterTest.java     |  181 -
 .../osmosis/areafilter/v0_6/PolygonFilterTest.java |  156 -
 .../test/task/v0_6/SinkEntityInspector.java        |   81 -
 .../resources/data/template/v0_6/areafilter-in.osm |   49 -
 .../v0_6/areafilter-out-cascadingrelations.osm     |   34 -
 .../v0_6/areafilter-out-clipincompleteentities.osm |   27 -
 .../v0_6/areafilter-out-completerelations.osm      |   49 -
 .../template/v0_6/areafilter-out-completeways.osm  |   37 -
 .../data/template/v0_6/areafilter-out-standard.osm |   29 -
 .../data/template/v0_6/areafilter-out-whole.osm    |   44 -
 build-support/.gitignore                           |    2 +
 build-support/.project                             |   11 -
 build-support/checkstyle.xml                       |   11 +-
 build-support/config/ant-build-common.properties   |   20 -
 build-support/config/ant-build.properties          |    1 -
 build-support/create_changes_xml.pl                |   37 -
 build-support/hudson_post_build.sh                 |   23 -
 build-support/hudson_pre_build.sh                  |    5 -
 build-support/script/build-init.xml                |   88 -
 build-support/script/build-ivy-base.xml            |   75 -
 build-support/script/build-ivy.xml                 |   25 -
 build-support/script/build-java.xml                |  146 -
 build.gradle                                       |  131 +
 build.xml                                          |   32 -
 core/.checkstyle                                   |   10 -
 core/.classpath                                    |   22 -
 core/.gitignore                                    |    7 -
 core/.project                                      |   23 -
 core/build.xml                                     |   31 -
 core/ivy.xml                                       |   46 -
 core/pom.xml                                       |  417 -
 .../osmosis/core/CorePluginLoader.java             |  116 -
 .../org/openstreetmap/osmosis/core/Osmosis.java    |  140 -
 .../osmosis/core/bound/v0_6/BoundComputer.java     |  146 -
 .../osmosis/core/bound/v0_6/BoundSetter.java       |   83 -
 .../osmosis/core/buffer/v0_6/ChangeBuffer.java     |   89 -
 .../osmosis/core/buffer/v0_6/EntityBuffer.java     |   89 -
 .../osmosis/core/cli/CommandLineParser.java        |  408 -
 .../core/container/v0_6/RelationContainer.java     |   84 -
 .../osmosis/core/container/v0_6/WayContainer.java  |   84 -
 .../osmosis/core/database/DatabaseConstants.java   |  114 -
 .../osmosis/core/domain/v0_6/Bound.java            |  503 --
 .../osmosis/core/domain/v0_6/Relation.java         |  386 -
 .../osmosis/core/domain/v0_6/Way.java              |  395 -
 .../osmosis/core/lifecycle/Releasable.java         |   23 -
 .../osmosis/core/misc/v0_6/EmptyChangeReader.java  |   37 -
 .../osmosis/core/misc/v0_6/EmptyReader.java        |   37 -
 .../osmosis/core/misc/v0_6/NullChangeWriter.java   |   38 -
 .../osmosis/core/misc/v0_6/NullWriter.java         |   38 -
 .../core/progress/v0_6/ChangeProgressLogger.java   |   84 -
 .../progress/v0_6/ChangeProgressLoggerFactory.java |   36 -
 .../core/progress/v0_6/EntityProgressLogger.java   |   81 -
 .../progress/v0_6/EntityProgressLoggerFactory.java |   35 -
 .../core/progress/v0_6/impl/ProgressTracker.java   |   87 -
 .../osmosis/core/report/v0_6/EntityReporter.java   |  316 -
 .../core/report/v0_6/IntegrityReporter.java        |  241 -
 .../osmosis/core/sort/common/FileBasedSort.java    |  287 -
 .../osmosis/core/sort/common/MergingIterator.java  |  132 -
 .../osmosis/core/sort/v0_6/ChangeSorter.java       |   83 -
 .../osmosis/core/sort/v0_6/ChangeTagSorter.java    |   87 -
 .../osmosis/core/sort/v0_6/EntitySorter.java       |   81 -
 .../sort/v0_6/SortedDeltaChangePipeValidator.java  |   81 -
 .../v0_6/SortedDuplicateEntityPipeValidator.java   |   79 -
 .../core/sort/v0_6/SortedEntityPipeValidator.java  |   78 -
 .../v0_6/SortedHistoryChangePipeValidator.java     |   82 -
 .../osmosis/core/sort/v0_6/TagSorter.java          |   84 -
 .../osmosis/core/store/DataPostbox.java            |  280 -
 .../core/store/DataPostboxLoadInjector.java        |   87 -
 .../osmosis/core/task/v0_6/ChangeSink.java         |   23 -
 .../openstreetmap/osmosis/core/task/v0_6/Sink.java |   23 -
 .../osmosis/core/tee/v0_6/ChangeTee.java           |  143 -
 .../osmosis/core/tee/v0_6/EntityTee.java           |  143 -
 .../osmosis/core/util/FileBasedLock.java           |  119 -
 .../osmosis/core/util/LazyHashMap.java             |  180 -
 .../osmosis/core/util/PropertiesPersister.java     |  128 -
 .../osmosis/core/bound/v0_6/BoundComputerTest.java |   99 -
 .../osmosis/core/bound/v0_6/BoundSetterTest.java   |  108 -
 .../osmosis/core/cli/CommandLineParserTest.java    |  126 -
 .../osmosis/core/domain/v0_6/BoundTest.java        |  597 --
 .../test/task/v0_6/SinkEntityInspector.java        |   81 -
 dataset/.checkstyle                                |   10 -
 dataset/.classpath                                 |   25 -
 dataset/.gitignore                                 |    5 -
 dataset/.project                                   |   17 -
 dataset/build.xml                                  |    7 -
 dataset/ivy.xml                                    |   35 -
 .../dataset/v0_6/DatasetBoundingBoxFilter.java     |  101 -
 .../osmosis/dataset/v0_6/DumpDataset.java          |   69 -
 .../osmosis/dataset/v0_6/WriteDataset.java         |   68 -
 .../osmosis/dataset/v0_6/impl/DatasetStore.java    |  438 -
 extract/.checkstyle                                |   10 -
 extract/.classpath                                 |   41 -
 extract/.gitignore                                 |    5 -
 extract/.project                                   |   17 -
 extract/build.xml                                  |    7 -
 extract/ivy.xml                                    |   36 -
 gradle.properties                                  |   46 +
 gradle/wrapper/gradle-wrapper.jar                  |  Bin 0 -> 46735 bytes
 gradle/wrapper/gradle-wrapper.properties           |    6 +
 gradlew                                            |  164 +
 gradlew.bat                                        |   90 +
 hstore-jdbc/.checkstyle                            |    7 -
 hstore-jdbc/.classpath                             |   16 -
 hstore-jdbc/.gitignore                             |    5 -
 hstore-jdbc/.project                               |   17 -
 hstore-jdbc/build.xml                              |   10 -
 hstore-jdbc/ivy.xml                                |   32 -
 ivy.xml                                            |   27 -
 osmosis-apidb/.checkstyle                          |   10 +
 osmosis-apidb/.gitignore                           |    6 +
 osmosis-apidb/build.gradle                         |   10 +
 .../osmosis/apidb/ApidbPluginLoader.java           |    0
 .../osmosis/apidb/common/BaseTableReader.java      |    0
 .../osmosis/apidb/common/DataSourceFactory.java    |    0
 .../osmosis/apidb/common/DatabaseContext.java      |    0
 .../osmosis/apidb/common/DatabaseContext2.java     |  385 +
 .../osmosis/apidb/common/IdentityValueLoader.java  |    0
 .../apidb/common/MysqlIdentityValueLoader.java     |    0
 .../apidb/common/MysqlIdentityValueLoader2.java    |    0
 .../common/PostgresqlIdentityValueLoader.java      |    0
 .../common/PostgresqlIdentityValueLoader2.java     |   57 +
 .../osmosis/apidb/v0_6/ApidbChangeReader.java      |  127 +
 .../apidb/v0_6/ApidbChangeReaderFactory.java       |    0
 .../osmosis/apidb/v0_6/ApidbChangeWriter.java      |   90 +
 .../apidb/v0_6/ApidbChangeWriterFactory.java       |    0
 .../osmosis/apidb/v0_6/ApidbCurrentReader.java     |  113 +
 .../apidb/v0_6/ApidbCurrentReaderFactory.java      |    0
 .../osmosis/apidb/v0_6/ApidbFileReplicator.java    |  107 +
 .../apidb/v0_6/ApidbFileReplicatorFactory.java     |   48 +
 .../osmosis/apidb/v0_6/ApidbReader.java            |  117 +
 .../osmosis/apidb/v0_6/ApidbReaderFactory.java     |    0
 .../osmosis/apidb/v0_6/ApidbTruncator.java         |    0
 .../osmosis/apidb/v0_6/ApidbTruncatorFactory.java  |    0
 .../osmosis/apidb/v0_6/ApidbVersionConstants.java  |   34 +
 .../osmosis/apidb/v0_6/ApidbWriter.java            | 1244 +++
 .../osmosis/apidb/v0_6/ApidbWriterFactory.java     |    0
 .../apidb/v0_6/impl/ActionChangeWriter.java        |    0
 .../osmosis/apidb/v0_6/impl/AllEntityDao.java      |    0
 .../osmosis/apidb/v0_6/impl/ChangeReader.java      |    0
 .../osmosis/apidb/v0_6/impl/ChangeWriter.java      |    0
 .../osmosis/apidb/v0_6/impl/ChangesetManager.java  |    0
 .../osmosis/apidb/v0_6/impl/DeltaToDiffReader.java |    0
 .../apidb/v0_6/impl/EntityContainerReader.java     |    0
 .../osmosis/apidb/v0_6/impl/EntityDao.java         |  557 ++
 .../apidb/v0_6/impl/EntityDataRowMapper.java       |    0
 .../osmosis/apidb/v0_6/impl/EntityHistory.java     |    0
 .../apidb/v0_6/impl/EntityHistoryComparator.java   |    0
 .../apidb/v0_6/impl/EntityHistoryListReader.java   |    0
 .../apidb/v0_6/impl/EntityHistoryReader.java       |    0
 .../apidb/v0_6/impl/EntityHistoryRowMapper.java    |    0
 .../apidb/v0_6/impl/EntitySnapshotReader.java      |    0
 .../apidb/v0_6/impl/FeatureHistoryPopulator.java   |    0
 .../osmosis/apidb/v0_6/impl/MemberTypeParser.java  |    0
 .../apidb/v0_6/impl/MemberTypeRenderer.java        |    0
 .../osmosis/apidb/v0_6/impl/NodeDao.java           |    0
 .../osmosis/apidb/v0_6/impl/NodeRowMapper.java     |    0
 .../osmosis/apidb/v0_6/impl/RelationDao.java       |    0
 .../apidb/v0_6/impl/RelationMemberRowMapper.java   |    0
 .../osmosis/apidb/v0_6/impl/RelationRowMapper.java |    0
 .../v0_6/impl/ReplicationQueryPredicates.java      |    0
 .../osmosis/apidb/v0_6/impl/ReplicationSource.java |    0
 .../osmosis/apidb/v0_6/impl/ReplicationState.java  |  237 +
 .../osmosis/apidb/v0_6/impl/Replicator.java        |  408 +
 .../apidb/v0_6/impl/SchemaVersionValidator.java    |    0
 .../osmosis/apidb/v0_6/impl/SystemTimeLoader.java  |    0
 .../apidb/v0_6/impl/TagCollectionLoader.java       |    0
 .../osmosis/apidb/v0_6/impl/TagRowMapper.java      |    0
 .../osmosis/apidb/v0_6/impl/TimeDao.java           |   39 +
 .../osmosis/apidb/v0_6/impl/TransactionDao.java    |   65 +
 .../apidb/v0_6/impl/TransactionManager.java        |   24 +
 .../apidb/v0_6/impl/TransactionSnapshot.java       |    0
 .../osmosis/apidb/v0_6/impl/UserManager.java       |    0
 .../osmosis/apidb/v0_6/impl/WayDao.java            |    0
 .../osmosis/apidb/v0_6/impl/WayNodeRowMapper.java  |    0
 .../osmosis/apidb/v0_6/impl/WayRowMapper.java      |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/apidb/v0_6/ApiDbTest.java              |    0
 .../apidb/v0_6/ApidbFileReplicatorTest.java        |  126 +
 .../apidb/v0_6/impl/ChangesetManagerTest.java      |    0
 .../osmosis/apidb/v0_6/impl/DatabaseUtilities.java |    0
 .../v0_6/impl/MockReplicationDestination.java      |  104 +
 .../apidb/v0_6/impl/MockReplicationSource.java     |    0
 .../apidb/v0_6/impl/MockSystemTimeLoader.java      |    0
 .../v0_6/impl/MockTransactionSnapshotLoader.java   |   36 +
 .../impl/ReplicationSequenceFormatterTest.java     |    0
 .../osmosis/apidb/v0_6/impl/ReplicatorTest.java    |  312 +
 .../apidb/v0_6/impl/TransactionSnapshotTest.java   |    0
 .../data/template/v0_6/apidb-authfile.txt          |    0
 .../data/template/v0_6/db-changeset-b.osc          |    0
 .../data/template/v0_6/db-changeset-expected.osm   |   44 +
 .../resources/data/template/v0_6/db-changeset.osc  |    0
 .../data/template/v0_6/db-replicate-changeset.osc  |    0
 .../resources/data/template/v0_6/db-snapshot-b.osm |   46 +
 .../resources/data/template/v0_6/db-snapshot.osm   |   46 +
 osmosis-areafilter/.checkstyle                     |   10 +
 osmosis-areafilter/.gitignore                      |    6 +
 osmosis-areafilter/build.gradle                    |    5 +
 .../osmosis/areafilter/AreaFilterPluginLoader.java |    0
 .../areafilter/common/PolygonFileReader.java       |    0
 .../osmosis/areafilter/v0_6/AreaFilter.java        |  659 ++
 .../v0_6/AreaFilterTaskManagerFactory.java         |   31 +
 .../osmosis/areafilter/v0_6/BoundingBoxFilter.java |    0
 .../areafilter/v0_6/BoundingBoxFilterFactory.java  |    0
 .../osmosis/areafilter/v0_6/PolygonFilter.java     |    0
 .../areafilter/v0_6/PolygonFilterFactory.java      |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/areafilter/v0_6/AreaFilterTest.java    |    0
 .../areafilter/v0_6/BoundingBoxFilterTest.java     |  181 +
 .../osmosis/areafilter/v0_6/PolygonFilterTest.java |  156 +
 .../resources/data/template/v0_6/areafilter-in.osm |   49 +
 .../v0_6/areafilter-out-cascadingrelations.osm     |   34 +
 .../v0_6/areafilter-out-clipincompleteentities.osm |   27 +
 .../v0_6/areafilter-out-completerelations.osm      |   49 +
 .../template/v0_6/areafilter-out-completeways.osm  |   37 +
 .../data/template/v0_6/areafilter-out-standard.osm |   29 +
 .../data/template/v0_6/areafilter-out-whole.osm    |   44 +
 .../osmosis/areafilter/v0_6/testPolygon.txt        |    0
 osmosis-core/.checkstyle                           |   10 +
 osmosis-core/.gitignore                            |    8 +
 osmosis-core/build.gradle                          |   74 +
 {core => osmosis-core}/doc/OsmApi06.xsd            |    0
 {core => osmosis-core}/doc/contributors.txt        |    0
 .../src/assembly/distribution.xml                  |    0
 .../osmosis/core/CorePluginLoader.java             |  116 +
 .../org/openstreetmap/osmosis/core/LogLevels.java  |   59 +
 .../org/openstreetmap/osmosis/core/Osmosis.java    |  143 +
 .../osmosis/core/OsmosisConstants.java.template    |    0
 .../osmosis/core/OsmosisException.java             |    0
 .../osmosis/core/OsmosisRuntimeException.java      |    0
 .../openstreetmap/osmosis/core/TaskRegistrar.java  |    0
 .../osmosis/core/bound/v0_6/BoundComputer.java     |  154 +
 .../core/bound/v0_6/BoundComputerFactory.java      |    0
 .../osmosis/core/bound/v0_6/BoundSetter.java       |   91 +
 .../core/bound/v0_6/BoundSetterFactory.java        |    0
 .../osmosis/core/buffer/v0_6/ChangeBuffer.java     |   95 +
 .../core/buffer/v0_6/ChangeBufferFactory.java      |    0
 .../osmosis/core/buffer/v0_6/EntityBuffer.java     |   95 +
 .../core/buffer/v0_6/EntityBufferFactory.java      |    0
 .../core/change/v0_6/impl/TimestampSetter.java     |    0
 .../osmosis/core/cli/CommandLineParser.java        |  384 +
 .../core/container/v0_6/BoundContainer.java        |    0
 .../core/container/v0_6/BoundContainerFactory.java |    0
 .../container/v0_6/BoundContainerIterator.java     |    0
 .../core/container/v0_6/ChangeContainer.java       |    0
 .../osmosis/core/container/v0_6/Dataset.java       |    0
 .../core/container/v0_6/DatasetContext.java        |    0
 .../core/container/v0_6/EntityContainer.java       |    0
 .../container/v0_6/EntityContainerBuilder.java     |    0
 .../container/v0_6/EntityContainerFactory.java     |    0
 .../osmosis/core/container/v0_6/EntityManager.java |    0
 .../core/container/v0_6/EntityProcessor.java       |    0
 .../osmosis/core/container/v0_6/NodeContainer.java |    0
 .../core/container/v0_6/NodeContainerFactory.java  |    0
 .../core/container/v0_6/NodeContainerIterator.java |    0
 .../core/container/v0_6/RelationContainer.java     |   82 +
 .../container/v0_6/RelationContainerFactory.java   |    0
 .../container/v0_6/RelationContainerIterator.java  |    0
 .../osmosis/core/container/v0_6/WayContainer.java  |   82 +
 .../core/container/v0_6/WayContainerFactory.java   |    0
 .../core/container/v0_6/WayContainerIterator.java  |    0
 .../database/AuthenticationPropertiesLoader.java   |    0
 .../osmosis/core/database/DatabaseConstants.java   |  114 +
 .../core/database/DatabaseLoginCredentials.java    |    0
 .../osmosis/core/database/DatabasePreferences.java |    0
 .../core/database/DatabaseTaskManagerFactory.java  |    0
 .../osmosis/core/database/DatabaseType.java        |    0
 .../osmosis/core/database/DbFeature.java           |    0
 .../osmosis/core/database/DbFeatureComparator.java |    0
 .../osmosis/core/database/DbFeatureHistory.java    |    0
 .../core/database/DbFeatureHistoryComparator.java  |    0
 .../core/database/DbFeatureHistoryRowMapper.java   |    0
 .../osmosis/core/database/DbFeatureRowMapper.java  |    0
 .../osmosis/core/database/DbOrderedFeature.java    |    0
 .../DbOrderedFeatureHistoryComparator.java         |    0
 .../core/database/DbOrderedFeatureRowMapper.java   |    0
 .../core/database/FeatureCollectionLoader.java     |    0
 .../osmosis/core/database/FeaturePopulator.java    |    0
 .../database/RelationMemberCollectionLoader.java   |    0
 .../database/ReleasableStatementContainer.java     |    0
 .../osmosis/core/database/RowMapperListener.java   |    0
 .../database/SortingStoreRowMapperListener.java    |    0
 .../core/database/WayNodeCollectionLoader.java     |    0
 .../domain/common/SimpleTimestampContainer.java    |    0
 .../core/domain/common/TimestampContainer.java     |    0
 .../core/domain/common/TimestampFormat.java        |    0
 .../domain/common/UnparsedTimestampContainer.java  |    0
 .../osmosis/core/domain/v0_6/Bound.java            |  515 ++
 .../osmosis/core/domain/v0_6/CommonEntityData.java |    0
 .../osmosis/core/domain/v0_6/Entity.java           |    0
 .../osmosis/core/domain/v0_6/EntityBuilder.java    |    0
 .../osmosis/core/domain/v0_6/EntityType.java       |    0
 .../osmosis/core/domain/v0_6/Node.java             |    0
 .../osmosis/core/domain/v0_6/NodeBuilder.java      |    0
 .../osmosis/core/domain/v0_6/OsmUser.java          |    0
 .../osmosis/core/domain/v0_6/Relation.java         |  386 +
 .../osmosis/core/domain/v0_6/RelationBuilder.java  |    0
 .../osmosis/core/domain/v0_6/RelationMember.java   |    0
 .../osmosis/core/domain/v0_6/Tag.java              |    0
 .../osmosis/core/domain/v0_6/TagCollection.java    |    0
 .../core/domain/v0_6/TagCollectionImpl.java        |    0
 .../domain/v0_6/UnmodifiableTagCollection.java     |    0
 .../osmosis/core/domain/v0_6/Way.java              |  395 +
 .../osmosis/core/domain/v0_6/WayBuilder.java       |    0
 .../osmosis/core/domain/v0_6/WayNode.java          |    0
 .../core/filter/common/BitSetIdTracker.java        |    0
 .../core/filter/common/DynamicIdTracker.java       |    0
 .../filter/common/DynamicIdTrackerSegment.java     |    0
 .../osmosis/core/filter/common/IdTracker.java      |    0
 .../core/filter/common/IdTrackerFactory.java       |    0
 .../osmosis/core/filter/common/IdTrackerType.java  |    0
 .../osmosis/core/filter/common/ListIdTracker.java  |    0
 .../osmosis/core/lifecycle/Completable.java        |    0
 .../core/lifecycle/CompletableContainer.java       |    0
 .../osmosis/core/lifecycle/Releasable.java         |   25 +
 .../core/lifecycle/ReleasableContainer.java        |    0
 .../osmosis/core/lifecycle/ReleasableIterator.java |    0
 .../merge/common/ConflictResolutionMethod.java     |    0
 .../osmosis/core/misc/v0_6/EmptyChangeReader.java  |   40 +
 .../core/misc/v0_6/EmptyChangeReaderFactory.java   |    0
 .../osmosis/core/misc/v0_6/EmptyReader.java        |   40 +
 .../osmosis/core/misc/v0_6/EmptyReaderFactory.java |    0
 .../osmosis/core/misc/v0_6/NullChangeWriter.java   |   49 +
 .../core/misc/v0_6/NullChangeWriterFactory.java    |    0
 .../osmosis/core/misc/v0_6/NullWriter.java         |   49 +
 .../osmosis/core/misc/v0_6/NullWriterFactory.java  |    0
 .../core/pipeline/common/ActiveTaskManager.java    |    0
 .../core/pipeline/common/PassiveTaskManager.java   |    0
 .../osmosis/core/pipeline/common/PipeTasks.java    |    0
 .../osmosis/core/pipeline/common/Pipeline.java     |    0
 .../core/pipeline/common/PipelineConstants.java    |    0
 .../core/pipeline/common/RunnableTaskManager.java  |    0
 .../core/pipeline/common/TaskConfiguration.java    |    0
 .../osmosis/core/pipeline/common/TaskManager.java  |    0
 .../core/pipeline/common/TaskManagerFactory.java   |    0
 .../common/TaskManagerFactoryRegister.java         |    0
 .../osmosis/core/pipeline/common/TaskRunner.java   |    0
 .../v0_6/ChangeSinkChangeSourceManager.java        |    0
 .../core/pipeline/v0_6/ChangeSinkManager.java      |    0
 .../v0_6/ChangeSinkMultiChangeSourceManager.java   |    0
 .../ChangeSinkRunnableChangeSourceManager.java     |    0
 .../pipeline/v0_6/ChangeSinkSourceManager.java     |   60 +
 .../core/pipeline/v0_6/DatasetSinkManager.java     |    0
 .../pipeline/v0_6/DatasetSinkSourceManager.java    |    0
 ...MultiChangeSinkRunnableChangeSourceManager.java |    0
 ...tiSinkMultiChangeSinkRunnableSourceManager.java |    0
 .../v0_6/MultiSinkRunnableChangeSourceManager.java |    0
 .../v0_6/MultiSinkRunnableSourceManager.java       |    0
 .../pipeline/v0_6/RunnableChangeSourceManager.java |    0
 .../v0_6/RunnableDatasetSourceManager.java         |    0
 .../core/pipeline/v0_6/RunnableSourceManager.java  |    0
 .../pipeline/v0_6/SinkDatasetSourceManager.java    |    0
 .../osmosis/core/pipeline/v0_6/SinkManager.java    |    0
 .../core/pipeline/v0_6/SinkMultiSourceManager.java |    0
 .../pipeline/v0_6/SinkRunnableSourceManager.java   |    0
 .../core/pipeline/v0_6/SinkSourceManager.java      |    0
 .../osmosis/core/plugin/CorePlugin.java            |    0
 .../osmosis/core/plugin/PluginLoader.java          |    0
 .../core/progress/v0_6/ChangeProgressLogger.java   |  108 +
 .../progress/v0_6/ChangeProgressLoggerFactory.java |   40 +
 .../core/progress/v0_6/EntityProgressLogger.java   |  104 +
 .../progress/v0_6/EntityProgressLoggerFactory.java |   40 +
 .../core/progress/v0_6/impl/ProgressTracker.java   |   95 +
 .../osmosis/core/report/v0_6/EntityReporter.java   |  324 +
 .../core/report/v0_6/EntityReporterFactory.java    |    0
 .../core/report/v0_6/IntegrityReporter.java        |  250 +
 .../core/report/v0_6/IntegrityReporterFactory.java |    0
 .../osmosis/core/sort/common/FileBasedSort.java    |  291 +
 .../osmosis/core/sort/common/MergingIterator.java  |  131 +
 .../core/sort/v0_6/ChangeAsEntityComparator.java   |    0
 .../v0_6/ChangeForSeekableApplierComparator.java   |    0
 .../v0_6/ChangeForStreamableApplierComparator.java |    0
 .../osmosis/core/sort/v0_6/ChangeSorter.java       |   92 +
 .../core/sort/v0_6/ChangeSorterFactory.java        |    0
 .../osmosis/core/sort/v0_6/ChangeTagSorter.java    |   96 +
 .../core/sort/v0_6/ChangeTagSorterFactory.java     |    0
 .../core/sort/v0_6/EntityByIdComparator.java       |    0
 .../core/sort/v0_6/EntityByTypeComparator.java     |    0
 .../sort/v0_6/EntityByTypeThenIdComparator.java    |    0
 .../EntityByTypeThenIdThenVersionComparator.java   |    0
 .../core/sort/v0_6/EntityByVersionComparator.java  |    0
 .../core/sort/v0_6/EntityContainerComparator.java  |    0
 .../osmosis/core/sort/v0_6/EntitySorter.java       |   90 +
 .../core/sort/v0_6/EntitySorterFactory.java        |    0
 .../core/sort/v0_6/EntitySubClassComparator.java   |    0
 .../sort/v0_6/SortedDeltaChangePipeValidator.java  |   90 +
 .../v0_6/SortedDuplicateEntityPipeValidator.java   |   89 +
 .../core/sort/v0_6/SortedEntityPipeValidator.java  |   88 +
 .../v0_6/SortedHistoryChangePipeValidator.java     |   91 +
 .../core/sort/v0_6/StackableComparator.java        |    0
 .../osmosis/core/sort/v0_6/TagSorter.java          |   93 +
 .../osmosis/core/sort/v0_6/TagSorterFactory.java   |    0
 .../osmosis/core/store/BaseObjectReader.java       |    0
 .../osmosis/core/store/BaseObjectWriter.java       |    0
 .../osmosis/core/store/BaseStoreClassRegister.java |    0
 .../store/BufferedRandomAccessFileInputStream.java |    0
 .../osmosis/core/store/ChunkedObjectStore.java     |    0
 .../osmosis/core/store/ComparableComparator.java   |    0
 .../osmosis/core/store/DataInputStoreReader.java   |    0
 .../osmosis/core/store/DataOutputStoreWriter.java  |    0
 .../osmosis/core/store/DataPostbox.java            |  544 ++
 .../core/store/DataPostboxLoadInjector.java        |   87 +
 .../core/store/DynamicStoreClassRegister.java      |    0
 .../osmosis/core/store/EmptyIterator.java          |    0
 .../osmosis/core/store/EndOfStoreException.java    |    0
 .../osmosis/core/store/GenericObjectReader.java    |    0
 .../store/GenericObjectSerializationFactory.java   |    0
 .../osmosis/core/store/GenericObjectWriter.java    |    0
 .../osmosis/core/store/IndexElement.java           |    0
 .../osmosis/core/store/IndexRangeIterator.java     |    0
 .../osmosis/core/store/IndexStore.java             |    0
 .../osmosis/core/store/IndexStoreReader.java       |    0
 .../osmosis/core/store/IndexedObjectStore.java     |    0
 .../core/store/IndexedObjectStoreReader.java       |    0
 .../core/store/IntegerLongIndexElement.java        |    0
 .../osmosis/core/store/LongLongIndexElement.java   |    0
 .../osmosis/core/store/MultipleSourceIterator.java |    0
 .../core/store/NoSuchIndexElementException.java    |    0
 .../core/store/ObjectDataInputIterator.java        |    0
 .../osmosis/core/store/ObjectReader.java           |    0
 .../core/store/ObjectSerializationFactory.java     |    0
 .../osmosis/core/store/ObjectStreamIterator.java   |    0
 .../osmosis/core/store/ObjectWriter.java           |    0
 .../core/store/OffsetTrackingOutputStream.java     |    0
 .../osmosis/core/store/PeekableIterator.java       |    0
 .../osmosis/core/store/PersistentIterator.java     |    0
 .../core/store/RandomAccessObjectStore.java        |    0
 .../core/store/RandomAccessObjectStoreReader.java  |    0
 .../core/store/ReleasableAdaptorForIterator.java   |    0
 .../osmosis/core/store/SegmentedObjectStore.java   |    0
 .../osmosis/core/store/SimpleObjectStore.java      |    0
 .../core/store/SingleClassObjectReader.java        |    0
 .../SingleClassObjectSerializationFactory.java     |    0
 .../core/store/SingleClassObjectWriter.java        |    0
 .../core/store/StaticStoreClassRegister.java       |    0
 .../osmosis/core/store/StorageStage.java           |    0
 .../osmosis/core/store/StoreClassRegister.java     |    0
 .../osmosis/core/store/StoreReader.java            |    0
 .../osmosis/core/store/StoreReleasingIterator.java |    0
 .../osmosis/core/store/StoreWriter.java            |    0
 .../osmosis/core/store/Storeable.java              |    0
 .../core/store/StoreableConstructorCache.java      |    0
 .../core/store/SubObjectStreamIterator.java        |    0
 .../core/store/UnsignedIntegerComparator.java      |    0
 .../osmosis/core/store/UpcastIterator.java         |    0
 .../osmosis/core/task/common/ChangeAction.java     |    0
 .../osmosis/core/task/common/RunnableTask.java     |    0
 .../osmosis/core/task/common/Task.java             |    0
 .../osmosis/core/task/v0_6/ChangeSink.java         |   22 +
 .../core/task/v0_6/ChangeSinkChangeSource.java     |    0
 .../task/v0_6/ChangeSinkMultiChangeSource.java     |    0
 .../task/v0_6/ChangeSinkRunnableChangeSource.java  |    0
 .../osmosis/core/task/v0_6/ChangeSinkSource.java   |   13 +
 .../osmosis/core/task/v0_6/ChangeSource.java       |    0
 .../osmosis/core/task/v0_6/DatasetSink.java        |    0
 .../osmosis/core/task/v0_6/DatasetSinkSource.java  |    0
 .../osmosis/core/task/v0_6/DatasetSource.java      |    0
 .../osmosis/core/task/v0_6/Initializable.java      |   30 +
 .../osmosis/core/task/v0_6/MultiChangeSink.java    |    0
 .../v0_6/MultiChangeSinkRunnableChangeSource.java  |    0
 .../osmosis/core/task/v0_6/MultiChangeSource.java  |    0
 .../osmosis/core/task/v0_6/MultiSink.java          |    0
 .../MultiSinkMultiChangeSinkRunnableSource.java    |    0
 .../task/v0_6/MultiSinkRunnableChangeSource.java   |    0
 .../core/task/v0_6/MultiSinkRunnableSource.java    |    0
 .../osmosis/core/task/v0_6/MultiSource.java        |    0
 .../core/task/v0_6/RunnableChangeSource.java       |    0
 .../core/task/v0_6/RunnableDatasetSource.java      |    0
 .../osmosis/core/task/v0_6/RunnableSource.java     |    0
 .../openstreetmap/osmosis/core/task/v0_6/Sink.java |   22 +
 .../osmosis/core/task/v0_6/SinkDatasetSource.java  |    0
 .../osmosis/core/task/v0_6/SinkMultiSource.java    |    0
 .../osmosis/core/task/v0_6/SinkRunnableSource.java |    0
 .../osmosis/core/task/v0_6/SinkSource.java         |    0
 .../osmosis/core/task/v0_6/Source.java             |    0
 .../osmosis/core/tee/v0_6/ChangeTee.java           |  162 +
 .../osmosis/core/tee/v0_6/ChangeTeeFactory.java    |    0
 .../osmosis/core/tee/v0_6/EntityTee.java           |  162 +
 .../osmosis/core/tee/v0_6/EntityTeeFactory.java    |    0
 .../osmosis/core/time/DateFormatter.java           |    0
 .../osmosis/core/time/DateParser.java              |    0
 .../osmosis/core/time/FallbackDateParser.java      |    0
 .../osmosis/core/util/AtomicFileCreator.java       |    0
 .../osmosis/core/util/CollectionWrapper.java       |    0
 .../osmosis/core/util/FileBasedLock.java           |  120 +
 .../util/FixedPrecisionCoordinateConvertor.java    |    0
 .../openstreetmap/osmosis/core/util/IntAsChar.java |    0
 .../osmosis/core/util/LazyHashMap.java             |  180 +
 .../openstreetmap/osmosis/core/util/LongAsInt.java |    0
 .../core/util/MultiMemberGZIPInputStream.java      |    0
 .../osmosis/core/util/PropertiesPersister.java     |  154 +
 .../osmosis/core/util/ResourceFileManager.java     |    0
 .../osmosis/core/util/TileCalculator.java          |    0
 .../osmosis/core/plugin/plugin.xml.template        |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 {core => osmosis-core}/src/site/site.xml           |    0
 .../openstreetmap/osmosis/core/MyPluginLoader.java |    0
 .../osmosis/core/bound/v0_6/BoundComputerTest.java |   99 +
 .../osmosis/core/bound/v0_6/BoundSetterTest.java   |  108 +
 .../osmosis/core/cli/CommandLineParserTest.java    |  140 +
 .../osmosis/core/domain/v0_6/BoundTest.java        |  587 ++
 .../osmosis/core/domain/v0_6/CloneTest.java        |   87 +
 .../osmosis/core/domain/v0_6/OsmUserTest.java      |    0
 .../core/filter/common/BitSetIdTrackerTest.java    |    0
 .../core/filter/common/DynamicIdTrackerTest.java   |    0
 .../osmosis/core/filter/common/IdTrackerBase.java  |    0
 .../core/filter/common/ListIdTrackerTest.java      |    0
 .../core/mysql/common/TileCalculatorTest.java      |    0
 .../core/sort/common/FileBasedSortTest.java        |   77 +
 .../osmosis/core/sort/common/SampleStoreable.java  |   57 +
 .../osmosis/testutil/v0_6/SinkEntityInspector.java |   91 +
 .../src/test/resources/data/template/readme.txt    |    0
 .../template/v0_6/compressor-bzip2-test.osm.bz2    |  Bin
 .../data/template/v0_6/migration-expected.osc      |    0
 .../data/template/v0_6/migration-expected.osm      |    0
 .../resources/data/template/v0_6/rep-changeset.osc |    0
 .../data/template/v0_6/repdb-authfile.txt          |    0
 osmosis-dataset/.checkstyle                        |   10 +
 osmosis-dataset/.gitignore                         |    6 +
 osmosis-dataset/build.gradle                       |    5 +
 .../osmosis/dataset/DatasetPluginLoader.java       |    0
 .../dataset/v0_6/DatasetBoundingBoxFilter.java     |  107 +
 .../v0_6/DatasetBoundingBoxFilterFactory.java      |    0
 .../osmosis/dataset/v0_6/DumpDataset.java          |   75 +
 .../osmosis/dataset/v0_6/DumpDatasetFactory.java   |    0
 .../osmosis/dataset/v0_6/ReadDataset.java          |    0
 .../osmosis/dataset/v0_6/ReadDatasetFactory.java   |    0
 .../osmosis/dataset/v0_6/WriteDataset.java         |   77 +
 .../osmosis/dataset/v0_6/WriteDatasetFactory.java  |    0
 .../dataset/v0_6/impl/BoundingBoxContext.java      |    0
 .../osmosis/dataset/v0_6/impl/DatasetStore.java    |  451 ++
 .../dataset/v0_6/impl/DatasetStoreFileManager.java |    0
 .../dataset/v0_6/impl/DatasetStoreReader.java      |    0
 .../osmosis/dataset/v0_6/impl/NodeManager.java     |    0
 .../dataset/v0_6/impl/NodeStorageContainer.java    |    0
 .../impl/PermanentFileDatasetStoreFileManager.java |    0
 .../osmosis/dataset/v0_6/impl/RelationManager.java |    0
 .../v0_6/impl/RelationStorageContainer.java        |    0
 .../v0_6/impl/RelationalIndexValueIdIterator.java  |    0
 .../v0_6/impl/TempFileDatasetStoreFileManager.java |    0
 .../v0_6/impl/TileIndexValueIdIterator.java        |    0
 .../osmosis/dataset/v0_6/impl/WayManager.java      |    0
 .../dataset/v0_6/impl/WayStorageContainer.java     |    0
 .../dataset/v0_6/impl/WayTileAreaIndex.java        |    0
 .../dataset/v0_6/impl/WayTileAreaIndexReader.java  |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/dataset/v0_6/CustomDbTest.java         |    0
 .../data/template/v0_6/customdb-snapshot.osm       |    0
 osmosis-extract/.checkstyle                        |   10 +
 osmosis-extract/.gitignore                         |    6 +
 osmosis-extract/build.gradle                       |    7 +
 .../extract/apidb/common/Configuration.java        |    0
 .../extract/apidb/v0_6/DatabaseTimeLoader.java     |    0
 .../extract/apidb/v0_6/IntervalExtractor.java      |    0
 .../extract/apidb/v0_6/OsmosisExtractApiDb.java    |    0
 .../extract/apidb/v0_6/osmosis-extract-apidb.conf  |    0
 .../extract/apidb/v0_6/DatabaseTimeLoaderTest.java |    0
 .../extract/apidb/v0_6/DatabaseUtilities.java      |    0
 .../data/template/v0_6/apidb-authfile.txt          |    0
 osmosis-hstore-jdbc/.checkstyle                    |   10 +
 osmosis-hstore-jdbc/.gitignore                     |    6 +
 osmosis-hstore-jdbc/build.gradle                   |    6 +
 .../org/openstreetmap/osmosis/hstore/PGHStore.java |    0
 .../org/postgresql/driverconfig.properties         |    0
 osmosis-osm-binary/.gitignore                      |    6 +
 osmosis-osm-binary/COPYING.osmpbf                  |  165 +
 osmosis-osm-binary/ReleaseNotes.txt                |   37 +
 osmosis-osm-binary/build.gradle                    |   29 +
 .../osmosis/osmbinary/Fileformat.java              |  992 +++
 .../openstreetmap/osmosis/osmbinary/Osmformat.java | 8472 ++++++++++++++++++++
 osmosis-osm-binary/osmosis-protoc.sh               |    3 +
 osmosis-osm-binary/readme.txt                      |   23 +
 .../osmosis/osmbinary/BinaryParser.java            |  139 +
 .../osmosis/osmbinary/BinarySerializer.java        |  161 +
 .../osmosis/osmbinary/StringTable.java             |  138 +
 .../osmosis/osmbinary/file/BlockInputStream.java   |   47 +
 .../osmosis/osmbinary/file/BlockOutputStream.java  |   74 +
 .../osmosis/osmbinary/file/BlockReaderAdapter.java |   40 +
 .../osmosis/osmbinary/file/FileBlock.java          |  142 +
 .../osmosis/osmbinary/file/FileBlockBase.java      |   58 +
 .../osmosis/osmbinary/file/FileBlockHead.java      |   97 +
 .../osmosis/osmbinary/file/FileBlockPosition.java  |  112 +
 .../osmosis/osmbinary/file/FileBlockReference.java |   54 +
 .../osmbinary/file/FileFormatException.java        |   33 +
 .../osmosis/osmbinary/test/BuildTestFile.java      |  243 +
 .../src/main/protobuf/fileformat.proto             |   54 +
 .../src/main/protobuf/osmformat.proto              |  260 +
 osmosis-pbf/.checkstyle                            |   10 +
 osmosis-pbf/.gitignore                             |    6 +
 osmosis-pbf/build.gradle                           |    7 +
 .../crosby/binary/osmosis/BinaryPluginLoader.java  |    0
 .../crosby/binary/osmosis/OsmosisBinaryParser.java |  260 +
 .../java/crosby/binary/osmosis/OsmosisReader.java  |   57 +
 .../binary/osmosis/OsmosisReaderFactory.java       |    0
 .../crosby/binary/osmosis/OsmosisSerializer.java   |  511 ++
 .../binary/osmosis/OsmosisSerializerFactory.java   |   61 +
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/OsmosisReaderAndSerializerTest.java    |   52 +
 .../resources/data/template/v0_6/data-snapshot.osm |   46 +
 osmosis-pbf2/.checkstyle                           |   10 +
 osmosis-pbf2/.gitignore                            |    6 +
 osmosis-pbf2/build.gradle                          |    8 +
 .../osmosis/pbf2/PbfPluginLoader.java              |   33 +
 .../openstreetmap/osmosis/pbf2/v0_6/PbfReader.java |   96 +
 .../osmosis/pbf2/v0_6/PbfReaderFactory.java        |   48 +
 .../osmosis/pbf2/v0_6/impl/PbfBlobDecoder.java     |  454 ++
 .../pbf2/v0_6/impl/PbfBlobDecoderListener.java     |   28 +
 .../osmosis/pbf2/v0_6/impl/PbfBlobResult.java      |   80 +
 .../osmosis/pbf2/v0_6/impl/PbfDecoder.java         |  181 +
 .../osmosis/pbf2/v0_6/impl/PbfFieldDecoder.java    |   93 +
 .../osmosis/pbf2/v0_6/impl/PbfRawBlob.java         |   49 +
 .../osmosis/pbf2/v0_6/impl/PbfStreamSplitter.java  |  132 +
 .../src/main/resources/osmosis-plugins.conf        |    1 +
 .../osmosis/pbf2/v0_6/OsmosisReaderTest.java       |   52 +
 .../resources/data/template/v0_6/data-snapshot.osm |   46 +
 osmosis-pgsimple/.checkstyle                       |   10 +
 osmosis-pgsimple/.gitignore                        |    6 +
 osmosis-pgsimple/build.gradle                      |   16 +
 .../osmosis/pgsimple/PgSimplePluginLoader.java     |    0
 .../osmosis/pgsimple/common/BaseDao.java           |    0
 .../osmosis/pgsimple/common/BaseTableReader.java   |    0
 .../common/CompactPersistentNodeLocation.java      |    0
 .../common/CompactPersistentNodeLocationStore.java |    0
 .../osmosis/pgsimple/common/CopyFileWriter.java    |    0
 .../osmosis/pgsimple/common/DatabaseContext.java   |    0
 .../pgsimple/common/InMemoryNodeLocationStore.java |    0
 .../pgsimple/common/NoSuchRecordException.java     |    0
 .../osmosis/pgsimple/common/NodeLocation.java      |    0
 .../osmosis/pgsimple/common/NodeLocationStore.java |    0
 .../pgsimple/common/NodeLocationStoreType.java     |    0
 .../common/PersistentNodeLocationStore.java        |    0
 .../osmosis/pgsimple/common/PointBuilder.java      |    0
 .../osmosis/pgsimple/common/PolygonBuilder.java    |    0
 .../pgsimple/common/SchemaVersionValidator.java    |    0
 .../pgsimple/v0_6/PostgreSqlChangeWriter.java      |  101 +
 .../v0_6/PostgreSqlChangeWriterFactory.java        |    0
 .../pgsimple/v0_6/PostgreSqlCopyWriter.java        |  133 +
 .../pgsimple/v0_6/PostgreSqlCopyWriterFactory.java |    0
 .../pgsimple/v0_6/PostgreSqlDatasetReader.java     |    0
 .../v0_6/PostgreSqlDatasetReaderFactory.java       |    0
 .../pgsimple/v0_6/PostgreSqlDumpWriter.java        |   85 +
 .../pgsimple/v0_6/PostgreSqlDumpWriterFactory.java |    0
 .../osmosis/pgsimple/v0_6/PostgreSqlTruncator.java |    0
 .../pgsimple/v0_6/PostgreSqlTruncatorFactory.java  |    0
 .../pgsimple/v0_6/PostgreSqlVersionConstants.java  |    0
 .../osmosis/pgsimple/v0_6/PostgreSqlWriter.java    |  838 ++
 .../pgsimple/v0_6/PostgreSqlWriterFactory.java     |    0
 .../pgsimple/v0_6/impl/ActionChangeWriter.java     |    0
 .../osmosis/pgsimple/v0_6/impl/ActionDao.java      |    0
 .../osmosis/pgsimple/v0_6/impl/ActionDataType.java |    0
 .../osmosis/pgsimple/v0_6/impl/ChangeWriter.java   |    0
 .../pgsimple/v0_6/impl/ChangesetAction.java        |    0
 .../osmosis/pgsimple/v0_6/impl/CopyFileset.java    |    0
 .../pgsimple/v0_6/impl/CopyFilesetBuilder.java     |  259 +
 .../pgsimple/v0_6/impl/CopyFilesetLoader.java      |    0
 .../v0_6/impl/DatabaseCapabilityChecker.java       |    0
 .../v0_6/impl/DbOrderedFeatureComparator.java      |    0
 .../pgsimple/v0_6/impl/DirectoryCopyFileset.java   |    0
 .../osmosis/pgsimple/v0_6/impl/EntityDao.java      |    0
 .../pgsimple/v0_6/impl/EntityFeatureDao.java       |    0
 .../pgsimple/v0_6/impl/EntityFeatureMapper.java    |    0
 .../v0_6/impl/EntityFeatureTableReader.java        |    0
 .../osmosis/pgsimple/v0_6/impl/EntityMapper.java   |    0
 .../osmosis/pgsimple/v0_6/impl/EntityReader.java   |    0
 .../pgsimple/v0_6/impl/EntityTableReader.java      |    0
 .../osmosis/pgsimple/v0_6/impl/IndexManager.java   |  158 +
 .../pgsimple/v0_6/impl/MemberTypeValueMapper.java  |    0
 .../osmosis/pgsimple/v0_6/impl/NodeDao.java        |  119 +
 .../osmosis/pgsimple/v0_6/impl/NodeMapper.java     |    0
 .../osmosis/pgsimple/v0_6/impl/NodeReader.java     |    0
 .../v0_6/impl/PostgreSqlDatasetContext.java        |    0
 .../v0_6/impl/PostgreSqlEntityManager.java         |    0
 .../osmosis/pgsimple/v0_6/impl/RelationDao.java    |    0
 .../osmosis/pgsimple/v0_6/impl/RelationMapper.java |    0
 .../pgsimple/v0_6/impl/RelationMemberMapper.java   |    0
 .../osmosis/pgsimple/v0_6/impl/RelationReader.java |    0
 .../osmosis/pgsimple/v0_6/impl/TagMapper.java      |    0
 .../pgsimple/v0_6/impl/TempCopyFileset.java        |    0
 .../osmosis/pgsimple/v0_6/impl/UserDao.java        |    0
 .../osmosis/pgsimple/v0_6/impl/WayDao.java         |  182 +
 .../pgsimple/v0_6/impl/WayGeometryBuilder.java     |    0
 .../osmosis/pgsimple/v0_6/impl/WayMapper.java      |    0
 .../osmosis/pgsimple/v0_6/impl/WayNodeMapper.java  |    0
 .../osmosis/pgsimple/v0_6/impl/WayReader.java      |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/pgsimple/v0_6/DatasetDriver.java       |    0
 .../pgsimple/v0_6/DatasetDriverFactory.java        |    0
 .../osmosis/pgsimple/v0_6/DatasetDriverPlugin.java |    0
 .../osmosis/pgsimple/v0_6/PostgreSqlTest.java      |    0
 .../pgsimple/v0_6/impl/NodeLocationStoreTest.java  |    0
 .../data/template/v0_6/db-changeset-expected.osm   |   44 +
 .../resources/data/template/v0_6/db-changeset.osc  |    0
 .../data/template/v0_6/db-dataset-expected.osm     |   48 +
 .../resources/data/template/v0_6/db-snapshot.osm   |   46 +
 .../data/template/v0_6/pgsql-authfile.txt          |    0
 osmosis-pgsnapshot/.checkstyle                     |   10 +
 osmosis-pgsnapshot/.gitignore                      |    6 +
 osmosis-pgsnapshot/build.gradle                    |   16 +
 .../osmosis/pgsnapshot/PgSnapshotPluginLoader.java |    0
 .../common/CompactPersistentNodeLocation.java      |    0
 .../common/CompactPersistentNodeLocationStore.java |    0
 .../osmosis/pgsnapshot/common/CopyFileWriter.java  |    0
 .../pgsnapshot/common/DataSourceManager.java       |    0
 .../osmosis/pgsnapshot/common/DatabaseContext.java |  307 +
 .../common/InMemoryNodeLocationStore.java          |    0
 .../pgsnapshot/common/NoSuchRecordException.java   |    0
 .../osmosis/pgsnapshot/common/NodeLocation.java    |    0
 .../pgsnapshot/common/NodeLocationStore.java       |    0
 .../pgsnapshot/common/NodeLocationStoreType.java   |    0
 .../common/PersistentNodeLocationStore.java        |    0
 .../osmosis/pgsnapshot/common/PointBuilder.java    |    0
 .../osmosis/pgsnapshot/common/PolygonBuilder.java  |    0
 .../common/RowMapperRowCallbackListener.java       |    0
 .../pgsnapshot/common/SchemaVersionValidator.java  |   74 +
 .../pgsnapshot/v0_6/PostgreSqlChangeWriter.java    |  125 +
 .../v0_6/PostgreSqlChangeWriterFactory.java        |   46 +
 .../pgsnapshot/v0_6/PostgreSqlCopyWriter.java      |  141 +
 .../v0_6/PostgreSqlCopyWriterFactory.java          |   48 +
 .../pgsnapshot/v0_6/PostgreSqlDatasetReader.java   |    0
 .../v0_6/PostgreSqlDatasetReaderFactory.java       |    0
 .../pgsnapshot/v0_6/PostgreSqlDumpWriter.java      |   90 +
 .../v0_6/PostgreSqlDumpWriterFactory.java          |   65 +
 .../pgsnapshot/v0_6/PostgreSqlTruncator.java       |   83 +
 .../v0_6/PostgreSqlTruncatorFactory.java           |    0
 .../v0_6/PostgreSqlVersionConstants.java           |    0
 .../pgsnapshot/v0_6/impl/ActionChangeWriter.java   |   72 +
 .../osmosis/pgsnapshot/v0_6/impl/ActionDao.java    |   56 +
 .../pgsnapshot/v0_6/impl/ActionDataType.java       |    0
 .../osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java |  231 +
 .../pgsnapshot/v0_6/impl/ChangesetAction.java      |    0
 .../osmosis/pgsnapshot/v0_6/impl/CopyFileset.java  |    0
 .../pgsnapshot/v0_6/impl/CopyFilesetBuilder.java   |  265 +
 .../pgsnapshot/v0_6/impl/CopyFilesetLoader.java    |  127 +
 .../v0_6/impl/DatabaseCapabilityChecker.java       |    0
 .../v0_6/impl/DbOrderedFeatureComparator.java      |    0
 .../pgsnapshot/v0_6/impl/DirectoryCopyFileset.java |    0
 .../osmosis/pgsnapshot/v0_6/impl/EntityDao.java    |  258 +
 .../pgsnapshot/v0_6/impl/EntityFeatureDao.java     |  108 +
 .../pgsnapshot/v0_6/impl/EntityFeatureMapper.java  |    0
 .../osmosis/pgsnapshot/v0_6/impl/EntityMapper.java |    0
 .../osmosis/pgsnapshot/v0_6/impl/EntityReader.java |    0
 .../pgsnapshot/v0_6/impl/EntityRowMapper.java      |    0
 .../pgsnapshot/v0_6/impl/FeaturePopulatorImpl.java |    0
 .../osmosis/pgsnapshot/v0_6/impl/IndexManager.java |  155 +
 .../v0_6/impl/MemberTypeValueMapper.java           |    0
 .../osmosis/pgsnapshot/v0_6/impl/NodeDao.java      |   84 +
 .../osmosis/pgsnapshot/v0_6/impl/NodeMapper.java   |    0
 .../pgsnapshot/v0_6/impl/NodeRowMapper.java        |    0
 .../v0_6/impl/PostgreSqlDatasetContext.java        |  441 +
 .../v0_6/impl/PostgreSqlEntityManager.java         |    0
 .../osmosis/pgsnapshot/v0_6/impl/RelationDao.java  |  195 +
 .../pgsnapshot/v0_6/impl/RelationMapper.java       |    0
 .../pgsnapshot/v0_6/impl/RelationMemberMapper.java |    0
 .../v0_6/impl/RelationMemberRowMapper.java         |    0
 .../pgsnapshot/v0_6/impl/RelationRowMapper.java    |    0
 .../pgsnapshot/v0_6/impl/TempCopyFileset.java      |    0
 .../osmosis/pgsnapshot/v0_6/impl/UserDao.java      |   87 +
 .../pgsnapshot/v0_6/impl/UserRowMapper.java        |    0
 .../osmosis/pgsnapshot/v0_6/impl/WayDao.java       |  168 +
 .../pgsnapshot/v0_6/impl/WayGeometryBuilder.java   |  223 +
 .../osmosis/pgsnapshot/v0_6/impl/WayMapper.java    |    0
 .../pgsnapshot/v0_6/impl/WayNodeMapper.java        |    0
 .../pgsnapshot/v0_6/impl/WayNodeRowMapper.java     |    0
 .../pgsnapshot/v0_6/impl/WayNodesArray.java        |    0
 .../osmosis/pgsnapshot/v0_6/impl/WayRowMapper.java |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/pgsnapshot/v0_6/DatasetDriver.java     |    0
 .../pgsnapshot/v0_6/DatasetDriverFactory.java      |    0
 .../pgsnapshot/v0_6/DatasetDriverPlugin.java       |    0
 .../osmosis/pgsnapshot/v0_6/PostgreSqlTest.java    |  278 +
 .../v0_6/impl/NodeLocationStoreTest.java           |    0
 .../data/template/v0_6/db-changeset-expected.osm   |   48 +
 .../resources/data/template/v0_6/db-changeset.osc  |   43 +
 .../data/template/v0_6/db-dataset-expected.osm     |   52 +
 .../resources/data/template/v0_6/db-snapshot.osm   |   50 +
 .../data/template/v0_6/pgsql-authfile.txt          |    0
 osmosis-replication-http/.checkstyle               |   10 +
 osmosis-replication-http/.gitignore                |    6 +
 osmosis-replication-http/build.gradle              |    7 +
 osmosis-replication-http/readme.txt                |   17 +
 .../ReplicationHttpPluginLoader.java               |   43 +
 .../v0_6/ReplicationDataClient.java                |  127 +
 .../v0_6/ReplicationDataClientFactory.java         |   57 +
 .../v0_6/ReplicationDataServer.java                |  111 +
 .../v0_6/ReplicationDataServerFactory.java         |   51 +
 .../v0_6/ReplicationSequenceServer.java            |  127 +
 .../v0_6/ReplicationSequenceServerFactory.java     |   40 +
 .../v0_6/impl/ChunkedDataReceiver.java             |  220 +
 ...eplicationDataClientChannelPipelineFactory.java |   47 +
 .../v0_6/impl/ReplicationDataClientHandler.java    |  231 +
 ...eplicationDataServerChannelPipelineFactory.java |   32 +
 .../v0_6/impl/ReplicationDataServerHandler.java    |  468 ++
 .../replicationhttp/v0_6/impl/SequenceClient.java  |  102 +
 .../impl/SequenceClientChannelPipelineFactory.java |   47 +
 .../v0_6/impl/SequenceClientControl.java           |   17 +
 .../v0_6/impl/SequenceClientHandler.java           |  137 +
 .../v0_6/impl/SequenceClientRestartManager.java    |  152 +
 ...SequenceNumberClientChannelPipelineFactory.java |   38 +
 .../v0_6/impl/SequenceNumberClientHandler.java     |   60 +
 .../v0_6/impl/SequenceNumberClientListener.java    |   20 +
 ...SequenceNumberServerChannelPipelineFactory.java |   15 +
 .../v0_6/impl/SequenceNumberServerHandler.java     |  118 +
 .../replicationhttp/v0_6/impl/SequenceServer.java  |  393 +
 .../impl/SequenceServerChannelPipelineFactory.java |   45 +
 .../v0_6/impl/SequenceServerControl.java           |   57 +
 .../v0_6/impl/SequenceServerHandler.java           |  369 +
 .../v0_6/impl/ServerStatistics.java                |   46 +
 .../src/main/resources/osmosis-plugins.conf        |    1 +
 .../v0_6/MockReplicationDestination.java           |  105 +
 .../v0_6/MockReplicationSource.java                |   80 +
 .../v0_6/ReplicationSequenceServerTest.java        |   46 +
 .../replicationhttp/v0_6/ReplicationTest.java      |  160 +
 osmosis-replication/.checkstyle                    |   10 +
 osmosis-replication/.gitignore                     |    6 +
 osmosis-replication/build.gradle                   |    5 +
 .../replication/ReplicationPluginLoader.java       |   67 +
 .../replication/common/FileReplicationStore.java   |   80 +
 .../common/ReplicationFileSequenceFormatter.java   |    0
 .../common/ReplicationSequenceFormatter.java       |    0
 .../replication/common/ReplicationState.java       |  189 +
 .../replication/common/ReplicationStore.java       |   63 +
 .../replication/common/ServerStateReader.java      |  126 +
 .../replication/common/TimestampTracker.java       |    0
 .../v0_6/BaseReplicationDownloader.java            |  389 +
 .../replication/v0_6/IntervalDownloader.java       |  413 +
 .../v0_6/IntervalDownloaderFactory.java            |    0
 .../v0_6/IntervalDownloaderInitializer.java        |    0
 .../v0_6/IntervalDownloaderInitializerFactory.java |    0
 .../replication/v0_6/ReplicationDownloader.java    |  127 +
 .../v0_6/ReplicationDownloaderFactory.java         |    0
 .../v0_6/ReplicationDownloaderInitializer.java     |    0
 .../ReplicationDownloaderInitializerFactory.java   |    0
 .../replication/v0_6/ReplicationFileMerger.java    |  328 +
 .../v0_6/ReplicationFileMergerFactory.java         |    0
 .../v0_6/ReplicationFileMergerInitializer.java     |    0
 .../ReplicationFileMergerInitializerFactory.java   |    0
 .../replication/v0_6/ReplicationLagReader.java     |  159 +
 .../v0_6/ReplicationLagReaderFactory.java          |    0
 .../replication/v0_6/ReplicationStateWriter.java   |  121 +
 .../v0_6/ReplicationToChangeWriter.java            |   96 +
 .../v0_6/ReplicationToChangeWriterFactory.java     |   40 +
 .../replication/v0_6/ReplicationWriter.java        |   90 +
 .../replication/v0_6/ReplicationWriterFactory.java |   40 +
 .../v0_6/impl/ChangesetFileNameFormatter.java      |    0
 .../v0_6/impl/IntervalDownloaderConfiguration.java |    0
 .../impl/ReplicationDownloaderConfiguration.java   |    0
 .../impl/ReplicationFileMergerConfiguration.java   |    0
 .../v0_6/impl/intervalConfiguration.txt            |    0
 .../impl/replicationDownloaderConfiguration.txt    |    0
 .../impl/replicationFileMergerConfiguration.txt    |    0
 .../src/main/resources/osmosis-plugins.conf        |    0
 osmosis-set/.checkstyle                            |   10 +
 osmosis-set/.gitignore                             |    6 +
 osmosis-set/build.gradle                           |    5 +
 .../openstreetmap/osmosis/set/SetPluginLoader.java |   63 +
 .../osmosis/set/v0_6/BoundRemovedAction.java       |    0
 .../osmosis/set/v0_6/ChangeAppender.java           |  116 +
 .../osmosis/set/v0_6/ChangeAppenderFactory.java    |    0
 .../osmosis/set/v0_6/ChangeApplier.java            |  244 +
 .../osmosis/set/v0_6/ChangeApplierFactory.java     |    0
 .../osmosis/set/v0_6/ChangeDeriver.java            |  184 +
 .../osmosis/set/v0_6/ChangeDeriverFactory.java     |    0
 .../osmosis/set/v0_6/ChangeMerger.java             |  208 +
 .../osmosis/set/v0_6/ChangeMergerFactory.java      |    0
 .../osmosis/set/v0_6/ChangeSimplifier.java         |   80 +
 .../osmosis/set/v0_6/ChangeSimplifierFactory.java  |    0
 .../set/v0_6/ChangeToFullHistoryConvertor.java     |   74 +
 .../v0_6/ChangeToFullHistoryConvertorFactory.java  |   28 +
 .../osmosis/set/v0_6/EntityMerger.java             |  296 +
 .../osmosis/set/v0_6/EntityMergerFactory.java      |    0
 .../osmosis/set/v0_6/FlattenFilter.java            |   90 +
 .../osmosis/set/v0_6/FlattenFilterFactory.java     |    0
 .../set/v0_6/impl/ChangeSimplifierImpl.java        |  125 +
 .../set/v0_6/impl/DataPostboxChangeSink.java       |   65 +
 .../osmosis/set/v0_6/impl/DataPostboxSink.java     |   65 +
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/set/v0_6/ChangeAppenderTest.java       |    0
 .../osmosis/set/v0_6/ChangeApplierTest.java        |  223 +
 .../osmosis/set/v0_6/ChangeDeriverTest.java        |  135 +
 .../osmosis/set/v0_6/ChangeSimplifierTest.java     |  203 +
 .../set/v0_6/ChangeToFullHistoryConvertorTest.java |   45 +
 .../osmosis/set/v0_6/EntityMergerTest.java         |  408 +
 .../osmosis/set/v0_6/FlattenFilterTest.java        |  102 +
 .../osmosis/set/v0_6/MergeBoundTest.java           |  236 +
 .../data/template/v0_6/append-change-in1.osc       |    0
 .../data/template/v0_6/append-change-in2.osc       |    0
 .../data/template/v0_6/append-change-out.osc       |    0
 .../v0_6/apply_change/apply-change-base-high.osm   |   15 +
 .../apply_change/apply-change-base-node-only.osm   |    6 +
 .../v0_6/apply_change/apply-change-base.osm        |   15 +
 .../v0_6/apply_change/apply-change-big.osm         |   24 +
 .../v0_6/apply_change/apply-change-create.osm      |   18 +
 .../v0_6/apply_change/apply-change-delete.osm      |   12 +
 .../apply_change/apply-change-modify-higher.osm    |   15 +
 .../apply-change-modify-nonexistent.osm            |   18 +
 .../v0_6/apply_change/apply-change-modify.osm      |   15 +
 .../v0_6/apply_change/change-big-create.osc        |   23 +
 .../v0_6/apply_change/change-big-delete.osc        |   23 +
 .../v0_6/apply_change/change-create-existent.osc   |    8 +
 .../apply_change/change-create-modify-delete.osc   |   18 +
 .../template/v0_6/apply_change/change-create.osc   |    8 +
 .../apply_change/change-delete-nonexistent.osc     |    7 +
 .../template/v0_6/apply_change/change-delete.osc   |    7 +
 .../apply_change/change-modify-nonexistent.osc     |    8 +
 .../template/v0_6/apply_change/change-modify.osc   |    8 +
 .../template/v0_6/change-to-full-history-in.osc    |   45 +
 .../template/v0_6/change-to-full-history-out.osm   |   39 +
 .../template/v0_6/derive_change/full-create.osc    |   17 +
 .../data/template/v0_6/derive_change/simple.osm    |   15 +
 .../resources/data/template/v0_6/empty-change.osc  |    3 +
 .../resources/data/template/v0_6/empty-entity.osm  |    3 +
 .../resources/data/template/v0_6/flatten-in.osm    |   39 +
 .../resources/data/template/v0_6/flatten-out.osm   |   15 +
 .../data/template/v0_6/merge/merge-in-1.osm        |   15 +
 .../template/v0_6/merge/merge-in-2-disjunct.osm    |   15 +
 .../v0_6/merge/merge-in-2-secondSource.osm         |   12 +
 .../template/v0_6/merge/merge-in-2-timestamp.osm   |   12 +
 .../template/v0_6/merge/merge-in-2-version.osm     |   12 +
 .../template/v0_6/merge/merge-in-badorder-id.osm   |   21 +
 .../template/v0_6/merge/merge-in-badorder-type.osm |   15 +
 .../template/v0_6/merge/merge-out-disjunct.osm     |   27 +
 .../template/v0_6/merge/merge-out-secondSource.osm |   17 +
 .../template/v0_6/merge/merge-out-timestamp.osm    |   16 +
 .../data/template/v0_6/merge/merge-out-version.osm |   16 +
 .../data/template/v0_6/simplify-change-in.osc      |    0
 .../data/template/v0_6/simplify-change-out.osc     |    0
 osmosis-tagfilter/.checkstyle                      |   10 +
 osmosis-tagfilter/.gitignore                       |    6 +
 osmosis-tagfilter/build.gradle                     |    5 +
 .../osmosis/tagfilter/TagFilterPluginLoader.java   |    0
 .../tagfilter/common/KeyValueFileReader.java       |   89 +
 .../osmosis/tagfilter/v0_6/NodeKeyFilter.java      |  131 +
 .../tagfilter/v0_6/NodeKeyFilterFactory.java       |    0
 .../osmosis/tagfilter/v0_6/NodeKeyValueFilter.java |  165 +
 .../tagfilter/v0_6/NodeKeyValueFilterFactory.java  |   44 +
 .../osmosis/tagfilter/v0_6/TagFilter.java          |  168 +
 .../osmosis/tagfilter/v0_6/TagFilterFactory.java   |    0
 .../osmosis/tagfilter/v0_6/TagRemover.java         |  111 +
 .../osmosis/tagfilter/v0_6/TagRemoverFactory.java  |    0
 .../osmosis/tagfilter/v0_6/UsedNodeFilter.java     |  195 +
 .../tagfilter/v0_6/UsedNodeFilterFactory.java      |   51 +
 .../osmosis/tagfilter/v0_6/UsedWayFilter.java      |  182 +
 .../tagfilter/v0_6/UsedWayFilterFactory.java       |   50 +
 .../osmosis/tagfilter/v0_6/WayKeyFilter.java       |  132 +
 .../tagfilter/v0_6/WayKeyFilterFactory.java        |    0
 .../osmosis/tagfilter/v0_6/WayKeyValueFilter.java  |  167 +
 .../tagfilter/v0_6/WayKeyValueFilterFactory.java   |   50 +
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../tagfilter/common/KeyValueFileReaderTest.java   |   47 +
 .../tagfilter/v0_6/NodeKeyValueFilterTest.java     |   69 +
 .../osmosis/tagfilter/v0_6/TagFilterTest.java      |  168 +
 .../osmosis/tagfilter/v0_6/TagRemoverTest.java     |    0
 .../tagfilter/v0_6/WayKeyValueFilterTest.java      |   68 +
 .../data/template/v0_6/allowed-key-values.txt      |    2 +
 .../v0_6/node-key-value-filter-expected.osm        |   19 +
 .../v0_6/node-key-value-filter-snapshot.osm        |   53 +
 .../data/template/v0_6/tag-remove-expected.osm     |   33 +
 .../data/template/v0_6/tag-remove-snapshot.osm     |   41 +
 .../v0_6/way-key-value-filter-expected.osm         |   29 +
 .../v0_6/way-key-value-filter-snapshot.osm         |   41 +
 osmosis-tagtransform/.checkstyle                   |   10 +
 osmosis-tagtransform/.gitignore                    |    6 +
 osmosis-tagtransform/build.gradle                  |   12 +
 .../openstreetmap/osmosis/tagtransform/Match.java  |   20 +
 .../osmosis/tagtransform/Matcher.java              |   15 +
 .../openstreetmap/osmosis/tagtransform/Output.java |   12 +
 .../osmosis/tagtransform/StatsSaveException.java   |   13 +
 .../osmosis/tagtransform/TTEntityType.java         |   26 +
 .../osmosis/tagtransform/TransformPlugin.java      |   31 +
 .../osmosis/tagtransform/Translation.java          |   21 +
 .../osmosis/tagtransform/impl/AndMatcher.java      |   67 +
 .../osmosis/tagtransform/impl/CopyAll.java         |   19 +
 .../osmosis/tagtransform/impl/CopyMatched.java     |   23 +
 .../osmosis/tagtransform/impl/CopyUnmatched.java   |   27 +
 .../tagtransform/impl/MatchResultMatch.java        |   52 +
 .../osmosis/tagtransform/impl/NoTagMatcher.java    |   86 +
 .../osmosis/tagtransform/impl/OrMatcher.java       |   69 +
 .../osmosis/tagtransform/impl/TagMatcher.java      |   68 +
 .../osmosis/tagtransform/impl/TagOutput.java       |   68 +
 .../osmosis/tagtransform/impl/TransformHelper.java |  153 +
 .../tagtransform/impl/TransformLoadException.java  |   12 +
 .../osmosis/tagtransform/impl/TransformLoader.java |  181 +
 .../osmosis/tagtransform/impl/TranslationImpl.java |   91 +
 .../tagtransform/v0_6/TransformChangeTask.java     |   38 +
 .../v0_6/TransformChangeTaskFactory.java           |   21 +
 .../osmosis/tagtransform/v0_6/TransformTask.java   |   25 +
 .../tagtransform/v0_6/TransformTaskFactory.java    |   22 +
 .../src/main/resources/osmosis-plugins.conf        |    1 +
 .../tagtransform/v0_6/TagTransformTest.java        |   55 +
 .../test/resources/data/template/v0_6/test-in.osm  |   56 +
 .../test/resources/data/template/v0_6/test-out.osm |   61 +
 .../resources/data/template/v0_6/translation.xml   |  190 +
 osmosis-testutil/.checkstyle                       |   10 +
 osmosis-testutil/.gitignore                        |    6 +
 osmosis-testutil/build.gradle                      |    4 +
 .../osmosis/testutil/AbstractDataTest.java         |    0
 .../osmosis/testutil/TestDataUtilities.java        |    0
 .../osmosis/testutil/v0_6/RunTaskUtilities.java    |  118 +
 .../osmosis/testutil/v0_6/SinkChangeInspector.java |   70 +
 .../osmosis/testutil/v0_6/SinkEntityInspector.java |   91 +
 osmosis-xml/.checkstyle                            |   10 +
 osmosis-xml/.gitignore                             |    6 +
 osmosis-xml/build.gradle                           |    5 +
 .../openstreetmap/osmosis/xml/XmlPluginLoader.java |    0
 .../osmosis/xml/common/BaseElementProcessor.java   |    0
 .../osmosis/xml/common/BaseXmlWriter.java          |  234 +
 .../osmosis/xml/common/CompressionActivator.java   |    0
 .../osmosis/xml/common/CompressionMethod.java      |    0
 .../xml/common/CompressionMethodDeriver.java       |    0
 .../osmosis/xml/common/DummyElementProcessor.java  |    0
 .../osmosis/xml/common/ElementProcessor.java       |    0
 .../osmosis/xml/common/ElementWriter.java          |    0
 .../osmosis/xml/common/XmlTaskManagerFactory.java  |    0
 .../osmosis/xml/common/XmlTimestampFormat.java     |    0
 .../osmosis/xml/v0_6/FastXmlReader.java            |  116 +
 .../osmosis/xml/v0_6/FastXmlReaderFactory.java     |    0
 .../osmosis/xml/v0_6/XmlChangeReader.java          |  138 +
 .../osmosis/xml/v0_6/XmlChangeReaderFactory.java   |    0
 .../osmosis/xml/v0_6/XmlChangeUploader.java        |  317 +
 .../osmosis/xml/v0_6/XmlChangeUploaderFactory.java |    0
 .../osmosis/xml/v0_6/XmlChangeWriter.java          |   84 +
 .../osmosis/xml/v0_6/XmlChangeWriterFactory.java   |    0
 .../osmosis/xml/v0_6/XmlDownloader.java            |  273 +
 .../osmosis/xml/v0_6/XmlDownloaderFactory.java     |    0
 .../openstreetmap/osmosis/xml/v0_6/XmlReader.java  |  140 +
 .../osmosis/xml/v0_6/XmlReaderFactory.java         |    0
 .../openstreetmap/osmosis/xml/v0_6/XmlWriter.java  |  104 +
 .../osmosis/xml/v0_6/XmlWriterFactory.java         |   53 +
 .../osmosis/xml/v0_6/impl/BoundWriter.java         |   92 +
 .../xml/v0_6/impl/BoundsElementProcessor.java      |   88 +
 .../v0_6/impl/ChangeSourceElementProcessor.java    |  163 +
 .../xml/v0_6/impl/EntityElementProcessor.java      |    0
 .../osmosis/xml/v0_6/impl/EntityWriter.java        |   65 +
 .../osmosis/xml/v0_6/impl/FastXmlParser.java       |  439 +
 .../xml/v0_6/impl/LegacyBoundElementProcessor.java |   89 +
 .../osmosis/xml/v0_6/impl/MemberTypeParser.java    |    0
 .../osmosis/xml/v0_6/impl/MemberTypeRenderer.java  |    0
 .../xml/v0_6/impl/NodeElementProcessor.java        |  174 +
 .../osmosis/xml/v0_6/impl/NodeWriter.java          |   97 +
 .../osmosis/xml/v0_6/impl/OsmChangeHandler.java    |    0
 .../osmosis/xml/v0_6/impl/OsmChangeWriter.java     |  153 +
 .../osmosis/xml/v0_6/impl/OsmElementProcessor.java |  156 +
 .../osmosis/xml/v0_6/impl/OsmHandler.java          |    0
 .../osmosis/xml/v0_6/impl/OsmWriter.java           |  192 +
 .../xml/v0_6/impl/RelationElementProcessor.java    |    0
 .../v0_6/impl/RelationMemberElementProcessor.java  |    0
 .../xml/v0_6/impl/RelationMemberListener.java      |    0
 .../xml/v0_6/impl/RelationMemberWriter.java        |    0
 .../osmosis/xml/v0_6/impl/RelationWriter.java      |   91 +
 .../xml/v0_6/impl/SourceElementProcessor.java      |    0
 .../osmosis/xml/v0_6/impl/TagElementProcessor.java |    0
 .../osmosis/xml/v0_6/impl/TagListener.java         |    0
 .../osmosis/xml/v0_6/impl/TagWriter.java           |    0
 .../osmosis/xml/v0_6/impl/WayElementProcessor.java |    0
 .../xml/v0_6/impl/WayNodeElementProcessor.java     |    0
 .../osmosis/xml/v0_6/impl/WayNodeListener.java     |    0
 .../osmosis/xml/v0_6/impl/WayNodeWriter.java       |    0
 .../osmosis/xml/v0_6/impl/WayWriter.java           |   91 +
 .../osmosis/xml/v0_6/impl/XmlConstants.java        |   56 +
 .../src/main/resources/osmosis-plugins.conf        |    0
 .../osmosis/xml/common/ElementWriterTest.java      |    0
 .../xml/v0_6/XmlChangeReaderWriterTest.java        |   91 +
 .../osmosis/xml/v0_6/XmlReaderWriterTest.java      |    0
 .../osmosis/xml/v0_6/impl/BoundWriterTest.java     |   88 +
 .../osmosis/xml/v0_6/impl/NodeWriterTest.java      |    0
 .../osmosis/xml/v0_6/impl/OsmHandlerTest.java      |  281 +
 .../osmosis/xml/v0_6/impl/OsmWriterTest.java       |  183 +
 .../osmosis/xml/v0_6/impl/RelationWriterTest.java  |    0
 .../osmosis/xml/v0_6/impl/WayWriterTest.java       |    0
 .../template/v0_6/xml-create-no-coordinates.osc    |    6 +
 .../template/v0_6/xml-delete-no-coordinates.osc    |    6 +
 .../data/template/v0_6/xml-task-tests-v0_6.osc     |    0
 .../data/template/v0_6/xml-task-tests-v0_6.osm     |    0
 package/.gitignore                                 |    5 +-
 package/.project                                   |   11 -
 package/bin/osmosis                                |    2 +-
 package/build.gradle                               |   73 +
 package/build.xml                                  |   54 -
 package/changes.txt                                |   59 +
 package/copying.txt                                |    3 +-
 package/ivy.xml                                    |   42 -
 package/script/contrib/apidb_0.6.sql               |    9 +-
 package/script/pgsimple_load_0.6.sql               |    2 +-
 package/script/pgsnapshot_load_0.6.sql             |    2 +-
 package/script/pgsnapshot_schema_0.6.sql           |    2 +-
 pbf/.checkstyle                                    |   10 -
 pbf/.classpath                                     |   23 -
 pbf/.gitignore                                     |    5 -
 pbf/.project                                       |   23 -
 pbf/build.xml                                      |    7 -
 pbf/ivy.xml                                        |   35 -
 .../crosby/binary/osmosis/OsmosisBinaryParser.java |  261 -
 .../java/crosby/binary/osmosis/OsmosisReader.java  |   47 -
 .../crosby/binary/osmosis/OsmosisSerializer.java   |  500 --
 .../binary/osmosis/OsmosisSerializerFactory.java   |   61 -
 pgsimple/.checkstyle                               |   10 -
 pgsimple/.classpath                                |   29 -
 pgsimple/.gitignore                                |    5 -
 pgsimple/.project                                  |   23 -
 pgsimple/build.xml                                 |   10 -
 pgsimple/ivy.xml                                   |   43 -
 .../pgsimple/v0_6/PostgreSqlChangeWriter.java      |   93 -
 .../pgsimple/v0_6/PostgreSqlCopyWriter.java        |  124 -
 .../pgsimple/v0_6/PostgreSqlDumpWriter.java        |   76 -
 .../osmosis/pgsimple/v0_6/PostgreSqlWriter.java    |  844 --
 .../pgsimple/v0_6/impl/CopyFilesetBuilder.java     |  250 -
 .../osmosis/pgsimple/v0_6/impl/IndexManager.java   |  158 -
 .../osmosis/pgsimple/v0_6/impl/NodeDao.java        |  119 -
 .../osmosis/pgsimple/v0_6/impl/WayDao.java         |  182 -
 .../data/template/v0_6/db-changeset-expected.osm   |   44 -
 .../data/template/v0_6/db-dataset-expected.osm     |   48 -
 .../resources/data/template/v0_6/db-snapshot.osm   |   46 -
 pgsnapshot/.checkstyle                             |   10 -
 pgsnapshot/.classpath                              |   41 -
 pgsnapshot/.gitignore                              |    5 -
 pgsnapshot/.project                                |   23 -
 pgsnapshot/build.xml                               |    7 -
 pgsnapshot/ivy.xml                                 |   44 -
 .../osmosis/pgsnapshot/common/DatabaseContext.java |  320 -
 .../pgsnapshot/common/SchemaVersionValidator.java  |   74 -
 .../pgsnapshot/v0_6/PostgreSqlChangeWriter.java    |  109 -
 .../v0_6/PostgreSqlChangeWriterFactory.java        |   40 -
 .../pgsnapshot/v0_6/PostgreSqlCopyWriter.java      |  126 -
 .../v0_6/PostgreSqlCopyWriterFactory.java          |   44 -
 .../pgsnapshot/v0_6/PostgreSqlDumpWriter.java      |   76 -
 .../v0_6/PostgreSqlDumpWriterFactory.java          |   60 -
 .../pgsnapshot/v0_6/PostgreSqlTruncator.java       |   83 -
 .../pgsnapshot/v0_6/impl/ActionChangeWriter.java   |   66 -
 .../osmosis/pgsnapshot/v0_6/impl/ActionDao.java    |   56 -
 .../osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java |  221 -
 .../pgsnapshot/v0_6/impl/CopyFilesetBuilder.java   |  249 -
 .../pgsnapshot/v0_6/impl/CopyFilesetLoader.java    |  127 -
 .../osmosis/pgsnapshot/v0_6/impl/EntityDao.java    |  255 -
 .../pgsnapshot/v0_6/impl/EntityFeatureDao.java     |  105 -
 .../osmosis/pgsnapshot/v0_6/impl/IndexManager.java |  155 -
 .../osmosis/pgsnapshot/v0_6/impl/NodeDao.java      |   84 -
 .../v0_6/impl/PostgreSqlDatasetContext.java        |  441 -
 .../osmosis/pgsnapshot/v0_6/impl/RelationDao.java  |  195 -
 .../osmosis/pgsnapshot/v0_6/impl/UserDao.java      |   87 -
 .../osmosis/pgsnapshot/v0_6/impl/WayDao.java       |  168 -
 .../pgsnapshot/v0_6/impl/WayGeometryBuilder.java   |  229 -
 .../osmosis/pgsnapshot/v0_6/PostgreSqlTest.java    |  277 -
 .../data/template/v0_6/db-changeset-expected.osm   |   44 -
 .../resources/data/template/v0_6/db-changeset.osc  |   40 -
 .../data/template/v0_6/db-dataset-expected.osm     |   48 -
 .../resources/data/template/v0_6/db-snapshot.osm   |   46 -
 replication/.checkstyle                            |   10 -
 replication/.classpath                             |   24 -
 replication/.gitignore                             |    5 -
 replication/.project                               |   23 -
 replication/build.xml                              |    7 -
 replication/ivy.xml                                |   34 -
 .../replication/ReplicationPluginLoader.java       |   59 -
 .../common/FileReplicationStatePersistor.java      |   57 -
 .../replication/common/ReplicationState.java       |  179 -
 .../common/ReplicationStatePersister.java          |   33 -
 .../replication/common/ServerStateReader.java      |  117 -
 .../v0_6/BaseReplicationDownloader.java            |  357 -
 .../replication/v0_6/IntervalDownloader.java       |  410 -
 .../replication/v0_6/ReplicationDownloader.java    |  104 -
 .../replication/v0_6/ReplicationFileMerger.java    |  301 -
 .../replication/v0_6/ReplicationLagReader.java     |  162 -
 set/.checkstyle                                    |   10 -
 set/.classpath                                     |   26 -
 set/.gitignore                                     |    5 -
 set/.project                                       |   23 -
 set/build.xml                                      |    7 -
 set/ivy.xml                                        |   35 -
 .../openstreetmap/osmosis/set/SetPluginLoader.java |   59 -
 .../osmosis/set/v0_6/ChangeAppender.java           |  102 -
 .../osmosis/set/v0_6/ChangeApplier.java            |  235 -
 .../osmosis/set/v0_6/ChangeDeriver.java            |  178 -
 .../osmosis/set/v0_6/ChangeMerger.java             |  202 -
 .../osmosis/set/v0_6/ChangeSimplifier.java         |   69 -
 .../osmosis/set/v0_6/EntityMerger.java             |  291 -
 .../osmosis/set/v0_6/FlattenFilter.java            |   82 -
 .../set/v0_6/impl/ChangeSimplifierImpl.java        |  115 -
 .../set/v0_6/impl/DataPostboxChangeSink.java       |   54 -
 .../osmosis/set/v0_6/impl/DataPostboxSink.java     |   54 -
 .../osmosis/set/v0_6/ChangeSimplifierTest.java     |   49 -
 .../osmosis/set/v0_6/MergeBoundTest.java           |  272 -
 .../test/task/v0_6/SinkEntityInspector.java        |   81 -
 settings.gradle                                    |   20 +
 tagfilter/.checkstyle                              |   10 -
 tagfilter/.classpath                               |   25 -
 tagfilter/.gitignore                               |    5 -
 tagfilter/.project                                 |   23 -
 tagfilter/build.xml                                |    7 -
 tagfilter/ivy.xml                                  |   35 -
 .../osmosis/tagfilter/v0_6/NodeKeyFilter.java      |  122 -
 .../osmosis/tagfilter/v0_6/NodeKeyValueFilter.java |  123 -
 .../tagfilter/v0_6/NodeKeyValueFilterFactory.java  |   31 -
 .../osmosis/tagfilter/v0_6/TagFilter.java          |  160 -
 .../osmosis/tagfilter/v0_6/TagRemover.java         |  102 -
 .../osmosis/tagfilter/v0_6/UsedNodeFilter.java     |  185 -
 .../tagfilter/v0_6/UsedNodeFilterFactory.java      |   67 -
 .../osmosis/tagfilter/v0_6/UsedWayFilter.java      |  172 -
 .../tagfilter/v0_6/UsedWayFilterFactory.java       |   67 -
 .../osmosis/tagfilter/v0_6/WayKeyFilter.java       |  123 -
 .../osmosis/tagfilter/v0_6/WayKeyValueFilter.java  |  125 -
 .../tagfilter/v0_6/WayKeyValueFilterFactory.java   |   32 -
 .../osmosis/tagfilter/v0_6/TagFilterTest.java      |  168 -
 .../test/task/v0_6/SinkEntityInspector.java        |   81 -
 .../data/template/v0_6/tag-remove-expected.osm     |   33 -
 .../data/template/v0_6/tag-remove-snapshot.osm     |   41 -
 testutil/.checkstyle                               |   10 -
 testutil/.classpath                                |   15 -
 testutil/.gitignore                                |    5 -
 testutil/.project                                  |   23 -
 testutil/build.xml                                 |    7 -
 testutil/ivy.xml                                   |   32 -
 xml/.checkstyle                                    |   10 -
 xml/.classpath                                     |   25 -
 xml/.gitignore                                     |    5 -
 xml/.project                                       |   23 -
 xml/build.xml                                      |    7 -
 xml/ivy.xml                                        |   35 -
 .../osmosis/xml/common/BaseXmlWriter.java          |  218 -
 .../osmosis/xml/v0_6/FastXmlReader.java            |  113 -
 .../osmosis/xml/v0_6/XmlChangeReader.java          |  136 -
 .../osmosis/xml/v0_6/XmlChangeUploader.java        |  308 -
 .../osmosis/xml/v0_6/XmlChangeWriter.java          |   70 -
 .../osmosis/xml/v0_6/XmlDownloader.java            |  270 -
 .../openstreetmap/osmosis/xml/v0_6/XmlReader.java  |  137 -
 .../openstreetmap/osmosis/xml/v0_6/XmlWriter.java  |   87 -
 .../osmosis/xml/v0_6/XmlWriterFactory.java         |   49 -
 .../xml/v0_6/impl/BoundElementProcessor.java       |   89 -
 .../osmosis/xml/v0_6/impl/BoundWriter.java         |   52 -
 .../v0_6/impl/ChangeSourceElementProcessor.java    |  153 -
 .../osmosis/xml/v0_6/impl/FastXmlParser.java       |  393 -
 .../xml/v0_6/impl/NodeElementProcessor.java        |  132 -
 .../osmosis/xml/v0_6/impl/NodeWriter.java          |  106 -
 .../osmosis/xml/v0_6/impl/OsmChangeWriter.java     |  153 -
 .../osmosis/xml/v0_6/impl/OsmElementProcessor.java |  129 -
 .../osmosis/xml/v0_6/impl/OsmWriter.java           |  184 -
 .../osmosis/xml/v0_6/impl/RelationWriter.java      |  106 -
 .../osmosis/xml/v0_6/impl/WayWriter.java           |  106 -
 .../osmosis/xml/v0_6/impl/XmlConstants.java        |   30 -
 .../test/task/v0_6/SinkEntityInspector.java        |   81 -
 .../xml/v0_6/XmlChangeReaderWriterTest.java        |   48 -
 .../osmosis/xml/v0_6/impl/BoundWriterTest.java     |   88 -
 .../osmosis/xml/v0_6/impl/OsmHandlerTest.java      |  236 -
 .../osmosis/xml/v0_6/impl/OsmWriterTest.java       |  183 -
 1285 files changed, 52392 insertions(+), 31687 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3ff4c65..5cad606 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-/eclipse
+.gradle
+
diff --git a/README b/README
index e4f6ab4..b815569 100644
--- a/README
+++ b/README
@@ -1,7 +1,50 @@
-Osmosis is a command line Java application for processing Open Street Map (http://www.openstreetmap.org) data.
+Osmosis is a command line Java application for processing Open Street Map
+(http://www.openstreetmap.org) data.
 
-The tool consists of a series of pluggable components that can be chained together to perform a larger operation. For example, it has components for reading from database and from file, components for writing to database and to file, components for deriving and applying change sets to data sources, components for sorting data, etc. It has been written so that it is easy to add new features without re-writing common tasks such as file or database handling.
+The tool consists of a series of pluggable components that can be chained
+together to perform a larger operation. For example, it has components for
+reading from database and from file, components for writing to database and to
+file, components for deriving and applying change sets to data sources,
+components for sorting data, etc. It has been written so that it is easy to add
+new features without re-writing common tasks such as file or database handling.
 
-More information may be found on the project wiki page.
-http://www.openstreetmap.org
+Some brief build, running and installation notes are provided below, however
+most documentation may be found on the project wiki page.
+http://wiki.openstreetmap.org/wiki/Osmosis
 
+**** BUILD ****
+Osmosis is built using the Gradle (http://gradle.org) built tool, however
+Gradle does not need to be installed.  The only requirements are a 1.6 JDK, and
+an Internet connection.
+
+Below are several commands useful to build the software.  All commands must be
+run from the root of the source tree.
+ 
+Build the software without running unit tests:
+./gradlew assemble
+
+Perform a complete build including unit tests:
+./gradlew build
+
+Clean the build tree:
+./gradlew clean
+
+Verify checkstyle compliance:
+./gradlew checkstyleMain checkstyleTest
+
+**** RUNNING ****
+After completing the build process, a working Osmosis installation is contained
+in the package sub-directory.  The Osmosis launcher scripts reside in the bin
+sub-directory of package.  On a UNIX-like environment use the "osmosis" script,
+on a Windows environment use the "osmosis.bat" script.
+
+However, for installing the software it is recommended to use a distribution
+archive described below.
+
+**** INSTALLATION ****
+After completing the build process, distribution archives in zip and tar gzipped
+formats are contained in the package/build/distribution directory.  These
+archives may be extracted to a location of your choice.  The bin sub-directory
+should either be added to your PATH, or in the case of UNIX-like environments
+the "osmosis" script may be symlinked into an existing directory already on the
+PATH.
diff --git a/apidb/.checkstyle b/apidb/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/apidb/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/apidb/.classpath b/apidb/.classpath
deleted file mode 100644
index 2d58b56..0000000
--- a/apidb/.classpath
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Replication"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/aopalliance-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-dbcp-1.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-pool-1.5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/mysql-connector-java-5.1.18.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-aop-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-asm-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-beans-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-context-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-core-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-expression-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-jdbc-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-tx-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/apidb/.gitignore b/apidb/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/apidb/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/apidb/.project b/apidb/.project
deleted file mode 100644
index 6cfe9e3..0000000
--- a/apidb/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Apidb</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/apidb/build.xml b/apidb/build.xml
deleted file mode 100644
index bcc38b1..0000000
--- a/apidb/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Apidb" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/apidb/ivy.xml b/apidb/ivy.xml
deleted file mode 100644
index a6e55fb..0000000
--- a/apidb/ivy.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-apidb"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-replication" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.springframework" name="spring-jdbc" rev="${dependency.version.spring}" conf="compile->default"/>
-        <dependency org="commons-dbcp" name="commons-dbcp" rev="${dependency.version.commons-dbcp}" conf="compile->default"/>
-    	<dependency org="postgresql" name="postgresql" rev="${dependency.version.postgresql}" conf="compile->default"/>
-    	<dependency org="mysql" name="mysql-connector-java" rev="${dependency.version.mysql}" conf="compile->default"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java
deleted file mode 100644
index 7887a0a..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java
+++ /dev/null
@@ -1,398 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.common;
-
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.commons.dbcp.BasicDataSource;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabaseType;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-import org.springframework.jdbc.datasource.DataSourceTransactionManager;
-import org.springframework.jdbc.datasource.DataSourceUtils;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.support.TransactionCallback;
-import org.springframework.transaction.support.TransactionTemplate;
-
-
-/**
- * This class manages the lifecycle of JDBC objects to minimise the risk of connection leaks and to
- * support a consistent approach to database access.
- * 
- * @author Brett Henderson
- */
-public class DatabaseContext2 {
-
-    private static final Logger LOG = Logger.getLogger(DatabaseContext.class.getName());
-
-    private BasicDataSource dataSource;
-    private PlatformTransactionManager txnManager;
-    private TransactionTemplate txnTemplate;
-    private JdbcTemplate jdbcTemplate;
-    private SimpleJdbcTemplate simpleJdbcTemplate;
-    private DatabaseType dbType;
-    private IdentityValueLoader identityValueLoader;
-    
-
-    /**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     */
-    public DatabaseContext2(DatabaseLoginCredentials loginCredentials) {
-    	dataSource = DataSourceFactory.createDataSource(loginCredentials);
-    	txnManager = new DataSourceTransactionManager(dataSource);
-    	txnTemplate = new TransactionTemplate(txnManager);
-    	jdbcTemplate = new JdbcTemplate(dataSource);
-    	simpleJdbcTemplate = new SimpleJdbcTemplate(jdbcTemplate);
-    	this.dbType = loginCredentials.getDbType();
-    	
-    	setStatementFetchSizeForStreaming();
-
-        switch (loginCredentials.getDbType()) {
-        case POSTGRESQL:
-            identityValueLoader = new PostgresqlIdentityValueLoader2(this);
-            break;
-        case MYSQL:
-            identityValueLoader = new MysqlIdentityValueLoader2(this);
-            break;
-        default:
-            throw new OsmosisRuntimeException("Unknown database type " + loginCredentials.getDbType() + ".");
-        }
-    }
-    
-    
-    /**
-     * Gets the jdbc template which provides simple access to database functions.
-     * 
-     * @return The jdbc template.
-     */
-    public SimpleJdbcTemplate getSimpleJdbcTemplate() {
-    	return simpleJdbcTemplate;
-    }
-    
-    
-    /**
-     * Gets the jdbc template which provides access to database functions.
-     * 
-     * @return The jdbc template.
-     */
-    public JdbcTemplate getJdbcTemplate() {
-    	return jdbcTemplate;
-    }
-    
-    
-	/**
-	 * Invokes the provided callback code within a transaction.
-	 * 
-	 * @param txnCallback
-	 *            The logic to be invoked within a transaction.
-	 * @param <T>
-	 *            The return type of the transaction callback.
-	 * 
-	 * @return The result.
-	 */
-    public <T> Object executeWithinTransaction(TransactionCallback<T> txnCallback) {
-    	return txnTemplate.execute(txnCallback);
-    }
-
-
-	/**
-	 * Returns the database type currently in use. This should only be used when it is not possible
-	 * to write database agnostic statements.
-	 * 
-	 * @return The database type.
-	 */
-    public DatabaseType getDatabaseType() {
-    	return dbType;
-    }
-    
-    
-    private void setStatementFetchSizeForStreaming() {
-    	switch (dbType) {
-        case POSTGRESQL:
-        	jdbcTemplate.setFetchSize(10000);
-			break;
-        case MYSQL:
-        	jdbcTemplate.setFetchSize(Integer.MIN_VALUE);
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-    }
-	
-	
-    /**
-	 * Truncates the contents of the specified tables.
-	 * 
-	 * @param tables
-	 *            The tables to be truncated.
-	 */
-	public void truncateTables(List<String> tables) {
-		switch (dbType) {
-        case POSTGRESQL:
-        	StringBuilder statementBuilder = new StringBuilder();
-    		
-			for (String table : tables) {
-				if (statementBuilder.length() == 0) {
-					statementBuilder.append("TRUNCATE ");
-				} else {
-					statementBuilder.append(", ");
-				}
-				
-				statementBuilder.append(table);
-			}
-			
-			jdbcTemplate.update(statementBuilder.toString());
-			break;
-        case MYSQL:
-			for (String table : tables) {
-				jdbcTemplate.update("TRUNCATE " + table);
-			}
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-	}
-	
-	
-    /**
-	 * Disables the indexes of the specified tables.
-	 * 
-	 * @param tables
-	 *            The tables to disable indexes on.
-	 */
-	public void disableIndexes(List<String> tables) {
-		switch (dbType) {
-        case POSTGRESQL:
-			// There is no way to automatically disable all indexes for a table.
-			break;
-        case MYSQL:
-        	for (String table : tables) {
-        		jdbcTemplate.update("ALTER TABLE " + table + " DISABLE KEYS");
-			}
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-	}
-	
-	
-    /**
-	 * Enables the indexes of the specified tables.
-	 * 
-	 * @param tables
-	 *            The tables to enable indexes on.
-	 */
-	public void enableIndexes(List<String> tables) {
-		switch (dbType) {
-        case POSTGRESQL:
-			// There is no way to automatically disable all indexes for a table.
-			break;
-        case MYSQL:
-        	for (String table : tables) {
-        		jdbcTemplate.update("ALTER TABLE " + table + " ENABLE KEYS");
-			}
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-	}
-	
-	
-    /**
-	 * Locks the specified tables for exclusive access.
-	 * 
-	 * @param tables
-	 *            The tables to lock.
-	 */
-	public void lockTables(List<String> tables) {
-		switch (dbType) {
-        case POSTGRESQL:
-			// Locking tables is not supported.
-			break;
-        case MYSQL:
-        	StringBuilder statementBuilder = new StringBuilder();
-        	
-        	for (String table : tables) {
-        		if (statementBuilder.length() == 0) {
-        			statementBuilder.append("LOCK TABLES ");
-        		} else {
-        			statementBuilder.append(", ");
-        		}
-        		statementBuilder.append(table);
-        		statementBuilder.append(" WRITE");
-			}
-        	
-        	jdbcTemplate.update(statementBuilder.toString());
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-	}
-	
-	
-    /**
-	 * Unlocks the specified tables.
-	 * 
-	 * @param tables
-	 *            The tables to unlock.
-	 */
-	public void unlockTables(List<String> tables) {
-		switch (dbType) {
-        case POSTGRESQL:
-			// Locking tables is not supported.
-			break;
-        case MYSQL:
-        	jdbcTemplate.update("UNLOCK TABLES");
-			break;
-		default:
-			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
-		}
-	}
-
-
-	/**
-	 * Gets the last inserted identity column value. This is a global value and may not work
-	 * correctly if the database uses triggers.
-	 * 
-	 * @return The last inserted identity column value.
-	 */
-	public long getLastInsertId() {
-		return identityValueLoader.getLastInsertId();
-	}
-
-
-	/**
-	 * Gets the last retrieved sequence value. This is specific to the current connection only.
-	 * 
-	 * @param sequenceName
-	 *            The name of the sequence.
-	 * @return The last inserted identity column value.
-	 */
-	public long getLastSequenceId(String sequenceName) {
-		return identityValueLoader.getLastSequenceId(sequenceName);
-	}
-	
-
-    /**
-     * Releases all database resources. This method is guaranteed not to throw transactions and
-     * should always be called in a finally block whenever this class is used.
-     */
-    public void release() {
-    	identityValueLoader.release();
-    	
-    	try {
-			dataSource.close();
-		} catch (SQLException e) {
-			LOG.log(Level.WARNING, "Unable to cleanup the database connection pool.", e);
-		}
-    }
-    
-
-    /**
-     * Indicates if the specified column exists in the database.
-     * 
-     * @param tableName The table to check for.
-     * @param columnName The column to check for.
-     * @return True if the column exists, false otherwise.
-     */
-    public boolean doesColumnExist(String tableName, String columnName) {
-        ResultSet resultSet = null;
-        boolean result;
-
-        try {
-        	Connection connection;
-        	
-            LOG.finest("Checking if column {" + columnName + "} in table {" + tableName + "} exists.");
-
-            // This connection may not be freed if an exception occurs. It's a small chance and the
-			// additional code to avoid it is cumbersome.
-            connection = DataSourceUtils.getConnection(dataSource);
-            
-            resultSet = connection.getMetaData().getColumns(null, null, tableName, columnName);
-            result = resultSet.next();
-            resultSet.close();
-            resultSet = null;
-            
-            DataSourceUtils.releaseConnection(connection, dataSource);
-
-            return result;
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to check for the existence of column " + tableName + "."
-                    + columnName + ".", e);
-        } finally {
-            if (resultSet != null) {
-                try {
-                    resultSet.close();
-                } catch (SQLException e) {
-                    // We are already in an error condition so log and continue.
-                    LOG.log(Level.WARNING, "Unable to close column existence result set.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Indicates if the specified table exists in the database.
-     * 
-     * @param tableName The table to check for.
-     * @return True if the table exists, false otherwise.
-     */
-    public boolean doesTableExist(String tableName) {
-        ResultSet resultSet = null;
-        boolean result;
-
-        try {
-        	Connection connection;
-        	
-            LOG.finest("Checking if table {" + tableName + "} exists.");
-
-            // This connection may not be freed if an exception occurs. It's a small chance and the
-			// additional code to avoid it is cumbersome.
-            connection = DataSourceUtils.getConnection(dataSource);
-
-            resultSet = connection.getMetaData().getTables(null, null, tableName, new String[] {"TABLE"});
-            result = resultSet.next();
-            resultSet.close();
-            resultSet = null;
-            
-            DataSourceUtils.releaseConnection(connection, dataSource);
-
-            return result;
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to check for the existence of table " + tableName + ".", e);
-        } finally {
-            if (resultSet != null) {
-                try {
-                    resultSet.close();
-                } catch (SQLException e) {
-                    // We are already in an error condition so log and continue.
-                    LOG.log(Level.WARNING, "Unable to close table existence result set.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Enforces cleanup of any remaining resources during garbage collection. This is a safeguard
-     * and should not be required if release is called appropriately.
-     * 
-     * @throws Throwable If a problem occurs during finalization.
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        release();
-
-        super.finalize();
-    }
-
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java
deleted file mode 100644
index c7a9a76..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.common;
-
-
-/**
- * Postgresql implementation of an identity value loader.
- * 
- * @author Brett Henderson
- */
-public class PostgresqlIdentityValueLoader2 implements IdentityValueLoader {
-	private static final String SQL_SELECT_LAST_INSERT_ID =
-		"SELECT lastval() AS lastInsertId";
-	private static final String SQL_SELECT_LAST_SEQUENCE_ID =
-		"SELECT currval(?) AS lastSequenceId";
-	
-	private DatabaseContext2 dbCtx;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for all database access.
-	 */
-	public PostgresqlIdentityValueLoader2(DatabaseContext2 dbCtx) {
-		this.dbCtx = dbCtx;
-	}
-	
-	
-	/**
-	 * Returns the id of the most recently inserted row on the current
-	 * connection.
-	 * 
-	 * @return The newly inserted id.
-	 */
-	public long getLastInsertId() {
-		return dbCtx.getJdbcTemplate().queryForLong(SQL_SELECT_LAST_INSERT_ID);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public long getLastSequenceId(String sequenceName) {
-		return dbCtx.getSimpleJdbcTemplate().queryForLong(SQL_SELECT_LAST_SEQUENCE_ID, sequenceName);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Do nothing.
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java
deleted file mode 100644
index 63a58bd..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.util.Date;
-
-import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.DeltaToDiffReader;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-
-
-/**
- * A change source reading from database history tables. This aims to be suitable for running at
- * regular intervals with database overhead proportional to changeset size.
- * 
- * @author Brett Henderson
- */
-public class ApidbChangeReader implements RunnableChangeSource {
-
-    private ChangeSink changeSink;
-    private DatabaseLoginCredentials loginCredentials;
-    private DatabasePreferences preferences;
-    private Date intervalBegin;
-    private Date intervalEnd;
-    private boolean fullHistory;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 * @param intervalBegin
-	 *            Marks the beginning (inclusive) of the time interval to be checked.
-	 * @param intervalEnd
-	 *            Marks the end (exclusive) of the time interval to be checked.
-	 * @param fullHistory
-	 *            Specifies if full version history should be returned, or just a single change per
-	 *            entity for the interval.
-	 */
-    public ApidbChangeReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-            Date intervalBegin, Date intervalEnd, boolean fullHistory) {
-        this.loginCredentials = loginCredentials;
-        this.preferences = preferences;
-        this.intervalBegin = intervalBegin;
-        this.intervalEnd = intervalEnd;
-        this.fullHistory = fullHistory;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setChangeSink(ChangeSink changeSink) {
-        this.changeSink = changeSink;
-    }
-    
-    
-    /**
-	 * Runs the task implementation. This is called by the run method within a transaction.
-	 * 
-	 * @param dbCtx
-	 *            Used to access the database.
-	 */
-    protected void runImpl(DatabaseContext2 dbCtx) {
-    	try {
-    		AllEntityDao entityDao;
-    		ReleasableIterator<ChangeContainer> reader;
-    		
-	        new SchemaVersionValidator(loginCredentials, preferences)
-	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-	        
-	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
-	        
-	        reader = entityDao.getHistory(intervalBegin, intervalEnd);
-	        if (!fullHistory) {
-	        	reader = new DeltaToDiffReader(reader);
-	        }
-	        try {
-	        	while (reader.hasNext()) {
-	        		changeSink.process(reader.next());
-	        	}
-	        	
-	        } finally {
-	        	reader.release();
-	        }
-	
-	        changeSink.complete();
-	        
-    	} finally {
-    		changeSink.release();
-    	}
-    }
-    
-
-    /**
-     * Reads all data from the database and send it to the sink.
-     */
-    public void run() {
-        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
-    	
-        try {
-        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
-        		private DatabaseContext2 dbCtxInner = dbCtx;
-
-				@Override
-				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
-					runImpl(dbCtxInner);
-				} });
-
-        } finally {
-            dbCtx.release();
-        }
-    }
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java
deleted file mode 100644
index 4934cab..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.ActionChangeWriter;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.ChangeWriter;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-
-/**
- * A change sink writing to database tables. This aims to be suitable for running at regular
- * intervals with database overhead proportional to changeset size.
- * 
- * @author Brett Henderson
- */
-public class ApidbChangeWriter implements ChangeSink {
-
-    private final ChangeWriter changeWriter;
-
-    private final Map<ChangeAction, ActionChangeWriter> actionWriterMap;
-
-    private final SchemaVersionValidator schemaVersionValidator;
-
-    /**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     * @param preferences Contains preferences configuring database behaviour.
-     * @param populateCurrentTables If true, the current tables will be populated as well as history
-     *        tables.
-     */
-    public ApidbChangeWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-            boolean populateCurrentTables) {
-        changeWriter = new ChangeWriter(loginCredentials, populateCurrentTables);
-        actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
-        actionWriterMap.put(ChangeAction.Create, new ActionChangeWriter(changeWriter, ChangeAction.Create));
-        actionWriterMap.put(ChangeAction.Modify, new ActionChangeWriter(changeWriter, ChangeAction.Modify));
-        actionWriterMap.put(ChangeAction.Delete, new ActionChangeWriter(changeWriter, ChangeAction.Delete));
-
-        schemaVersionValidator = new SchemaVersionValidator(loginCredentials, preferences);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(ChangeContainer change) {
-        ChangeAction action;
-
-        // Verify that the schema version is supported.
-        schemaVersionValidator.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-
-        action = change.getAction();
-
-        if (!actionWriterMap.containsKey(action)) {
-            throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
-        }
-
-        // Process the entity using the action writer appropriate for the change
-        // action.
-        change.getEntityContainer().process(actionWriterMap.get(action));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void complete() {
-        changeWriter.complete();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void release() {
-        changeWriter.release();
-    }
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java
deleted file mode 100644
index 00627f6..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-
-
-/**
- * An OSM data source reading from a databases current tables. The entire contents of the database
- * are read.
- * 
- * @author Brett Henderson
- */
-public class ApidbCurrentReader implements RunnableSource {
-
-    private Sink sink;
-    private DatabaseLoginCredentials loginCredentials;
-    private DatabasePreferences preferences;
-
-
-    /**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 */
-    public ApidbCurrentReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
-        this.loginCredentials = loginCredentials;
-        this.preferences = preferences;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setSink(Sink sink) {
-        this.sink = sink;
-    }
-    
-    
-    /**
-	 * Runs the task implementation. This is called by the run method within a transaction.
-	 * 
-	 * @param dbCtx
-	 *            Used to access the database.
-	 */
-    protected void runImpl(DatabaseContext2 dbCtx) {
-    	try {
-    		AllEntityDao entityDao;
-    		ReleasableIterator<EntityContainer> reader;
-    		
-	        new SchemaVersionValidator(loginCredentials, preferences)
-	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-	        
-	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
-	        
-	        sink.process(new BoundContainer(new Bound("Osmosis " + OsmosisConstants.VERSION)));
-	        reader = entityDao.getCurrent();
-	        try {
-	        	while (reader.hasNext()) {
-	        		sink.process(reader.next());
-	        	}
-	        	
-	        } finally {
-	        	reader.release();
-	        }
-	
-	        sink.complete();
-	        
-    	} finally {
-    		sink.release();
-    	}
-    }
-    
-
-    /**
-     * Reads all data from the database and send it to the sink.
-     */
-    public void run() {
-        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
-    	
-        try {
-        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
-        		private DatabaseContext2 dbCtxInner = dbCtx;
-
-				@Override
-				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
-					runImpl(dbCtxInner);
-				} });
-
-        } finally {
-            dbCtx.release();
-        }
-    }
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java
deleted file mode 100644
index c4c577b..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.FileReplicationDestination;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.ReplicationDestination;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.ReplicationSource;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.Replicator;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SystemTimeLoader;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.TimeDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.TransactionDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.TransactionSnapshotLoader;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.task.common.RunnableTask;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-
-
-/**
- * Performs replication from an API database into change files.
- */
-public class ApidbFileReplicator implements RunnableTask {
-	
-	private DatabaseLoginCredentials loginCredentials;
-	private DatabasePreferences preferences;
-	private File workingDirectory;
-	
-	
-	/**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     * @param preferences Contains preferences configuring database behaviour.
-     * @param workingDirectory The directory to store all output files.
-     */
-    public ApidbFileReplicator(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-            File workingDirectory) {
-    	this.loginCredentials = loginCredentials;
-    	this.preferences = preferences;
-    	this.workingDirectory = workingDirectory;
-    }
-    
-    
-    /**
-	 * Runs the task implementation. This is called by the run method within a transaction.
-	 * 
-	 * @param dbCtx
-	 *            Used to access the database.
-	 */
-    protected void runImpl(DatabaseContext2 dbCtx) {
-		Replicator replicator;
-		ReplicationSource source;
-		ReplicationDestination destination;
-		TransactionSnapshotLoader txnSnapshotLoader;
-		SystemTimeLoader systemTimeLoader;
-		
-		new SchemaVersionValidator(loginCredentials, preferences)
-				.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-		
-		source = new AllEntityDao(dbCtx.getJdbcTemplate());
-		destination = new FileReplicationDestination(workingDirectory);
-		txnSnapshotLoader = new TransactionDao(dbCtx.getJdbcTemplate());
-		systemTimeLoader = new TimeDao(dbCtx.getJdbcTemplate());
-		
-		replicator = new Replicator(source, destination, txnSnapshotLoader, systemTimeLoader);
-		
-		replicator.replicate();
-    }
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
-    	
-        try {
-        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
-        		private DatabaseContext2 dbCtxInner = dbCtx;
-
-				@Override
-				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
-					runImpl(dbCtxInner);
-				} });
-
-        } finally {
-            dbCtx.release();
-        }
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java
deleted file mode 100644
index 753ee9b..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.common.RunnableTaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-
-
-/**
- * The task factory for a file-based database replicator.
- */
-public class ApidbFileReplicatorFactory extends DatabaseTaskManagerFactory {
-	private static final String ARG_WORKING_DIRECTORY = "directory";
-	private static final String DEFAULT_WORKING_DIRECTORY = "replicate";
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		DatabaseLoginCredentials loginCredentials;
-		DatabasePreferences preferences;
-		String workingDirectoryName;
-		File workingDirectory;
-		
-		// Get the task arguments.
-		loginCredentials = getDatabaseLoginCredentials(taskConfig);
-		preferences = getDatabasePreferences(taskConfig);
-		workingDirectoryName = getStringArgument(
-				taskConfig, ARG_WORKING_DIRECTORY, DEFAULT_WORKING_DIRECTORY);
-		
-		// Create a file object representing the directory from the name provided.
-		workingDirectory = new File(workingDirectoryName);
-		
-		return new RunnableTaskManager(
-			taskConfig.getId(),
-			new ApidbFileReplicator(loginCredentials, preferences, workingDirectory),
-			taskConfig.getPipeArgs()
-		);
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java
deleted file mode 100644
index 06dd3d0..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.util.Date;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.EntitySnapshotReader;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
-
-/**
- * An OSM data source reading from a database. The entire contents of the database are read.
- * 
- * @author Brett Henderson
- */
-public class ApidbReader implements RunnableSource {
-
-    private Sink sink;
-
-    private DatabaseLoginCredentials loginCredentials;
-    private DatabasePreferences preferences;
-    private Date snapshotInstant;
-
-
-    /**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     * @param preferences Contains preferences configuring database behaviour.
-     * @param snapshotInstant The state of the node table at this point in time will be dumped. This
-     *        ensures a consistent snapshot.
-     */
-    public ApidbReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-            Date snapshotInstant) {
-        this.loginCredentials = loginCredentials;
-        this.preferences = preferences;
-        this.snapshotInstant = snapshotInstant;
-        
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setSink(Sink sink) {
-        this.sink = sink;
-    }
-    
-    
-    /**
-	 * Runs the task implementation. This is called by the run method within a transaction.
-	 * 
-	 * @param dbCtx
-	 *            Used to access the database.
-	 */
-    protected void runImpl(DatabaseContext2 dbCtx) {
-    	try {
-    		AllEntityDao entityDao;
-    		ReleasableIterator<EntityContainer> reader;
-    		
-	        new SchemaVersionValidator(loginCredentials, preferences)
-	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-	        
-	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
-
-	        sink.process(new BoundContainer(new Bound("Osmosis " + OsmosisConstants.VERSION)));
-	        reader = new EntitySnapshotReader(entityDao.getHistory(), snapshotInstant);
-	        try {
-	        	while (reader.hasNext()) {
-	        		sink.process(reader.next());
-	        	}
-	        	
-	        } finally {
-	        	reader.release();
-	        }
-	
-	        sink.complete();
-	        
-    	} finally {
-    		sink.release();
-    	}
-    }
-    
-
-    /**
-     * Reads all data from the database and send it to the sink.
-     */
-    public void run() {
-        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
-    	
-        try {
-        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
-        		private DatabaseContext2 dbCtxInner = dbCtx;
-
-				@Override
-				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
-					runImpl(dbCtxInner);
-				} });
-
-        } finally {
-            dbCtx.release();
-        }
-    }
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java
deleted file mode 100644
index d6c9741..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-
-/**
- * Defines constants specific to the specific schema version.
- * 
- * @author Brett Henderson
- */
-public final class ApidbVersionConstants {
-	
-	/**
-	 * This class cannot be instantiated.
-	 */
-	private ApidbVersionConstants() {
-	}
-	
-	/**
-	 * Defines the schema migrations expected to be in the database.
-	 */
-	public static final String[] SCHEMA_MIGRATIONS = {
-		"1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
-		"11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
-		"21", "22", "23", "24", "25", "26", "27", "28", "29", "30",
-		"31", "32", "33", "34", "35", "36", "37", "38", "39", "40",
-		"41", "42", "43", "44", "45", "46", "47", "48", "49", "50",
-		"51", "52", "20100513171259", "20100516124737",
-		"20100910084426", "20101114011429", "20110322001319", 
-		"20110925112722", "20111116184519"
-	};
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java
deleted file mode 100644
index a3bbc77..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java
+++ /dev/null
@@ -1,1235 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.openstreetmap.osmosis.apidb.common.DatabaseContext;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.ChangesetManager;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.MemberTypeRenderer;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.UserManager;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.database.DbFeature;
-import org.openstreetmap.osmosis.core.database.DbFeatureHistory;
-import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.util.FixedPrecisionCoordinateConvertor;
-import org.openstreetmap.osmosis.core.util.TileCalculator;
-
-
-/**
- * An OSM data sink for storing all data to a database. This task is intended for writing to an
- * empty database.
- * 
- * @author Brett Henderson
- */
-public class ApidbWriter implements Sink, EntityProcessor {
-
-    // These SQL strings are the prefix to statements that will be built based
-    // on how many rows of data are to be inserted at a time.
-	private static final String INSERT_SQL_NODE_COLUMNS =
-		"INSERT INTO nodes(node_id, timestamp, version, visible, changeset_id, latitude, longitude, tile)";
-	private static final String INSERT_SQL_NODE_PARAMS = "?, ?, ?, ?, ?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_NODE = 8;
-
-    private static final String INSERT_SQL_NODE_TAG_COLUMNS = "INSERT INTO node_tags (node_id, k, v, version)";
-    private static final String INSERT_SQL_NODE_TAG_PARAMS = "?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_NODE_TAG = 4;
-
-    private static final String INSERT_SQL_WAY_COLUMNS =
-    	"INSERT INTO ways (way_id, timestamp, version, visible, changeset_id)";
-    private static final String INSERT_SQL_WAY_PARAMS = "?, ?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_WAY = 5;
-
-    private static final String INSERT_SQL_WAY_TAG_COLUMNS = "INSERT INTO way_tags (way_id, k, v, version)";
-    private static final String INSERT_SQL_WAY_TAG_PARAMS = "?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_WAY_TAG = 4;
-
-    private static final String INSERT_SQL_WAY_NODE_COLUMNS =
-    	"INSERT INTO way_nodes (way_id, node_id, sequence_id, version)";
-    private static final String INSERT_SQL_WAY_NODE_PARAMS = "?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_WAY_NODE = 4;
-
-    private static final String INSERT_SQL_RELATION_COLUMNS =
-    	"INSERT INTO relations (relation_id, timestamp, version, visible, changeset_id)";
-    private static final String INSERT_SQL_RELATION_PARAMS =
-    	"?, ?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_RELATION = 5;
-
-    private static final String INSERT_SQL_RELATION_TAG_COLUMNS =
-        "INSERT INTO relation_tags (relation_id, k, v, version)";
-    private static final String INSERT_SQL_RELATION_TAG_PARAMS = "?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_RELATION_TAG = 4;
-
-    private static final String INSERT_SQL_RELATION_MEMBER_COLUMNS =
-    	"INSERT INTO relation_members (relation_id, member_type, member_id, sequence_id, member_role, version)";
-    private static final String INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL =
-    	"?, ?, ?, ?, ?, ?";
-    private static final String INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL =
-    	"?, ?::nwr_enum, ?, ?, ?, ?";
-	private static final int INSERT_PRM_COUNT_RELATION_MEMBER = 6;
-
-    // These tables will have indexes disabled during loading data.
-    private static final List<String> DISABLE_KEY_TABLES = Arrays.asList(new String[] {"nodes",
-            "node_tags", "ways", "way_tags",
-            "way_nodes", "relations",
-            "relation_tags", "relation_members"});
-
-    // These SQL statements will be invoked after loading history tables to
-    // populate the current tables.
-    private static final int LOAD_CURRENT_NODE_ROW_COUNT = 1000000;
-
-    private static final int LOAD_CURRENT_WAY_ROW_COUNT = 100000;
-
-    private static final int LOAD_CURRENT_RELATION_ROW_COUNT = 100000;
-
-    private static final String LOAD_CURRENT_NODES =
-    	"INSERT INTO current_nodes SELECT node_id, latitude, longitude, changeset_id, visible, timestamp, tile, version"
-            + " FROM nodes WHERE node_id >= ? AND node_id < ?";
-
-    private static final String LOAD_CURRENT_NODE_TAGS =
-    	"INSERT INTO current_node_tags SELECT node_id, k, v FROM node_tags WHERE node_id >= ? AND node_id < ?";
-
-    private static final String LOAD_CURRENT_WAYS =
-    	"INSERT INTO current_ways SELECT way_id, changeset_id, timestamp, visible, version FROM ways"
-            + " WHERE way_id >= ? AND way_id < ?";
-
-    private static final String LOAD_CURRENT_WAY_TAGS =
-    	"INSERT INTO current_way_tags SELECT way_id, k, v FROM way_tags"
-            + " WHERE way_id >= ? AND way_id < ?";
-
-    private static final String LOAD_CURRENT_WAY_NODES =
-    	"INSERT INTO current_way_nodes SELECT way_id, node_id, sequence_id FROM way_nodes"
-            + " WHERE way_id >= ? AND way_id < ?";
-
-    private static final String LOAD_CURRENT_RELATIONS =
-    	"INSERT INTO current_relations SELECT relation_id, changeset_id, timestamp, visible, version"
-            + " FROM relations WHERE relation_id >= ? AND relation_id < ?";
-
-    private static final String LOAD_CURRENT_RELATION_TAGS =
-    	"INSERT INTO current_relation_tags SELECT relation_id, k, v FROM relation_tags"
-            + " WHERE relation_id >= ? AND relation_id < ?";
-
-    private static final String LOAD_CURRENT_RELATION_MEMBERS =
-    	"INSERT INTO current_relation_members (relation_id, member_id, member_role, member_type, sequence_id)"
-    		+ " SELECT relation_id, member_id, member_role, member_type, sequence_id"
-            + " FROM relation_members WHERE relation_id >= ? AND relation_id < ?";
-
-    // These tables will be locked for exclusive access while loading data.
-	private static final List<String> LOCK_TABLES = Arrays.asList(new String[] {"nodes", "node_tags", "ways",
-			"way_tags", "way_nodes", "relations", "relation_tags", "relation_members", "current_nodes",
-			"current_node_tags", "current_ways", "current_way_tags", "current_way_nodes", "current_relations",
-			"current_relation_tags", "current_relation_members", "users", "changesets", "changeset_tags" });
-
-    // These constants define how many rows of each data type will be inserted
-    // with single insert statements.
-    private static final int INSERT_BULK_ROW_COUNT_NODE = 100;
-    private static final int INSERT_BULK_ROW_COUNT_NODE_TAG = 100;
-    private static final int INSERT_BULK_ROW_COUNT_WAY = 100;
-    private static final int INSERT_BULK_ROW_COUNT_WAY_TAG = 100;
-    private static final int INSERT_BULK_ROW_COUNT_WAY_NODE = 100;
-    private static final int INSERT_BULK_ROW_COUNT_RELATION = 100;
-    private static final int INSERT_BULK_ROW_COUNT_RELATION_TAG = 100;
-    private static final int INSERT_BULK_ROW_COUNT_RELATION_MEMBER = 100;
-
-    /**
-	 * Builds a multi-row SQL insert statement.
-	 * 
-	 * @param columnSql
-	 *            The basic query without value bind variables.
-	 * @param parametersSql
-	 *            The SQL parameters portion of the query.
-	 * @param rowCount
-	 *            The number of rows to insert in a single query.
-	 * @return The generated SQL statement.
-	 */
-    private static String buildSqlInsertStatement(String columnSql, String parametersSql, int rowCount) {
-        StringBuilder buffer;
-
-        buffer = new StringBuilder();
-
-        buffer.append(columnSql).append(" VALUES ");
-
-        for (int i = 0; i < rowCount; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            
-            buffer.append("(");
-            buffer.append(parametersSql);
-            buffer.append(")");
-        }
-
-        return buffer.toString();
-    }
-
-    
-    private String insertSqlSingleNode;
-    private String insertSqlBulkNode;
-    private String insertSqlSingleNodeTag;
-    private String insertSqlBulkNodeTag;
-    private String insertSqlSingleWay;
-    private String insertSqlBulkWay;
-    private String insertSqlSingleWayTag;
-    private String insertSqlBulkWayTag;
-    private String insertSqlSingleWayNode;
-    private String insertSqlBulkWayNode;
-    private String insertSqlSingleRelation;
-    private String insertSqlBulkRelation;
-    private String insertSqlSingleRelationTag;
-    private String insertSqlBulkRelationTag;
-    private String insertSqlSingleRelationMember;
-    private String insertSqlBulkRelationMember;
-    private final DatabaseContext dbCtx;
-    private final UserManager userManager;
-    private final ChangesetManager changesetManager;
-    private final SchemaVersionValidator schemaVersionValidator;
-    private final boolean lockTables;
-    private final boolean populateCurrentTables;
-    private final List<Node> nodeBuffer;
-    private final List<DbFeatureHistory<DbFeature<Tag>>> nodeTagBuffer;
-    private final List<Way> wayBuffer;
-    private final List<DbFeatureHistory<DbFeature<Tag>>> wayTagBuffer;
-    private final List<DbFeatureHistory<DbOrderedFeature<WayNode>>> wayNodeBuffer;
-    private final List<Relation> relationBuffer;
-    private final List<DbFeatureHistory<DbFeature<Tag>>> relationTagBuffer;
-    private final List<DbFeatureHistory<DbOrderedFeature<RelationMember>>> relationMemberBuffer;
-    private long maxNodeId;
-    private long maxWayId;
-    private long maxRelationId;
-    private final TileCalculator tileCalculator;
-    private final MemberTypeRenderer memberTypeRenderer;
-    private boolean initialized;
-    private PreparedStatement singleNodeStatement;
-    private PreparedStatement bulkNodeStatement;
-    private PreparedStatement singleNodeTagStatement;
-    private PreparedStatement bulkNodeTagStatement;
-    private PreparedStatement singleWayStatement;
-    private PreparedStatement bulkWayStatement;
-    private PreparedStatement singleWayTagStatement;
-    private PreparedStatement bulkWayTagStatement;
-    private PreparedStatement singleWayNodeStatement;
-    private PreparedStatement bulkWayNodeStatement;
-    private PreparedStatement singleRelationStatement;
-    private PreparedStatement bulkRelationStatement;
-    private PreparedStatement singleRelationTagStatement;
-    private PreparedStatement bulkRelationTagStatement;
-    private PreparedStatement singleRelationMemberStatement;
-    private PreparedStatement bulkRelationMemberStatement;
-    private PreparedStatement loadCurrentNodesStatement;
-    private PreparedStatement loadCurrentNodeTagsStatement;
-    private PreparedStatement loadCurrentWaysStatement;
-    private PreparedStatement loadCurrentWayTagsStatement;
-	private PreparedStatement loadCurrentWayNodesStatement;
-	private PreparedStatement loadCurrentRelationsStatement;
-	private PreparedStatement loadCurrentRelationTagsStatement;
-	private PreparedStatement loadCurrentRelationMembersStatement;
-
-    /**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     * @param preferences Contains preferences configuring database behaviour.
-     * @param lockTables If true, all tables will be locked during loading.
-     * @param populateCurrentTables If true, the current tables will be populated as well as history
-     *        tables.
-     */
-    public ApidbWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences, boolean lockTables,
-            boolean populateCurrentTables) {
-        dbCtx = new DatabaseContext(loginCredentials);
-        
-        userManager = new UserManager(dbCtx);
-        changesetManager = new ChangesetManager(dbCtx);
-
-        schemaVersionValidator = new SchemaVersionValidator(loginCredentials, preferences);
-
-        this.lockTables = lockTables;
-        this.populateCurrentTables = populateCurrentTables;
-
-        nodeBuffer = new ArrayList<Node>();
-        nodeTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
-        wayBuffer = new ArrayList<Way>();
-        wayTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
-        wayNodeBuffer = new ArrayList<DbFeatureHistory<DbOrderedFeature<WayNode>>>();
-        relationBuffer = new ArrayList<Relation>();
-        relationTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
-        relationMemberBuffer = new ArrayList<DbFeatureHistory<DbOrderedFeature<RelationMember>>>();
-
-        maxNodeId = 0;
-        maxWayId = 0;
-        maxRelationId = 0;
-
-        tileCalculator = new TileCalculator();
-        memberTypeRenderer = new MemberTypeRenderer();
-
-        initialized = false;
-    }
-    
-    
-    private void buildSqlStatements() {
-    	insertSqlSingleNode = buildSqlInsertStatement(INSERT_SQL_NODE_COLUMNS, INSERT_SQL_NODE_PARAMS, 1);
-		insertSqlBulkNode = buildSqlInsertStatement(INSERT_SQL_NODE_COLUMNS, INSERT_SQL_NODE_PARAMS,
-				INSERT_BULK_ROW_COUNT_NODE);
-		insertSqlSingleNodeTag = buildSqlInsertStatement(
-				INSERT_SQL_NODE_TAG_COLUMNS, INSERT_SQL_NODE_TAG_PARAMS, 1);
-		insertSqlBulkNodeTag = buildSqlInsertStatement(INSERT_SQL_NODE_TAG_COLUMNS, INSERT_SQL_NODE_TAG_PARAMS,
-				INSERT_BULK_ROW_COUNT_NODE_TAG);
-		insertSqlSingleWay = buildSqlInsertStatement(INSERT_SQL_WAY_COLUMNS, INSERT_SQL_WAY_PARAMS, 1);
-		insertSqlBulkWay = buildSqlInsertStatement(INSERT_SQL_WAY_COLUMNS, INSERT_SQL_WAY_PARAMS,
-				INSERT_BULK_ROW_COUNT_WAY);
-		insertSqlSingleWayTag = buildSqlInsertStatement(INSERT_SQL_WAY_TAG_COLUMNS, INSERT_SQL_WAY_TAG_PARAMS, 1);
-		insertSqlBulkWayTag = buildSqlInsertStatement(INSERT_SQL_WAY_TAG_COLUMNS, INSERT_SQL_WAY_TAG_PARAMS,
-				INSERT_BULK_ROW_COUNT_WAY_TAG);
-		insertSqlSingleWayNode = buildSqlInsertStatement(
-				INSERT_SQL_WAY_NODE_COLUMNS, INSERT_SQL_WAY_NODE_PARAMS, 1);
-		insertSqlBulkWayNode = buildSqlInsertStatement(INSERT_SQL_WAY_NODE_COLUMNS, INSERT_SQL_WAY_NODE_PARAMS,
-				INSERT_BULK_ROW_COUNT_WAY_NODE);
-		insertSqlSingleRelation = buildSqlInsertStatement(INSERT_SQL_RELATION_COLUMNS, INSERT_SQL_RELATION_PARAMS,
-				1);
-		insertSqlBulkRelation = buildSqlInsertStatement(INSERT_SQL_RELATION_COLUMNS, INSERT_SQL_RELATION_PARAMS,
-				INSERT_BULK_ROW_COUNT_RELATION);
-    	insertSqlSingleRelationTag = buildSqlInsertStatement(INSERT_SQL_RELATION_TAG_COLUMNS,
-				INSERT_SQL_RELATION_TAG_PARAMS, 1);
-		insertSqlBulkRelationTag = buildSqlInsertStatement(INSERT_SQL_RELATION_TAG_COLUMNS,
-				INSERT_SQL_RELATION_TAG_PARAMS, INSERT_BULK_ROW_COUNT_RELATION_TAG);
-    }
-    
-
-    /**
-     * Initialises prepared statements and obtains database locks. Can be called multiple times.
-     */
-    private void initialize() {
-        if (!initialized) {
-            schemaVersionValidator.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
-            
-            buildSqlStatements();
-            
-            switch (dbCtx.getDatabaseType()) {
-            case POSTGRESQL:
-    			insertSqlSingleRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
-    					INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL, 1);
-    			insertSqlBulkRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
-    					INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL, INSERT_BULK_ROW_COUNT_RELATION_MEMBER);
-                break;
-            case MYSQL:
-    			insertSqlSingleRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
-    					INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL, 1);
-    			insertSqlBulkRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
-    					INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL, INSERT_BULK_ROW_COUNT_RELATION_MEMBER);
-                break;
-            default:
-                throw new OsmosisRuntimeException("Unknown database type " + dbCtx.getDatabaseType() + ".");
-            }
-
-            bulkNodeStatement = dbCtx.prepareStatement(insertSqlBulkNode);
-            singleNodeStatement = dbCtx.prepareStatement(insertSqlSingleNode);
-            bulkNodeTagStatement = dbCtx.prepareStatement(insertSqlBulkNodeTag);
-            singleNodeTagStatement = dbCtx.prepareStatement(insertSqlSingleNodeTag);
-            bulkWayStatement = dbCtx.prepareStatement(insertSqlBulkWay);
-            singleWayStatement = dbCtx.prepareStatement(insertSqlSingleWay);
-            bulkWayTagStatement = dbCtx.prepareStatement(insertSqlBulkWayTag);
-            singleWayTagStatement = dbCtx.prepareStatement(insertSqlSingleWayTag);
-            bulkWayNodeStatement = dbCtx.prepareStatement(insertSqlBulkWayNode);
-            singleWayNodeStatement = dbCtx.prepareStatement(insertSqlSingleWayNode);
-            bulkRelationStatement = dbCtx.prepareStatement(insertSqlBulkRelation);
-            singleRelationStatement = dbCtx.prepareStatement(insertSqlSingleRelation);
-            bulkRelationTagStatement = dbCtx.prepareStatement(insertSqlBulkRelationTag);
-            singleRelationTagStatement = dbCtx.prepareStatement(insertSqlSingleRelationTag);
-            bulkRelationMemberStatement = dbCtx.prepareStatement(insertSqlBulkRelationMember);
-            singleRelationMemberStatement = dbCtx.prepareStatement(insertSqlSingleRelationMember);
-
-            loadCurrentNodesStatement = dbCtx.prepareStatement(LOAD_CURRENT_NODES);
-            loadCurrentNodeTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_NODE_TAGS);
-            loadCurrentWaysStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAYS);
-            loadCurrentWayTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAY_TAGS);
-            loadCurrentWayNodesStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAY_NODES);
-            loadCurrentRelationsStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATIONS);
-            loadCurrentRelationTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATION_TAGS);
-            loadCurrentRelationMembersStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATION_MEMBERS);
-
-            // Disable indexes to improve load performance.
-            dbCtx.disableIndexes(DISABLE_KEY_TABLES);
-
-            // Lock tables if required to improve load performance.
-            if (lockTables) {
-            	dbCtx.lockTables(LOCK_TABLES);
-            }
-
-            initialized = true;
-        }
-    }
-
-    /**
-     * Sets node values as bind variable parameters to a node insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param node The node containing the data to be inserted.
-     */
-    private void populateNodeParameters(PreparedStatement statement, int initialIndex, Node node) {
-        int prmIndex;
-
-        prmIndex = initialIndex;
-
-        // We can't write an entity with a null timestamp.
-        if (node.getTimestamp() == null) {
-            throw new OsmosisRuntimeException("Node " + node.getId() + " does not have a timestamp set.");
-        }
-
-        try {
-            statement.setLong(prmIndex++, node.getId());
-            statement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime()));
-            statement.setInt(prmIndex++, node.getVersion());
-            statement.setBoolean(prmIndex++, true);
-            statement.setLong(prmIndex++, node.getChangesetId());
-            statement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node.getLatitude()));
-            statement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node.getLongitude()));
-            statement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node.getLongitude()));
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a node.", e);
-        }
-    }
-
-    /**
-     * Sets way values as bind variable parameters to a way insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param way The way containing the data to be inserted.
-     */
-    private void populateWayParameters(PreparedStatement statement, int initialIndex, Way way) {
-        int prmIndex;
-
-        prmIndex = initialIndex;
-
-        // We can't write an entity with a null timestamp.
-        if (way.getTimestamp() == null) {
-            throw new OsmosisRuntimeException("Way " + way.getId() + " does not have a timestamp set.");
-        }
-
-        try {
-            statement.setLong(prmIndex++, way.getId());
-            statement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime()));
-            statement.setInt(prmIndex++, way.getVersion());
-            statement.setBoolean(prmIndex++, true);
-            statement.setLong(prmIndex++, way.getChangesetId());
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a way.", e);
-        }
-    }
-
-    /**
-     * Sets tag values as bind variable parameters to a tag insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param dbEntityTag The entity tag containing the data to be inserted.
-     */
-    private void populateEntityTagParameters(PreparedStatement statement, int initialIndex,
-        DbFeatureHistory<DbFeature<Tag>> dbEntityTag) {
-        int prmIndex;
-        Tag tag;
-
-        prmIndex = initialIndex;
-
-        tag = dbEntityTag.getFeature().getFeature();
-
-        try {
-            statement.setLong(prmIndex++, dbEntityTag.getFeature().getEntityId());
-            statement.setString(prmIndex++, tag.getKey());
-            statement.setString(prmIndex++, tag.getValue());
-            statement.setInt(prmIndex++, dbEntityTag.getVersion());
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for an entity tag.", e);
-        }
-    }
-
-    /**
-     * Sets node reference values as bind variable parameters to a way node insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param dbWayNode The way node containing the data to be inserted.
-     */
-    private void populateWayNodeParameters(PreparedStatement statement, int initialIndex,
-        DbFeatureHistory<DbOrderedFeature<WayNode>> dbWayNode) {
-        int prmIndex;
-
-        prmIndex = initialIndex;
-
-        try {
-            statement.setLong(prmIndex++, dbWayNode.getFeature().getEntityId());
-            statement.setLong(prmIndex++, dbWayNode.getFeature().getFeature().getNodeId());
-            statement.setInt(prmIndex++, dbWayNode.getFeature().getSequenceId());
-            statement.setInt(prmIndex++, dbWayNode.getVersion());
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a way node.", e);
-        }
-    }
-
-    /**
-     * Sets relation values as bind variable parameters to a relation insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param relation The way containing the data to be inserted.
-     */
-    private void populateRelationParameters(PreparedStatement statement, int initialIndex, Relation relation) {
-        int prmIndex;
-
-        prmIndex = initialIndex;
-
-        // We can't write an entity with a null timestamp.
-        if (relation.getTimestamp() == null) {
-            throw new OsmosisRuntimeException("Relation " + relation.getId() + " does not have a timestamp set.");
-        }
-
-        try {
-            statement.setLong(prmIndex++, relation.getId());
-            statement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp().getTime()));
-            statement.setInt(prmIndex++, relation.getVersion());
-            statement.setBoolean(prmIndex++, true);
-            statement.setLong(prmIndex++, relation.getChangesetId());
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a relation.", e);
-        }
-    }
-
-    /**
-     * Sets member reference values as bind variable parameters to a relation member insert query.
-     * 
-     * @param statement The prepared statement to add the values to.
-     * @param initialIndex The offset index of the first variable to set.
-     * @param dbRelationMember The relation member containing the data to be inserted.
-     */
-    private void populateRelationMemberParameters(PreparedStatement statement, int initialIndex,
-        DbFeatureHistory<DbOrderedFeature<RelationMember>> dbRelationMember) {
-        int prmIndex;
-        RelationMember relationMember;
-
-        prmIndex = initialIndex;
-
-        relationMember = dbRelationMember.getFeature().getFeature();
-
-        try {
-            statement.setLong(prmIndex++, dbRelationMember.getFeature().getEntityId());
-            statement.setString(prmIndex++, memberTypeRenderer.render(relationMember.getMemberType()));
-            statement.setLong(prmIndex++, relationMember.getMemberId());
-            statement.setInt(prmIndex++, dbRelationMember.getFeature().getSequenceId());
-            statement.setString(prmIndex++, relationMember.getMemberRole());
-            statement.setInt(prmIndex++, dbRelationMember.getVersion());
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a relation member.", e);
-        }
-    }
-
-    /**
-     * Flushes nodes to the database. If complete is false, this will only write nodes until the
-     * remaining node count is less than the multi-row insert statement row count. If complete is
-     * true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushNodes(boolean complete) {
-        while (nodeBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE) {
-            int prmIndex;
-            List<Node> processedNodes;
-
-            processedNodes = new ArrayList<Node>(INSERT_BULK_ROW_COUNT_NODE);
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE; i++) {
-                Node node;
-
-                node = nodeBuffer.remove(0);
-                processedNodes.add(node);
-
-                populateNodeParameters(bulkNodeStatement, prmIndex, node);
-                prmIndex += INSERT_PRM_COUNT_NODE;
-            }
-
-            try {
-                bulkNodeStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert nodes into the database.", e);
-            }
-
-            for (Node node : processedNodes) {
-                addNodeTags(node);
-            }
-        }
-
-        if (complete) {
-            while (nodeBuffer.size() > 0) {
-                Node node;
-
-                node = nodeBuffer.remove(0);
-
-                populateNodeParameters(singleNodeStatement, 1, node);
-
-                try {
-                    singleNodeStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a node into the database.", e);
-                }
-
-                addNodeTags(node);
-            }
-        }
-    }
-
-    /**
-     * Flushes node tags to the database. If complete is false, this will only write node tags until
-     * the remaining node tag count is less than the multi-row insert statement row count. If
-     * complete is true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushNodeTags(boolean complete) {
-        while (nodeTagBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE_TAG) {
-            int prmIndex;
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE_TAG; i++) {
-                populateEntityTagParameters(bulkNodeTagStatement, prmIndex, nodeTagBuffer.remove(0));
-                prmIndex += INSERT_PRM_COUNT_NODE_TAG;
-            }
-
-            try {
-                bulkNodeTagStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert node tags into the database.", e);
-            }
-        }
-
-        if (complete) {
-            while (nodeTagBuffer.size() > 0) {
-                populateEntityTagParameters(singleNodeTagStatement, 1, nodeTagBuffer.remove(0));
-
-                try {
-                    singleNodeTagStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a node tag into the database.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Flushes ways to the database. If complete is false, this will only write ways until the
-     * remaining way count is less than the multi-row insert statement row count. If complete is
-     * true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushWays(boolean complete) {
-        while (wayBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY) {
-            List<Way> processedWays;
-            int prmIndex;
-
-            processedWays = new ArrayList<Way>(INSERT_BULK_ROW_COUNT_WAY);
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY; i++) {
-                Way way;
-
-                way = wayBuffer.remove(0);
-                processedWays.add(way);
-
-                populateWayParameters(bulkWayStatement, prmIndex, way);
-                prmIndex += INSERT_PRM_COUNT_WAY;
-            }
-
-            try {
-                bulkWayStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert ways into the database.", e);
-            }
-
-            for (Way way : processedWays) {
-                addWayTags(way);
-                addWayNodes(way);
-            }
-        }
-
-        if (complete) {
-            while (wayBuffer.size() > 0) {
-                Way way;
-
-                way = wayBuffer.remove(0);
-
-                populateWayParameters(singleWayStatement, 1, way);
-
-                try {
-                    singleWayStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a way into the database.", e);
-                }
-
-                addWayTags(way);
-                addWayNodes(way);
-            }
-        }
-    }
-
-    /**
-     * Flushes way tags to the database. If complete is false, this will only write way tags until
-     * the remaining way tag count is less than the multi-row insert statement row count. If
-     * complete is true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushWayTags(boolean complete) {
-        while (wayTagBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_TAG) {
-            int prmIndex;
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_TAG; i++) {
-                populateEntityTagParameters(bulkWayTagStatement, prmIndex, wayTagBuffer.remove(0));
-                prmIndex += INSERT_PRM_COUNT_WAY_TAG;
-            }
-
-            try {
-                bulkWayTagStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert way tags into the database.", e);
-            }
-        }
-
-        if (complete) {
-            while (wayTagBuffer.size() > 0) {
-                populateEntityTagParameters(singleWayTagStatement, 1, wayTagBuffer.remove(0));
-
-                try {
-                    singleWayTagStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a way tag into the database.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Flushes way nodes to the database. If complete is false, this will only write way nodes until
-     * the remaining way node count is less than the multi-row insert statement row count. If
-     * complete is true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushWayNodes(boolean complete) {
-        while (wayNodeBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_NODE) {
-            int prmIndex;
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_NODE; i++) {
-                populateWayNodeParameters(bulkWayNodeStatement, prmIndex, wayNodeBuffer.remove(0));
-                prmIndex += INSERT_PRM_COUNT_WAY_NODE;
-            }
-
-            try {
-                bulkWayNodeStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert way nodes into the database.", e);
-            }
-        }
-
-        if (complete) {
-            while (wayNodeBuffer.size() > 0) {
-                populateWayNodeParameters(singleWayNodeStatement, 1, wayNodeBuffer.remove(0));
-
-                try {
-                    singleWayNodeStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a way node into the database.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Flushes relations to the database. If complete is false, this will only write relations until
-     * the remaining way count is less than the multi-row insert statement row count. If complete is
-     * true, all remaining rows will be written using single row insert statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushRelations(boolean complete) {
-        while (relationBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION) {
-            List<Relation> processedRelations;
-            int prmIndex;
-
-            processedRelations = new ArrayList<Relation>(INSERT_BULK_ROW_COUNT_RELATION);
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION; i++) {
-                Relation relation;
-
-                relation = relationBuffer.remove(0);
-                processedRelations.add(relation);
-
-                populateRelationParameters(bulkRelationStatement, prmIndex, relation);
-                prmIndex += INSERT_PRM_COUNT_RELATION;
-            }
-
-            try {
-                bulkRelationStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert relations into the database.", e);
-            }
-
-            for (Relation relation : processedRelations) {
-                addRelationTags(relation);
-                addRelationMembers(relation);
-            }
-        }
-
-        if (complete) {
-            while (relationBuffer.size() > 0) {
-                Relation relation;
-
-                relation = relationBuffer.remove(0);
-
-                populateRelationParameters(singleRelationStatement, 1, relation);
-
-                try {
-                    singleRelationStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a relation into the database.", e);
-                }
-
-                addRelationTags(relation);
-                addRelationMembers(relation);
-            }
-        }
-    }
-
-    /**
-     * Flushes relation tags to the database. If complete is false, this will only write relation
-     * tags until the remaining relation tag count is less than the multi-row insert statement row
-     * count. If complete is true, all remaining rows will be written using single row insert
-     * statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushRelationTags(boolean complete) {
-        while (relationTagBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_TAG) {
-            int prmIndex;
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_TAG; i++) {
-                populateEntityTagParameters(bulkRelationTagStatement, prmIndex, relationTagBuffer.remove(0));
-                prmIndex += INSERT_PRM_COUNT_RELATION_TAG;
-            }
-
-            try {
-                bulkRelationTagStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert relation tags into the database.", e);
-            }
-        }
-
-        if (complete) {
-            while (relationTagBuffer.size() > 0) {
-                populateEntityTagParameters(singleRelationTagStatement, 1, relationTagBuffer.remove(0));
-
-                try {
-                    singleRelationTagStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a relation tag into the database.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Flushes relation members to the database. If complete is false, this will only write relation
-     * members until the remaining relation member count is less than the multi-row insert statement
-     * row count. If complete is true, all remaining rows will be written using single row insert
-     * statements.
-     * 
-     * @param complete If true, all data will be written to the database. If false, some data may be
-     *        left until more data is available.
-     */
-    private void flushRelationMembers(boolean complete) {
-        while (relationMemberBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_MEMBER) {
-            int prmIndex;
-
-            prmIndex = 1;
-            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_MEMBER; i++) {
-                populateRelationMemberParameters(bulkRelationMemberStatement, prmIndex, relationMemberBuffer.remove(0));
-                prmIndex += INSERT_PRM_COUNT_RELATION_MEMBER;
-            }
-
-            try {
-                bulkRelationMemberStatement.executeUpdate();
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to bulk insert relation members into the database.", e);
-            }
-        }
-
-        if (complete) {
-            while (relationMemberBuffer.size() > 0) {
-                populateRelationMemberParameters(singleRelationMemberStatement, 1, relationMemberBuffer.remove(0));
-
-                try {
-                    singleRelationMemberStatement.executeUpdate();
-                } catch (SQLException e) {
-                    throw new OsmosisRuntimeException("Unable to insert a relation member into the database.", e);
-                }
-            }
-        }
-    }
-    
-    
-    private void populateCurrentNodes() {
-        // Copy data into the current node tables.
-        for (int i = 0; i < maxNodeId; i += LOAD_CURRENT_NODE_ROW_COUNT) {
-            // Node
-            try {
-                loadCurrentNodesStatement.setInt(1, i);
-                loadCurrentNodesStatement.setInt(2, i + LOAD_CURRENT_NODE_ROW_COUNT);
-
-                loadCurrentNodesStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current nodes.", e);
-            }
-
-            // Node tags
-            try {
-                loadCurrentNodeTagsStatement.setInt(1, i);
-                loadCurrentNodeTagsStatement.setInt(2, i + LOAD_CURRENT_NODE_ROW_COUNT);
-
-                loadCurrentNodeTagsStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current node tags.", e);
-            }
-
-            dbCtx.commit();
-        }
-    }
-    
-    
-    private void populateCurrentWays() {
-        for (int i = 0; i < maxWayId; i += LOAD_CURRENT_WAY_ROW_COUNT) {
-            // Way
-            try {
-                loadCurrentWaysStatement.setInt(1, i);
-                loadCurrentWaysStatement.setInt(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
-
-                loadCurrentWaysStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current ways.", e);
-            }
-
-            // Way tags
-            try {
-                loadCurrentWayTagsStatement.setInt(1, i);
-                loadCurrentWayTagsStatement.setInt(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
-
-                loadCurrentWayTagsStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current way tags.", e);
-            }
-
-            // Way nodes
-            try {
-                loadCurrentWayNodesStatement.setInt(1, i);
-                loadCurrentWayNodesStatement.setInt(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
-
-                loadCurrentWayNodesStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current way nodes.", e);
-            }
-
-            dbCtx.commit();
-        }
-    }
-    
-    
-    private void populateCurrentRelations() {
-        for (int i = 0; i < maxRelationId; i += LOAD_CURRENT_RELATION_ROW_COUNT) {
-            // Way
-            try {
-                loadCurrentRelationsStatement.setInt(1, i);
-                loadCurrentRelationsStatement.setInt(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
-
-                loadCurrentRelationsStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current relations.", e);
-            }
-
-            // Relation tags
-            try {
-                loadCurrentRelationTagsStatement.setInt(1, i);
-                loadCurrentRelationTagsStatement.setInt(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
-
-                loadCurrentRelationTagsStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current relation tags.", e);
-            }
-
-            // Relation members
-            try {
-                loadCurrentRelationMembersStatement.setInt(1, i);
-                loadCurrentRelationMembersStatement.setInt(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
-
-                loadCurrentRelationMembersStatement.execute();
-
-            } catch (SQLException e) {
-                throw new OsmosisRuntimeException("Unable to load current relation members.", e);
-            }
-
-            dbCtx.commit();
-        }
-    }
-    
-    
-    private void populateCurrentTables() {
-    	if (populateCurrentTables) {
-    		populateCurrentNodes();
-    		populateCurrentWays();
-    		populateCurrentRelations();
-        }
-    }
-    
-
-    /**
-     * Writes any buffered data to the database and commits.
-     */
-    @Override
-    public void complete() {
-        initialize();
-
-        flushNodes(true);
-        flushNodeTags(true);
-        flushWays(true);
-        flushWayTags(true);
-        flushWayNodes(true);
-        flushRelations(true);
-        flushRelationTags(true);
-        flushRelationMembers(true);
-
-        // Re-enable indexes now that the load has completed.
-        dbCtx.enableIndexes(DISABLE_KEY_TABLES);
-
-        populateCurrentTables();
-
-        // Unlock tables (if they were locked) now that we have completed.
-        if (lockTables) {
-        	dbCtx.unlockTables(LOCK_TABLES);
-        }
-
-        dbCtx.commit();
-    }
-
-    /**
-     * Releases all database resources.
-     */
-    public void release() {
-        userManager.release();
-
-        dbCtx.release();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(EntityContainer entityContainer) {
-    	Entity entity;
-    	
-        initialize();
-
-        entity = entityContainer.getEntity();
-        userManager.addOrUpdateUser(entityContainer.getEntity().getUser());
-        changesetManager.addChangesetIfRequired(entity.getChangesetId(), entity.getUser());
-
-        entityContainer.process(this);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(BoundContainer boundContainer) {
-        // Do nothing.
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(NodeContainer nodeContainer) {
-        Node node;
-        long nodeId;
-
-        node = nodeContainer.getEntity();
-        nodeId = node.getId();
-
-        if (nodeId >= maxNodeId) {
-            maxNodeId = nodeId + 1;
-        }
-
-        nodeBuffer.add(node);
-
-        flushNodes(false);
-    }
-
-    /**
-     * Process the node tags.
-     * 
-     * @param node The node to be processed.
-     */
-    private void addNodeTags(Node node) {
-        for (Tag tag : node.getTags()) {
-            nodeTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(node.getId(), tag), node
-                    .getVersion()));
-        }
-
-        flushNodeTags(false);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(WayContainer wayContainer) {
-        Way way;
-        long wayId;
-
-        flushNodes(true);
-
-        way = wayContainer.getEntity();
-        wayId = way.getId();
-
-        if (wayId >= maxWayId) {
-            maxWayId = wayId + 1;
-        }
-
-        wayBuffer.add(way);
-
-        flushWays(false);
-    }
-
-    /**
-     * Process the way tags.
-     * 
-     * @param way The way to be processed.
-     */
-    private void addWayTags(Way way) {
-        for (Tag tag : way.getTags()) {
-            wayTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(way.getId(), tag), way
-                    .getVersion()));
-        }
-
-        flushWayTags(false);
-    }
-
-    /**
-     * Process the way nodes.
-     * 
-     * @param way The way to be processed.
-     */
-    private void addWayNodes(Way way) {
-        List<WayNode> nodeReferenceList;
-
-        nodeReferenceList = way.getWayNodes();
-
-        for (int i = 0; i < nodeReferenceList.size(); i++) {
-            wayNodeBuffer.add(new DbFeatureHistory<DbOrderedFeature<WayNode>>(new DbOrderedFeature<WayNode>(
-                    way.getId(), nodeReferenceList.get(i), i + 1), way.getVersion()));
-        }
-
-        flushWayNodes(false);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(RelationContainer relationContainer) {
-        Relation relation;
-        long relationId;
-
-        flushWays(true);
-
-        relation = relationContainer.getEntity();
-        relationId = relation.getId();
-
-        if (relationId >= maxRelationId) {
-            maxRelationId = relationId + 1;
-        }
-
-        relationBuffer.add(relation);
-
-        flushRelations(false);
-    }
-
-    /**
-     * Process the relation tags.
-     * 
-     * @param relation The relation to be processed.
-     */
-    private void addRelationTags(Relation relation) {
-        for (Tag tag : relation.getTags()) {
-            relationTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(relation.getId(), tag),
-                    relation.getVersion()));
-        }
-
-        flushRelationTags(false);
-    }
-
-    /**
-     * Process the relation members.
-     * 
-     * @param relation The relation to be processed.
-     */
-    private void addRelationMembers(Relation relation) {
-        List<RelationMember> memberReferenceList;
-
-        memberReferenceList = relation.getMembers();
-
-        for (int i = 0; i < memberReferenceList.size(); i++) {
-            relationMemberBuffer.add(new DbFeatureHistory<DbOrderedFeature<RelationMember>>(
-                    new DbOrderedFeature<RelationMember>(relation.getId(), memberReferenceList.get(i), i + 1), relation
-                            .getVersion()));
-        }
-
-        flushRelationMembers(false);
-    }
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java
deleted file mode 100644
index 2d89e85..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java
+++ /dev/null
@@ -1,541 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainerFactory;
-import org.openstreetmap.osmosis.core.database.DbFeature;
-import org.openstreetmap.osmosis.core.database.DbFeatureHistory;
-import org.openstreetmap.osmosis.core.database.DbFeatureHistoryComparator;
-import org.openstreetmap.osmosis.core.database.DbFeatureHistoryRowMapper;
-import org.openstreetmap.osmosis.core.database.DbFeatureRowMapper;
-import org.openstreetmap.osmosis.core.database.RowMapperListener;
-import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
-import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
-
-
-/**
- * Provides functionality common to all top level entity daos.
- * 
- * @param <T>
- *            The entity type to be supported.
- */
-public abstract class EntityDao<T extends Entity> {
-	private static final Logger LOG = Logger.getLogger(EntityDao.class.getName());
-
-	private JdbcTemplate jdbcTemplate;
-	private NamedParameterJdbcTemplate namedParamJdbcTemplate;
-	private String entityName;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Used to access the database.
-	 * @param entityName
-	 *            The name of the entity. Used for building dynamic sql queries.
-	 */
-	protected EntityDao(JdbcTemplate jdbcTemplate, String entityName) {
-		this.jdbcTemplate = jdbcTemplate;
-		this.namedParamJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
-		this.entityName = entityName;
-	}
-	
-	
-	/**
-	 * Provides access to the named parameter jdbc template.
-	 * 
-	 * @return The jdbc template.
-	 */
-	protected NamedParameterJdbcTemplate getNamedParamJdbcTemplate() {
-		return namedParamJdbcTemplate;
-	}
-
-
-	/**
-	 * Produces an array of additional column names specific to this entity type to be returned by
-	 * entity queries.
-	 * 
-	 * @return The column names.
-	 */
-	protected abstract String[] getTypeSpecificFieldNames();
-	
-	
-	/**
-	 * Creates a row mapper that receives common entity data objects and produces full entity
-	 * objects.
-	 * 
-	 * @param entityListener
-	 *            The full entity object listener.
-	 * @return The full entity row mapper.
-	 */
-	protected abstract RowMapperListener<CommonEntityData> getEntityRowMapper(RowMapperListener<T> entityListener);
-	
-	
-	/**
-	 * Gets the entity container factory for the entity type.
-	 * 
-	 * @return The factory.
-	 */
-	protected abstract EntityContainerFactory<T> getContainerFactory();
-
-
-	/**
-	 * Gets the history feature populators for the entity type.
-	 * 
-	 * @param selectedEntityStatement
-	 *            The statement for obtaining the id and version pairs of entity records selected.
-	 * @param parameterSource
-	 *            The parameters required to execute the selected entity statement.
-	 * @return The history feature populators.
-	 */
-	protected abstract List<FeatureHistoryPopulator<T, ?, ?>> getFeatureHistoryPopulators(
-			String selectedEntityStatement, MapSqlParameterSource parameterSource);
-	
-	
-	private String buildFeaturelessEntityHistoryQuery(String selectedEntityStatement) {
-		StringBuilder sql;
-
-		sql = new StringBuilder();
-		sql.append("SELECT e.");
-		sql.append(entityName);
-		sql.append("_id AS id, e.version, e.timestamp, e.visible, u.data_public,");
-		sql.append(" u.id AS user_id, u.display_name, e.changeset_id");
-
-		for (String fieldName : getTypeSpecificFieldNames()) {
-			sql.append(", e.");
-			sql.append(fieldName);
-		}
-
-		sql.append(" FROM ");
-		sql.append(entityName);
-		sql.append("s e");
-		sql.append(" INNER JOIN ");
-		sql.append(selectedEntityStatement);
-		sql.append(" t ON e.");
-		sql.append(entityName);
-		sql.append("_id = t.");
-		sql.append(entityName);
-		sql.append("_id AND e.version = t.version");
-		sql.append(" INNER JOIN changesets c ON e.changeset_id = c.id INNER JOIN users u ON c.user_id = u.id");
-		
-		LOG.log(Level.FINER, "Entity history query: " + sql);
-
-		return sql.toString();
-	}
-
-
-	private ReleasableIterator<EntityHistory<T>> getFeaturelessEntityHistory(
-			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
-		
-		FileBasedSort<EntityHistory<T>> sortingStore =
-			new FileBasedSort<EntityHistory<T>>(
-				new SingleClassObjectSerializationFactory(EntityHistory.class),
-				new EntityHistoryComparator<T>(), true);
-		
-		try {
-			String sql;
-			SortingStoreRowMapperListener<EntityHistory<T>> storeListener;
-			EntityHistoryRowMapper<T> entityHistoryRowMapper;
-			RowMapperListener<CommonEntityData> entityRowMapper;
-			EntityDataRowMapper entityDataRowMapper;
-			ReleasableIterator<EntityHistory<T>> resultIterator;
-			
-			sql = buildFeaturelessEntityHistoryQuery(selectedEntityStatement);
-			
-			// Sends all received data into the object store.
-			storeListener = new SortingStoreRowMapperListener<EntityHistory<T>>(sortingStore);
-			// Retrieves the visible attribute allowing modifies to be distinguished
-			// from deletes.
-			entityHistoryRowMapper = new EntityHistoryRowMapper<T>(storeListener);
-			// Retrieves the entity type specific columns and builds the entity objects.
-			entityRowMapper = getEntityRowMapper(entityHistoryRowMapper);
-			// Retrieves the common entity information.
-			entityDataRowMapper = new EntityDataRowMapper(entityRowMapper, false);
-			
-			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
-			namedParamJdbcTemplate.query(sql, parameterSource, entityDataRowMapper);
-			
-			// Open a iterator on the store that will release the store upon completion.
-			resultIterator = new StoreReleasingIterator<EntityHistory<T>>(sortingStore.iterate(), sortingStore);
-			
-			// The store itself shouldn't be released now that it has been attached to the iterator.
-			sortingStore = null;
-			
-			return resultIterator;
-			
-		} finally {
-			if (sortingStore != null) {
-				sortingStore.release();
-			}
-		}
-	}
-	
-	
-	private ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> getTagHistory(String selectedEntityStatement,
-			MapSqlParameterSource parameterSource) {
-		
-		FileBasedSort<DbFeatureHistory<DbFeature<Tag>>> sortingStore =
-			new FileBasedSort<DbFeatureHistory<DbFeature<Tag>>>(
-				new SingleClassObjectSerializationFactory(DbFeatureHistory.class),
-				new DbFeatureHistoryComparator<Tag>(), true);
-		
-		try {
-			String sql;
-			SortingStoreRowMapperListener<DbFeatureHistory<DbFeature<Tag>>> storeListener;
-			DbFeatureHistoryRowMapper<DbFeature<Tag>> dbFeatureHistoryRowMapper;
-			DbFeatureRowMapper<Tag> dbFeatureRowMapper;
-			TagRowMapper tagRowMapper;
-			ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> resultIterator;
-			
-			sql =
-				"SELECT et."
-				+ entityName
-				+ "_id AS id, et.k, et.v, et.version"
-				+ " FROM "
-				+ entityName
-				+ "_tags et"
-				+ " INNER JOIN "
-				+ selectedEntityStatement
-				+ " t ON et."
-				+ entityName
-				+ "_id = t."
-				+ entityName
-				+ "_id AND et.version = t.version";
-			
-			LOG.log(Level.FINER, "Tag history query: " + sql);
-			
-			// Sends all received data into the object store.
-			storeListener = new SortingStoreRowMapperListener<DbFeatureHistory<DbFeature<Tag>>>(sortingStore);
-			// Retrieves the version information associated with the tag.
-			dbFeatureHistoryRowMapper = new DbFeatureHistoryRowMapper<DbFeature<Tag>>(storeListener);
-			// Retrieves the entity information associated with the tag.
-			dbFeatureRowMapper = new DbFeatureRowMapper<Tag>(dbFeatureHistoryRowMapper);
-			// Retrieves the basic tag information.
-			tagRowMapper = new TagRowMapper(dbFeatureRowMapper);
-			
-			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
-			namedParamJdbcTemplate.query(sql, parameterSource, tagRowMapper);
-			
-			// Open a iterator on the store that will release the store upon completion.
-			resultIterator = new StoreReleasingIterator<DbFeatureHistory<DbFeature<Tag>>>(sortingStore.iterate(),
-					sortingStore);
-			
-			// The store itself shouldn't be released now that it has been attached to the iterator.
-			sortingStore = null;
-			
-			return resultIterator;
-			
-		} finally {
-			if (sortingStore != null) {
-				sortingStore.release();
-			}
-		}
-	}
-	
-	
-	private ReleasableIterator<EntityHistory<T>> getEntityHistory(
-			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
-		ReleasableContainer releasableContainer;
-		
-		releasableContainer = new ReleasableContainer();
-		try {
-			ReleasableIterator<EntityHistory<T>> entityIterator;
-			ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> tagIterator;
-			List<FeatureHistoryPopulator<T, ?, ?>> featurePopulators;
-			EntityHistoryReader<T> entityHistoryReader;
-			
-			entityIterator = releasableContainer.add(
-					getFeaturelessEntityHistory(selectedEntityStatement, parameterSource));
-			tagIterator = releasableContainer.add(
-					getTagHistory(selectedEntityStatement, parameterSource));
-			
-			featurePopulators = getFeatureHistoryPopulators(selectedEntityStatement, parameterSource);
-			for (FeatureHistoryPopulator<T, ?, ?> featurePopulator : featurePopulators) {
-				releasableContainer.add(featurePopulator);
-			}
-			
-			entityHistoryReader = new EntityHistoryReader<T>(entityIterator, tagIterator, featurePopulators);
-			
-			// The sources are now all attached to the history reader so we don't want to release
-			// them in the finally block.
-			releasableContainer.clear();
-			
-			return entityHistoryReader;
-			
-		} finally {
-			releasableContainer.release();
-		}
-	}
-	
-	
-	private ReleasableIterator<ChangeContainer> getChangeHistory(
-			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
-		
-		return new ChangeReader<T>(getEntityHistory(selectedEntityStatement, parameterSource), getContainerFactory());
-	}
-	
-	
-	private List<Integer> buildTransactionRanges(long bottomTransactionId, long topTransactionId) {
-		List<Integer> rangeValues;
-		int currentXid;
-		int finishXid;
-		
-		// Begin building the values to use in the WHERE clause transaction ranges. Each pair of ids
-		// in this list will become a range selection.
-		rangeValues = new ArrayList<Integer>();
-		
-		// If the bottom and top values are identical then no ranges are required. If we don't add
-		// this check here we could end up querying for all 4 billion transaction ids or all data in
-		// the database.
-		if (bottomTransactionId == topTransactionId) {
-			return rangeValues;
-		}
-		
-		// We must now convert the top and bottom long representations of xids into their integer
-		// format because that is how they are indexed in the database.
-		
-		// The bottom id is the first transaction id that has not been read yet, so we begin reading from it.
-		currentXid = (int) bottomTransactionId;
-		rangeValues.add(currentXid);
-		
-		// The top transaction id is the first unassigned transaction id, so we must stop reading one short of that.
-		finishXid = ((int) topTransactionId) - 1;
-		
-		// Process until we have enough ranges to reach the finish xid.
-		do {
-			// Determine how to terminate the current transaction range.
-			if (currentXid <= 2 && finishXid >= 0) {
-				// The range overlaps special ids 0-2 which should never be queried on.
-				
-				// Terminate the current range before the special values.
-				rangeValues.add(-1);
-				// Begin the new range after the special values.
-				rangeValues.add(3);
-				
-				currentXid = 3;
-				
-			} else if (finishXid < currentXid) {
-				// The range crosses the integer overflow point. Only do this check once we are
-				// past 2 because the xid special values 0-2 may need to be crossed first.
-				
-				// Terminate the current range at the maximum int value.
-				rangeValues.add(Integer.MAX_VALUE);
-				// Begin a new range at the minimum int value.
-				rangeValues.add(Integer.MIN_VALUE);
-				
-				currentXid = Integer.MIN_VALUE;
-				
-			} else {
-				// There are no problematic transaction id values between the current value and
-				// the top transaction id so terminate the current range at the top transaction
-				// id.
-				rangeValues.add(finishXid);
-				currentXid = finishXid;
-			}
-		} while (currentXid != finishXid);
-		
-		return rangeValues;
-	}
-	
-	
-	private void buildTransactionRangeWhereClause(StringBuilder sql, MapSqlParameterSource parameters,
-			long bottomTransactionId, long topTransactionId) {
-		List<Integer> rangeValues;
-		
-		// Determine the transaction ranges required to select all transactions between the bottom
-		// and top values. This takes into account transaction id wrapping issues and reserved ids.
-		rangeValues = buildTransactionRanges(bottomTransactionId, topTransactionId);
-		
-		// Create a range clause for each range pair.
-		for (int i = 0; i < rangeValues.size(); i = i + 2) {
-			if (i > 0) {
-				sql.append(" OR ");
-			}
-			sql.append("(");
-			sql.append("xid_to_int4(xmin) >= :rangeValue").append(i);
-			sql.append(" AND ");
-			sql.append("xid_to_int4(xmin) <= :rangeValue").append(i + 1);
-			sql.append(")");
-			
-			parameters.addValue("rangeValue" + i, rangeValues.get(i), Types.INTEGER);
-			parameters.addValue("rangeValue" + (i + 1), rangeValues.get(i + 1), Types.INTEGER);
-		}
-	}
-	
-	
-	private void buildTransactionIdListWhereClause(StringBuilder sql, List<Long> transactionIdList) {
-		for (int i = 0; i < transactionIdList.size(); i++) {
-			if (i > 0) {
-				sql.append(",");
-			}
-		
-			// Must cast to int to allow the integer based xmin index to be used correctly.
-			sql.append((int) transactionIdList.get(i).longValue());
-		}
-	}
-
-
-	/**
-	 * Retrieves the changes that have were made by a set of transactions.
-	 * 
-	 * @param predicates
-	 *            Contains the predicates defining the transactions to be queried.
-	 * @return An iterator pointing at the identified records.
-	 */
-	public ReleasableIterator<ChangeContainer> getHistory(ReplicationQueryPredicates predicates) {
-		String selectedEntityTableName;
-		StringBuilder sql;
-		MapSqlParameterSource parameterSource;
-		
-		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
-		// prevent this. Note that this is not recommended practice according to documentation
-		// but fixing this would require modifying the table statistics gathering
-		// configuration on the production database to produce better plans.
-		jdbcTemplate.execute(
-				"set local enable_seqscan = false;"
-				+ "set local enable_mergejoin = false;"
-				+ "set local enable_hashjoin = false");
-		
-		parameterSource = new MapSqlParameterSource();
-		
-		selectedEntityTableName = "tmp_" + entityName + "s";
-		
-		sql = new StringBuilder();
-		sql.append("CREATE TEMPORARY TABLE ");
-		sql.append(selectedEntityTableName);
-		sql.append(" ON COMMIT DROP");
-		sql.append(" AS SELECT ");
-		sql.append(entityName);
-		sql.append("_id, version FROM ");
-		sql.append(entityName);
-		sql.append("s WHERE ((");
-		// Add the main transaction ranges to the where clause.
-		buildTransactionRangeWhereClause(sql, parameterSource, predicates.getBottomTransactionId(), predicates
-				.getTopTransactionId());
-		sql.append(")");
-		// If previously active transactions have become ready since the last invocation we include those as well.
-		if (predicates.getReadyList().size() > 0) {
-			sql.append(" OR xid_to_int4(xmin) IN (");
-			buildTransactionIdListWhereClause(sql, predicates.getReadyList());
-			sql.append(")");
-		}
-		sql.append(")");
-		// Any active transactions must be explicitly excluded.
-		if (predicates.getActiveList().size() > 0) {
-			sql.append(" AND xid_to_int4(xmin) NOT IN (");
-			buildTransactionIdListWhereClause(sql, predicates.getActiveList());
-			sql.append(")");
-		}
-		
-		LOG.log(Level.FINER, "Entity identification query: " + sql);
-		
-		namedParamJdbcTemplate.update(sql.toString(), parameterSource);
-		
-		jdbcTemplate.update("ALTER TABLE ONLY " + selectedEntityTableName
-				+ " ADD CONSTRAINT pk_" + selectedEntityTableName
-				+ " PRIMARY KEY (" + entityName + "_id, version)");
-		jdbcTemplate.update("ANALYZE " + selectedEntityTableName);
-		
-		if (LOG.isLoggable(Level.FINER)) {
-			LOG.log(Level.FINER,
-					jdbcTemplate.queryForInt("SELECT Count(id) FROM " + selectedEntityTableName) + " "
-					+ entityName + " records located.");
-		}
-		
-		return getChangeHistory(selectedEntityTableName, new MapSqlParameterSource());
-	}
-
-
-	/**
-	 * Retrieves the changes that have were made between two points in time.
-	 * 
-	 * @param intervalBegin
-	 *            Marks the beginning (inclusive) of the time interval to be checked.
-	 * @param intervalEnd
-	 *            Marks the end (exclusive) of the time interval to be checked.
-	 * @return An iterator pointing at the identified records.
-	 */
-	public ReleasableIterator<ChangeContainer> getHistory(Date intervalBegin, Date intervalEnd) {
-		String selectedEntityTableName;
-		StringBuilder sql;
-		MapSqlParameterSource parameterSource;
-		
-		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
-		// prevent this. Note that this is not recommended practice according to documentation
-		// but fixing this would require modifying the table statistics gathering
-		// configuration on the production database to produce better plans.
-		jdbcTemplate.execute(
-				"set local enable_seqscan = false;"
-				+ "set local enable_mergejoin = false;"
-				+ "set local enable_hashjoin = false");
-		
-		selectedEntityTableName = "tmp_" + entityName + "s";
-		
-		sql = new StringBuilder();
-		sql.append("CREATE TEMPORARY TABLE ");
-		sql.append(selectedEntityTableName);
-		sql.append(" ON COMMIT DROP");
-		sql.append(" AS SELECT ");
-		sql.append(entityName);
-		sql.append("_id, version FROM ");
-		sql.append(entityName);
-		sql.append("s WHERE timestamp > :intervalBegin AND timestamp <= :intervalEnd");
-		
-		LOG.log(Level.FINER, "Entity identification query: " + sql);
-
-		parameterSource = new MapSqlParameterSource();
-		parameterSource.addValue("intervalBegin", intervalBegin, Types.TIMESTAMP);
-		parameterSource.addValue("intervalEnd", intervalEnd, Types.TIMESTAMP);
-		
-		namedParamJdbcTemplate.update(sql.toString(), parameterSource);
-		
-		jdbcTemplate.update("ANALYZE " + selectedEntityTableName);
-		
-		return getChangeHistory(selectedEntityTableName, new MapSqlParameterSource());
-	}
-	
-	
-	/**
-	 * Retrieves all changes in the database.
-	 * 
-	 * @return An iterator pointing at the identified records.
-	 */
-	public ReleasableIterator<ChangeContainer> getHistory() {
-		// Join the entity table to itself which will return all records.
-		return getChangeHistory(entityName + "s", new MapSqlParameterSource());
-	}
-	
-	
-	/**
-	 * Retrieves all current data in the database.
-	 * 
-	 * @return An iterator pointing at the current records.
-	 */
-	public ReleasableIterator<EntityContainer> getCurrent() {
-		// Join the entity table to the current version of itself which will return all current
-		// records.
-		return new EntityContainerReader<T>(
-				getEntityHistory("(SELECT id AS " + entityName + "_id, version FROM current_"
-							+ entityName + "s WHERE visible = TRUE)",
-						new MapSqlParameterSource()), getContainerFactory());
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationDestination.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationDestination.java
deleted file mode 100644
index ab8b92c..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationDestination.java
+++ /dev/null
@@ -1,188 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.util.AtomicFileCreator;
-import org.openstreetmap.osmosis.core.util.FileBasedLock;
-import org.openstreetmap.osmosis.replication.common.ReplicationFileSequenceFormatter;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
-
-
-/**
- * A file-based destination for replication data.  This writes files beginning at 1.osc.gz and increasing incrementally.
- */
-public class FileReplicationDestination implements ReplicationDestination {
-	
-	private static final String LOCK_FILE = "replicate.lock";
-	private static final String STATE_FILE = "state.txt";
-	private static final String SEQUENCE_STATE_FILE_SUFFIX = ".state.txt";
-	private static final String CHANGE_FILE_SUFFIX = ".osc.gz";
-	private static final CompressionMethod CHANGE_FILE_COMPRESSION = CompressionMethod.GZip;
-	
-
-	private File stateFile;
-	private FileBasedLock fileLock;
-	private boolean lockObtained;
-	private FileReplicationStatePersistor statePersistor;
-	private ReplicationState state;
-	private AtomicFileCreator atomicXmlFile;
-	private XmlChangeWriter writer;
-	private ReplicationFileSequenceFormatter sequenceFormatter;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param workingDirectory
-	 *            The directory that all files will be produced in.
-	 */
-	public FileReplicationDestination(File workingDirectory) {
-		stateFile = new File(workingDirectory, STATE_FILE);
-		
-		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
-		
-		statePersistor = new FileReplicationStatePersistor(stateFile);
-		
-		sequenceFormatter = new ReplicationFileSequenceFormatter(workingDirectory);
-	}
-	
-	
-	private void ensureLocked() {
-		if (!lockObtained) {
-			fileLock.lock();
-			lockObtained = true;
-		}
-	}
-	
-	
-	private File generateFormattedSequenceFile(String fileNameSuffix) {
-		File formattedSequenceNumber;
-		
-		// Generate the formatted sequence number.
-		formattedSequenceNumber = sequenceFormatter.getFormattedName(state.getSequenceNumber(), fileNameSuffix);
-		
-		return formattedSequenceNumber;
-	}
-	
-	
-	private void initializeWriter() {
-		if (writer == null) {
-			ensureLocked();
-			
-			// Create an atomic file creator for the xml file.
-			atomicXmlFile = new AtomicFileCreator(generateFormattedSequenceFile(CHANGE_FILE_SUFFIX));
-			
-			// Create a writer writing to a new temporary file.
-			writer = new XmlChangeWriter(
-					atomicXmlFile.getTmpFile(),
-					CHANGE_FILE_COMPRESSION);
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(ChangeContainer change) {
-		ensureLocked();
-		initializeWriter();
-		
-		writer.process(change);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		ensureLocked();
-		
-		// We can't do anything if we haven't loaded state yet.
-		if (state != null) {
-			// We won't write an output file if we are initializing.
-			if (statePersistor.stateExists()) {
-				// When replicating, we will always write a file even if there is no data.
-				initializeWriter();
-				
-				// Close the output file and rename it to the final file.
-				writer.complete();
-				writer.release();
-				writer = null;
-				atomicXmlFile.renameTmpFileToCurrent();
-			}
-			
-			// The final step is to save the current state. This must be done last so that if a crash
-			// occurs during processing it starts from the same point as last time.
-			statePersistor.saveState(state);
-			
-			// Also the state to a sequence specific state file.
-			new FileReplicationStatePersistor(generateFormattedSequenceFile(SEQUENCE_STATE_FILE_SUFFIX))
-					.saveState(state);
-		}
-		
-		fileLock.unlock();
-		lockObtained = false;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		if (writer != null) {
-			writer.release();
-			writer = null;
-		}
-		
-		fileLock.release();
-		lockObtained = false;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReplicationState loadState() {
-		ensureLocked();
-		
-		if (state == null) {
-			state = statePersistor.loadState();
-		}
-		
-		return state;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void saveState(ReplicationState newState) {
-		ensureLocked();
-		
-		// The caller will usually be passing back the state object that was initially provided by
-		// this class, however when initialising a new one will need to be created externally.
-		state = newState;
-		
-		// Don't save the state to file at this point, we will write the state out during the
-		// complete call in order to simulate a transaction.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean stateExists() {
-		ensureLocked();
-		
-		return statePersistor.stateExists();
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationStatePersistor.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationStatePersistor.java
deleted file mode 100644
index 92ec862..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FileReplicationStatePersistor.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.io.File;
-import java.util.Properties;
-
-import org.openstreetmap.osmosis.core.util.PropertiesPersister;
-
-
-/**
- * A file-based persister for replication state.
- */
-public class FileReplicationStatePersistor implements ReplicationStatePersister {
-	
-	private PropertiesPersister persister;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param stateFile
-	 *            The location of the file containing the persisted data.
-	 */
-	public FileReplicationStatePersistor(File stateFile) {
-		persister = new PropertiesPersister(stateFile);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public ReplicationState loadState() {
-		return new ReplicationState(persister.load());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void saveState(ReplicationState state) {
-		Properties properties;
-		
-		properties = new Properties();
-		state.store(properties);
-		
-		persister.store(properties);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean stateExists() {
-		return persister.exists();
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationDestination.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationDestination.java
deleted file mode 100644
index 45b5bb9..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationDestination.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-
-
-/**
- * Provides {@link Replicator} with end points for the change streams that it produces.
- */
-public interface ReplicationDestination extends ChangeSink, ReplicationStatePersister {
-	// This interface exists only to combine the functionality of its parents.
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java
deleted file mode 100644
index fd73441..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java
+++ /dev/null
@@ -1,326 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-import org.openstreetmap.osmosis.core.store.Storeable;
-import org.openstreetmap.osmosis.core.time.DateFormatter;
-import org.openstreetmap.osmosis.core.time.DateParser;
-
-
-/**
- * Contains the state to be remembered between replication invocations. This state ensures that no
- * data is missed during replication, and none is repeated.
- */
-public class ReplicationState implements Storeable {
-	private long txnMax;
-	private long txnMaxQueried;
-	private List<Long> txnActive;
-	private List<Long> txnReady;
-	private Date timestamp;
-	private long sequenceNumber;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param txnMax
-	 *            The maximum transaction id in the database.
-	 * @param txnMaxQueried
-	 *            The maximum transaction id currently replicated from the database.
-	 * @param txnActive
-	 *            The currently active transaction ids.
-	 * @param txnReady
-	 *            The previously active transaction ids that can now be queried.
-	 * @param timestamp
-	 *            The maximum timestamp of data currently read from the database.
-	 * @param sequenceNumber
-	 *            The replication sequence number.
-	 */
-	public ReplicationState(long txnMax, long txnMaxQueried, List<Long> txnActive, List<Long> txnReady,
-			Date timestamp, long sequenceNumber) {
-		this.txnMax = txnMax;
-		this.txnMaxQueried = txnMaxQueried;
-		this.txnActive = new ArrayList<Long>(txnActive);
-		this.txnReady = new ArrayList<Long>(txnReady);
-		this.timestamp = timestamp;
-		this.sequenceNumber = sequenceNumber;
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param reader
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public ReplicationState(StoreReader reader, StoreClassRegister scr) {
-		int txnActiveCount;
-		int txnReadyCount;
-		
-		txnMax = reader.readLong();
-		txnMaxQueried = reader.readLong();
-		
-		txnActiveCount = reader.readInteger();
-		txnActive = new ArrayList<Long>();
-		for (int i = 0; i < txnActiveCount; i++) {
-			txnActive.add(reader.readLong());
-		}
-		
-		txnReadyCount = reader.readInteger();
-		txnReady = new ArrayList<Long>();
-		for (int i = 0; i < txnReadyCount; i++) {
-			txnReady.add(reader.readLong());
-		}
-		
-		timestamp = new Date(reader.readLong());
-		sequenceNumber = reader.readLong();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param properties
-	 *            The properties to load state from.
-	 */
-	public ReplicationState(Properties properties) {
-		txnMax = Long.parseLong(properties.getProperty("txnMax"));
-		txnMaxQueried = Long.parseLong(properties.getProperty("txnMaxQueried"));
-		txnActive = fromString(properties.getProperty("txnActiveList"));
-		txnReady = fromString(properties.getProperty("txnReadyList"));
-		timestamp = new DateParser().parse(properties.getProperty("timestamp"));
-		sequenceNumber = Long.parseLong(properties.getProperty("sequenceNumber"));
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void store(StoreWriter writer, StoreClassRegister storeClassRegister) {
-		writer.writeLong(txnMax);
-		writer.writeLong(txnMaxQueried);
-		
-		writer.writeInteger(txnActive.size());
-		for (Long value : txnActive) {
-			writer.writeLong(value);
-		}
-		
-		writer.writeInteger(txnReady.size());
-		for (Long value : txnReady) {
-			writer.writeLong(value);
-		}
-		
-		writer.writeLong(timestamp.getTime());
-		writer.writeLong(sequenceNumber);
-	}
-
-
-	/**
-	 * Writes all state into the provided properties object.
-	 * 
-	 * @param properties
-	 *            The properties to be updated.
-	 */
-	public void store(Properties properties) {
-		properties.setProperty("txnMax", Long.toString(txnMax));
-		properties.setProperty("txnMaxQueried", Long.toString(txnMaxQueried));
-		properties.setProperty("txnActiveList", toString(txnActive));
-		properties.setProperty("txnReadyList", toString(txnReady));
-		properties.setProperty("timestamp", new DateFormatter().format(timestamp));
-		properties.setProperty("sequenceNumber", Long.toString(sequenceNumber));
-	}
-	
-	
-	private String toString(List<Long> values) {
-		StringBuilder buffer;
-		
-		buffer = new StringBuilder();
-		for (long value : values) {
-			if (buffer.length() > 0) {
-				buffer.append(',');
-			}
-			buffer.append(value);
-		}
-		
-		return buffer.toString();
-	}
-	
-	
-	private List<Long> fromString(String values) {
-		StringTokenizer tokens;
-		List<Long> result;
-		
-		tokens = new StringTokenizer(values, ",");
-		
-		result = new ArrayList<Long>();
-		while (tokens.hasMoreTokens()) {
-			result.add(Long.parseLong(tokens.nextToken()));
-		}
-		
-		return result;
-	}
-
-
-	/**
-	 * Gets the maximum transaction id in the database.
-	 * 
-	 * @return The transaction id.
-	 */
-	public long getTxnMax() {
-		return txnMax;
-	}
-	
-
-	/**
-	 * Sets the maximum transaction id in the database.
-	 * 
-	 * @param txnMax
-	 *            The transaction id.
-	 */
-	public void setTxnMax(long txnMax) {
-		this.txnMax = txnMax;
-	}
-
-
-	/**
-	 * Gets the maximum transaction id currently replicated from the database.
-	 * 
-	 * @return The transaction id.
-	 */
-	public long getTxnMaxQueried() {
-		return txnMaxQueried;
-	}
-
-
-	/**
-	 * Sets the maximum transaction id currently replicated from the database.
-	 * 
-	 * @param txnMaxQueried
-	 *            The transaction id.
-	 */
-	public void setTxnMaxQueried(long txnMaxQueried) {
-		this.txnMaxQueried = txnMaxQueried;
-	}
-
-
-	/**
-	 * Gets the currently active transaction ids. These cannot be replicated until they have
-	 * committed.
-	 * 
-	 * @return The list of transaction ids.
-	 */
-	public List<Long> getTxnActive() {
-		return txnActive;
-	}
-
-
-	/**
-	 * Gets the previously active transaction ids that can now be queried.
-	 * 
-	 * @return The list of transaction ids.
-	 */
-	public List<Long> getTxnReady() {
-		return txnReady;
-	}
-
-
-	/**
-	 * Gets the maximum timestamp of data currently read from the database.
-	 * 
-	 * @return The timestamp.
-	 */
-	public Date getTimestamp() {
-		return timestamp;
-	}
-
-
-	/**
-	 * Sets the maximum timestamp of data currently read from the database.
-	 * 
-	 * @param timestamp
-	 *            The timestamp.
-	 */
-	public void setTimestamp(Date timestamp) {
-		this.timestamp = timestamp;
-	}
-	
-	
-	/**
-	 * Gets the replication sequence number.
-	 * 
-	 * @return The sequence number.
-	 */
-	public long getSequenceNumber() {
-		return sequenceNumber;
-	}
-
-
-	/**
-	 * Sets the replication sequence number.
-	 * 
-	 * @param sequenceNumber
-	 *            The sequence number.
-	 */
-	public void setSequenceNumber(long sequenceNumber) {
-		this.sequenceNumber = sequenceNumber;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		boolean result;
-		
-		if (obj instanceof ReplicationState) {
-			ReplicationState compareState = (ReplicationState) obj;
-			
-			if (txnMax == compareState.txnMax
-					&& txnMaxQueried == compareState.txnMaxQueried
-					&& txnActive.equals(compareState.txnActive)
-					&& txnReady.equals(compareState.txnReady)
-					&& timestamp.equals(compareState.timestamp)
-					&& sequenceNumber == compareState.sequenceNumber) {
-				result = true;
-			} else {
-				result = false;
-			}
-		} else {
-			result = false;
-		}
-		
-		return result;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		return (int) sequenceNumber + (int) txnMax + (int) txnMaxQueried + (int) timestamp.getTime();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String toString() {
-		return "ReplicationState(txnMax=" + txnMax + ", txnMaxQueried=" + txnMaxQueried + ", txnActive=" + txnActive
-				+ ", txnReady=" + txnReady + ", timestamp=" + timestamp + ", sequenceNumber=" + sequenceNumber + ")";
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationStatePersister.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationStatePersister.java
deleted file mode 100644
index 3e29433..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationStatePersister.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-/**
- * Implementations of this interface provide persistence for replication state objects. This state
- * should be persisted after completion of the data replication, and preferably within the same
- * transaction to avoid duplicates in the face of failures.
- */
-public interface ReplicationStatePersister {
-	/**
-	 * Persists the state.
-	 * 
-	 * @param state
-	 *            The state to be persisted.
-	 */
-	void saveState(ReplicationState state);
-	
-	
-	/**
-	 * Loads the existing state.
-	 * 
-	 * @return The state to be loaded.
-	 */
-	ReplicationState loadState();
-	
-	
-	/**
-	 * Checks if state currently exists. If no state exists it will need to be initialized.
-	 * 
-	 * @return True if state exists, false otherwise.
-	 */
-	boolean stateExists();
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java
deleted file mode 100644
index cbf7288..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java
+++ /dev/null
@@ -1,317 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-
-
-/**
- * Replicates changes from the database utilising transaction snapshots.
- */
-public class Replicator {
-	
-	private static final Logger LOG = Logger.getLogger(Replicator.class.getName());
-	
-	
-	/**
-	 * The number of special transactions beginning from 0.
-	 */
-	private static final int SPECIAL_TRANSACTION_OFFSET = 3;
-	/**
-	 * This is the maximum number of transaction ids sent in a single query. If larger than 2 power
-	 * 16 it fails due to a 16 bit number failing, but still fails below that with a stack limit
-	 * being exceeded. The current value is near to the maximum value known to work, it will work
-	 * slightly higher but this is a round number. It is dependent on the max_stack_depth parameter
-	 * defined in postgresql.conf.
-	 */
-	private static final int TRANSACTION_QUERY_SIZE_MAX = 25000;
-	
-	
-	private ReplicationDestination destination;
-	private ReplicationSource source;
-	private TransactionSnapshotLoader snapshotLoader;
-	private SystemTimeLoader systemTimeLoader;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param source
-	 *            The source for all replication changes.
-	 * @param destination
-	 *            The destination for all replicated changes.
-	 * @param snapshotLoader
-	 *            Loads transaction snapshots from the database.
-	 * @param systemTimeLoader
-	 *            Loads the current system time from the database.
-	 */
-	public Replicator(ReplicationSource source, ReplicationDestination destination,
-			TransactionSnapshotLoader snapshotLoader, SystemTimeLoader systemTimeLoader) {
-		this.source = source;
-		this.destination = destination;
-		this.snapshotLoader = snapshotLoader;
-		this.systemTimeLoader = systemTimeLoader;
-	}
-	
-	
-	/**
-	 * Obtains a new transaction shapshot from the database and updates the state to match.
-	 * 
-	 * @param state
-	 *            The replication state.
-	 */
-	private void obtainNewSnapshot(ReplicationState state) {
-		TransactionSnapshot transactionSnapshot;
-		
-		// Obtain the latest transaction snapshot from the database.
-		transactionSnapshot = snapshotLoader.getTransactionSnapshot();
-		
-		// Update the xmax value.
-		state.setTxnMax(transactionSnapshot.getXMax());
-		
-		// Any items in the old active transaction list but not in the new active transaction list
-		// must be added to the ready list.
-		for (Iterator<Long> i = state.getTxnActive().iterator(); i.hasNext();) {
-			Long id;
-			
-			id = i.next();
-			
-			if (!transactionSnapshot.getXIpList().contains(id)) {
-				// They only need to be added if the maximum queried xmax has been passed.
-				if (compareTxnIds(id, state.getTxnMaxQueried()) < 0) {
-					state.getTxnReady().add(id);
-				}
-			}
-		}
-		
-		// The active transaction list must be updated to match the latest snapshot.
-		state.getTxnActive().clear();
-		state.getTxnActive().addAll(transactionSnapshot.getXIpList());
-		
-		if (LOG.isLoggable(Level.FINER)) {
-			LOG.finer("Updated replication state with new snapshot, maxTxnQueried="
-					+ state.getTxnMaxQueried() + ", maxTxn=" + state.getTxnMax()
-					+ ", txnActiveList=" + state.getTxnActive()
-					+ ", txnReadyList=" + state.getTxnReady() + ".");
-		}
-	}
-	
-	
-	/**
-	 * This performs a comparison of the two transaction ids using 32-bit arithmetic. The result is
-	 * (id1 - id2), but with both numbers cast to an integer prior to the comparison. This provides
-	 * a correct result even in the case where the transaction id has wrapped around to 0.
-	 * 
-	 * @param id1
-	 *            The first transaction id.
-	 * @param id2
-	 *            The second transaction id.
-	 * @return The difference between the two transaction ids (id1 - id2).
-	 */
-	private int compareTxnIds(long id1, long id2) {
-		return ((int) id1) - ((int) id2);
-	}
-	
-	
-	/**
-	 * This adds an offset to the transaction id. It takes into account the special values 0-2 and
-	 * adds an extra 3 to the offset accordingly.
-	 * 
-	 * @param id
-	 *            The transaction id.
-	 * @param increment
-	 *            The amount to increment the id.
-	 * @return The result transaction id.
-	 */
-	private long incrementTxnId(long id, int increment) {
-		int oldId;
-		int newId;
-		
-		oldId = (int) id;
-		newId = oldId + increment;
-		
-		if (oldId < 0 && newId >= 0) {
-			newId += SPECIAL_TRANSACTION_OFFSET;
-		}
-		
-		return newId;
-	}
-	
-	
-	private ReplicationQueryPredicates buildQueryPredicates(ReplicationState state) {
-		long topTransactionId;
-		int rangeLength;
-		ReplicationQueryPredicates predicates;
-		
-		// The top transaction id of the next query is the current xMax up to a maximum of
-		// TRANSACTION_QUERY_SIZE_MAX transactions.
-		topTransactionId = state.getTxnMax();
-		rangeLength = compareTxnIds(topTransactionId, state.getTxnMaxQueried());
-		if (rangeLength > TRANSACTION_QUERY_SIZE_MAX) {
-			topTransactionId = incrementTxnId(state.getTxnMaxQueried(), TRANSACTION_QUERY_SIZE_MAX);
-		}
-		
-		// Build the predicate object with the new range.
-		predicates = new ReplicationQueryPredicates(state.getTxnMaxQueried(), topTransactionId);
-		
-		// Update the state with the new queried marker.
-		state.setTxnMaxQueried(topTransactionId);
-		
-		// Copy the active transaction list into the predicate.
-		predicates.getActiveList().addAll(state.getTxnActive());
-		
-		// The state object will only contain ready ids for transaction ranges that have passed
-		// already so we must add all of them to the predicate so they get included in this query.
-		predicates.getReadyList().addAll(state.getTxnReady());
-		
-		// The ready list can be cleared on the state object now.
-		state.getTxnReady().clear();
-		
-		if (LOG.isLoggable(Level.FINER)) {
-			LOG.finer("Query predicates updated, bottomXid="
-					+ predicates.getBottomTransactionId()
-					+ ", topXid=" + predicates.getTopTransactionId()
-					+ ", activeXidList=" + predicates.getActiveList()
-					+ ", readyXidList=" + predicates.getReadyList() + ".");
-		}
-		
-		return predicates;
-	}
-	
-	
-	private void copyChanges(ReleasableIterator<ChangeContainer> sourceIterator, ReplicationState state) {
-		try {
-			Date currentTimestamp;
-			
-			// As we process, we must update the timestamp to match the latest record we have
-			// received.
-			currentTimestamp = state.getTimestamp();
-			
-			while (sourceIterator.hasNext()) {
-				ChangeContainer change;
-				Date nextTimestamp;
-				
-				change = sourceIterator.next();
-				nextTimestamp = change.getEntityContainer().getEntity().getTimestamp();
-				
-				if (currentTimestamp.compareTo(nextTimestamp) < 0) {
-					currentTimestamp = nextTimestamp;
-				}
-				
-				destination.process(change);
-			}
-			
-			state.setTimestamp(currentTimestamp);
-			
-		} finally {
-			sourceIterator.release();
-		}
-	}
-	
-	
-	/**
-	 * Replicates the next set of changes from the database.
-	 */
-	public void replicate() {
-		// If we have already run once we begin replication, otherwise we initialise to the current
-		// database state.
-		if (destination.stateExists()) {
-			LOG.fine("Replication state exists, beginning replication.");
-			replicateImpl();
-		} else {
-			LOG.fine("Replication state does not exist, initializing.");
-			initialize();
-		}
-	}
-	
-	
-	/**
-	 * Replicates the next set of changes from the database.
-	 */
-	private void replicateImpl() {
-		try {
-			ReplicationState state;
-			ReplicationQueryPredicates predicates;
-			Date systemTimestamp;
-			
-			// Determine the time of processing.
-			systemTimestamp = systemTimeLoader.getSystemTime();
-			if (LOG.isLoggable(Level.FINER)) {
-				LOG.finer("Loaded system time " + systemTimestamp + " from the database.");
-			}
-			
-			// Load the current replication state.
-			state = destination.loadState();
-			
-			// Increment the current replication sequence number.
-			state.setSequenceNumber(state.getSequenceNumber() + 1);
-			if (LOG.isLoggable(Level.FINER)) {
-				LOG.finer("Replication sequence number is " + state.getSequenceNumber() + ".");
-			}
-			
-			// If the maximum queried transaction id has reached the maximum transaction id then a new
-			// transaction snapshot must be obtained in order to get more data.
-			if (compareTxnIds(state.getTxnMaxQueried(), state.getTxnMax()) >= 0) {
-				obtainNewSnapshot(state);
-			}
-			
-			// Obtain the predicates to use during the query.
-			predicates = buildQueryPredicates(state);
-			
-			// Write the changes to the destination.
-			if (predicates.getBottomTransactionId() != predicates.getTopTransactionId()) {
-				copyChanges(source.getHistory(predicates), state);
-			}
-			
-			// If we have completely caught up to the database, we can update the timestamp to the
-			// database system timestamp. Otherwise we leave the timestamp set at the value
-			// determined while processing changes.
-			if (compareTxnIds(state.getTxnMaxQueried(), state.getTxnMax()) >= 0) {
-				state.setTimestamp(systemTimestamp);
-			}
-			
-			// Persist the updated replication state.
-			destination.saveState(state);
-			
-			// Commit changes.
-			destination.complete();
-			
-		} finally {
-			destination.release();
-		}
-	}
-	
-	
-	/**
-	 * Initialises the destination using the current state of the source.
-	 */
-	private void initialize() {
-		try {
-			TransactionSnapshot snapshot;
-			ReplicationState state;
-			Date systemTime;
-			
-			snapshot = snapshotLoader.getTransactionSnapshot();
-			systemTime = systemTimeLoader.getSystemTime();
-			
-			// Create a new state initialised with the current state of the database.
-			state = new ReplicationState(snapshot.getXMax(), snapshot.getXMax(), snapshot.getXIpList(),
-					new ArrayList<Long>(), systemTime, 0);
-			
-			// Persist the updated replication state.
-			destination.saveState(state);
-			
-			// Commit changes.
-			destination.complete();
-			
-		} finally {
-			destination.release();
-		}
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java
deleted file mode 100644
index fb47cd0..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.util.Date;
-
-import org.springframework.jdbc.core.JdbcTemplate;
-
-
-/**
- * A DAO providing access to the system time on the database server. This avoids relying on the
- * clock of this system which may be different.
- */
-public class TimeDao implements SystemTimeLoader {
-	
-	private JdbcTemplate jdbcTemplate;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Used to access the database.
-	 */
-	public TimeDao(JdbcTemplate jdbcTemplate) {
-		this.jdbcTemplate = jdbcTemplate;
-	}
-	
-	
-	/**
-	 * Gets the system time of the database server.
-	 * 
-	 * @return The timestamp.
-	 */
-	public Date getSystemTime() {
-		return jdbcTemplate.queryForObject("SELECT now() AS SystemTime", Date.class);
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java
deleted file mode 100644
index 3915a09..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Reads active transaction ids from the database allowing up-to-current queries to be performed
- * when extracting changesets from the history tables.
- */
-public class TransactionDao implements TransactionSnapshotLoader {
-	private static final Logger LOG = Logger.getLogger(TransactionDao.class.getName());
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Used to access the database.
-	 */
-	public TransactionDao(JdbcTemplate jdbcTemplate) {
-		this.jdbcTemplate = new SimpleJdbcTemplate(jdbcTemplate);
-	}
-	
-	
-	/**
-	 * Obtains the current database snapshot.
-	 * 
-	 * @return The transaction snapshot.
-	 */
-	public TransactionSnapshot getTransactionSnapshot() {
-		String snapshotString;
-		TransactionSnapshot snapshot; 
-		
-		snapshotString = jdbcTemplate.queryForObject("SELECT txid_current_snapshot()", String.class);
-		
-		snapshot = new TransactionSnapshot(snapshotString);
-		
-		if (LOG.isLoggable(Level.FINER)) {
-			LOG.finer("Loaded new database snapshot, xmin=" + snapshot.getXMin()
-					+ ", xmax=" + snapshot.getXMax()
-					+ ", xiplist=" + snapshot.getXIpList());
-		}
-		
-		return snapshot;
-	}
-}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotLoader.java b/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotLoader.java
deleted file mode 100644
index 13cd914..0000000
--- a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotLoader.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-
-/**
- * Obtains transaction snapshots used for replication.
- */
-public interface TransactionSnapshotLoader {
-	/**
-	 * Obtains the current database snapshot.
-	 * 
-	 * @return The transaction snapshot.
-	 */
-	TransactionSnapshot getTransactionSnapshot();
-}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java b/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java
deleted file mode 100644
index ca52938..0000000
--- a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.apidb.v0_6.impl.DatabaseUtilities;
-import org.openstreetmap.osmosis.core.Osmosis;
-import org.openstreetmap.osmosis.testutil.AbstractDataTest;
-
-
-/**
- * Tests the file-based database replicator.
- */
-public class ApidbFileReplicatorTest extends AbstractDataTest {
-
-    private final DatabaseUtilities dbUtils = new DatabaseUtilities(dataUtils);
-
-
-    /**
-     * A basic test loading an osm file into an API database and verifying that it gets replicated correctly.
-     * 
-     * @throws IOException if any file operations fail.
-     */
-    @Test
-    public void testLoadAndDump() throws IOException {
-        File authFile;
-        File snapshotFile;
-        File changesetFile;
-        File outputFile;
-        File workingDirectory;
-
-        // Generate input files.
-        authFile = dbUtils.getAuthorizationFile();
-        snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
-        changesetFile = dataUtils.createDataFile("v0_6/db-replicate-changeset.osc");
-        outputFile = dataUtils.newFile();
-        workingDirectory = dataUtils.newFolder();
-
-        // Remove all existing data from the database.
-        dbUtils.truncateDatabase();
-        
-        // Initialise replication.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--replicate-apidb-0.6",
-        		"authFile=" + authFile.getPath(),
-                "allowIncorrectSchemaVersion=true",
-        		"directory=" + workingDirectory.getPath()
-                });
-
-        // Load the database with a dataset.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--read-xml-0.6",
-        		snapshotFile.getPath(),
-        		"--write-apidb-0.6",
-                "authFile=" + authFile.getPath(),
-        		"allowIncorrectSchemaVersion=true"
-                });
-        
-        // Run replication.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--replicate-apidb-0.6",
-        		"authFile=" + authFile.getPath(),
-                "allowIncorrectSchemaVersion=true",
-        		"directory=" + workingDirectory.getPath()
-                });
-
-        // Apply the changeset file to the database.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--read-xml-change-0.6",
-        		changesetFile.getPath(),
-        		"--write-apidb-change-0.6",
-                "authFile=" + authFile.getPath(),
-        		"allowIncorrectSchemaVersion=true" });
-        
-        // Run replication.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--replicate-apidb-0.6",
-        		"authFile=" + authFile.getPath(),
-                "allowIncorrectSchemaVersion=true",
-        		"directory=" + workingDirectory.getPath()
-                });
-        
-        // Ensure that replication runs successfully even if no data is available.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--replicate-apidb-0.6",
-        		"authFile=" + authFile.getPath(),
-                "allowIncorrectSchemaVersion=true",
-        		"directory=" + workingDirectory.getPath()
-                });
-        
-        // Decompress the result file.
-        Osmosis.run(new String[] {
-        		"-q",
-        		"--read-xml-change-0.6",
-        		new File(workingDirectory, "000/000/002.osc.gz").getPath(),
-        		"--write-xml-change-0.6",
-        		outputFile.getPath()
-                });
-
-        // Validate that the replicated file matches the input file.
-        dataUtils.compareFiles(changesetFile, outputFile);
-    }
-}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java b/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java
deleted file mode 100644
index 0ab6449..0000000
--- a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-
-
-/**
- * A mocked replication destination allowing the existing replication state to be loaded and the
- * current state maintained. All data processing calls such as process/complete/etc will be ignored.
- */
-public class MockReplicationDestination implements ReplicationDestination {
-	
-	private boolean stateExists;
-	private ReplicationState currentState;
-	
-	
-	/**
-	 * Creates a new instance with no state.
-	 */
-	public MockReplicationDestination() {
-		stateExists = false;
-	}
-	
-	
-	/**
-	 * Creates a new instance with an initial state.
-	 * 
-	 * @param initialState
-	 *            The initial replication state.
-	 */
-	public MockReplicationDestination(ReplicationState initialState) {
-		this.currentState = new ReplicationState(
-				initialState.getTxnMax(),
-				initialState.getTxnMaxQueried(),
-				initialState.getTxnActive(),
-				initialState.getTxnReady(),
-				initialState.getTimestamp(),
-				initialState.getSequenceNumber());
-		
-		stateExists = true;
-	}
-	
-	
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReplicationState loadState() {
-		if (!stateExists) {
-			throw new OsmosisRuntimeException("No state currently exists.");
-		}
-		
-		return currentState;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void saveState(ReplicationState state) {
-		this.currentState = state;
-		
-		stateExists = true;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean stateExists() {
-		return stateExists;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(ChangeContainer change) {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Do nothing.
-	}
-}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java b/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java
deleted file mode 100644
index c28ebae..0000000
--- a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * A mocked transaction snapshot loader allowing canned snapshots to be returned.
- */
-public class MockTransactionSnapshotLoader implements TransactionSnapshotLoader {
-
-	private List<TransactionSnapshot> snapshots = new ArrayList<TransactionSnapshot>();
-
-
-	/**
-	 * Gets the currently available snapshots.
-	 * 
-	 * @return The snapshots.
-	 */
-	public List<TransactionSnapshot> getSnapshots() {
-		return snapshots;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public TransactionSnapshot getTransactionSnapshot() {
-		return snapshots.remove(0);
-	}
-}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java b/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java
deleted file mode 100644
index d325ef0..0000000
--- a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java
+++ /dev/null
@@ -1,306 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.apidb.v0_6.impl;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * Tests for the Replicator class.
- */
-public class ReplicatorTest {
-
-	private Date buildDate(String rawDate) {
-		try {
-			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(rawDate);
-		} catch (ParseException e) {
-			throw new OsmosisRuntimeException("The date could not be parsed.", e);
-		}
-	}
-
-
-	/**
-	 * Tests replication behaviour during initialisation. Initialisation occurs the first time
-	 * replication is run.
-	 */
-	@Test
-	public void testInitialization() {
-		Replicator replicator;
-		MockReplicationSource source;
-		MockReplicationDestination destination;
-		MockTransactionSnapshotLoader snapshotLoader;
-		MockSystemTimeLoader timeLoader;
-		ReplicationState state;
-		
-		// Build the mocks.
-		source = new MockReplicationSource();
-		destination = new MockReplicationDestination();
-		snapshotLoader = new MockTransactionSnapshotLoader();
-		timeLoader = new MockSystemTimeLoader();
-		
-		// Instantiate the new replicator.
-		replicator = new Replicator(source, destination, snapshotLoader, timeLoader);
-		
-		// Provide initialisation data.
-		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:14"));
-		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:200:110,112"));
-		
-		// Launch the replication process.
-		replicator.replicate();
-		
-		// Verify the final state.
-		state = destination.loadState();
-		Assert.assertEquals("Incorrect final state.",
-				new ReplicationState(
-						200,
-						200,
-						Arrays.asList(new Long[]{110L, 112L}),
-						Arrays.asList(new Long[]{}),
-						buildDate("2009-10-11 12:13:14"),
-						0),
-				state);
-	}
-
-
-	/**
-	 * Tests replication behaviour when no replication is required.
-	 */
-	@Test
-	public void testNoAction() {
-		Replicator replicator;
-		MockReplicationSource source;
-		MockReplicationDestination destination;
-		MockTransactionSnapshotLoader snapshotLoader;
-		MockSystemTimeLoader timeLoader;
-		ReplicationState initialState;
-		ReplicationState finalState;
-		
-		// Build initial replication state.
-		initialState = new ReplicationState(
-				200,
-				200,
-				Arrays.asList(new Long[]{110L, 112L}),
-				Arrays.asList(new Long[]{}),
-				buildDate("2009-10-11 12:13:14"),
-				0);
-		
-		// Build the mocks.
-		source = new MockReplicationSource();
-		destination = new MockReplicationDestination(initialState);
-		snapshotLoader = new MockTransactionSnapshotLoader();
-		timeLoader = new MockSystemTimeLoader();
-		
-		// Instantiate the new replicator.
-		replicator = new Replicator(source, destination, snapshotLoader, timeLoader);
-		
-		// We want the snapshot loader to return the same snapshot to simulate no database changes.
-		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:200:110,112"));
-		// But we want the clock time to have progressed.
-		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
-		
-		// Launch the replication process.
-		replicator.replicate();
-		
-		// Verify that the final state does not match the initial state, but that the only
-		// difference is the time and increment sequence number.
-		finalState = destination.loadState();
-		Assert.assertFalse("Final state should not match initial state.", finalState.equals(initialState));
-		finalState.setTimestamp(initialState.getTimestamp());
-		finalState.setSequenceNumber(finalState.getSequenceNumber() - 1);
-		Assert.assertTrue("Final state should match initial state after updating timestamp.",
-				finalState.equals(initialState));
-		
-		// Verify that no changes were replicated.
-		Assert.assertTrue("No changes should have been replicated.", source.getPredicatesList().size() == 0);
-	}
-
-
-	/**
-	 * Tests replication behaviour when a simple replication interval is required.
-	 */
-	@Test
-	public void testSimpleIncrement() {
-		Replicator replicator;
-		MockReplicationSource source;
-		MockReplicationDestination destination;
-		MockTransactionSnapshotLoader snapshotLoader;
-		MockSystemTimeLoader timeLoader;
-		ReplicationState state;
-		ReplicationQueryPredicates predicates;
-		
-		// Build initial replication state.
-		state = new ReplicationState(
-				200,
-				200,
-				Arrays.asList(new Long[]{}),
-				Arrays.asList(new Long[]{}),
-				buildDate("2009-10-11 12:13:14"),
-				0);
-		
-		// Build the mocks.
-		source = new MockReplicationSource();
-		destination = new MockReplicationDestination(state);
-		snapshotLoader = new MockTransactionSnapshotLoader();
-		timeLoader = new MockSystemTimeLoader();
-		
-		// Instantiate the new replicator.
-		replicator = new Replicator(source, destination, snapshotLoader, timeLoader);
-		
-		// Set the snapshot loader to return a snapshot with higher xMax.
-		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:220"));
-		// We also want the clock time to have progressed.
-		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
-		
-		// Launch the replication process.
-		replicator.replicate();
-		
-		// Verify that the final state is correct.
-		state = destination.loadState();
-		Assert.assertEquals("Incorrect final state.",
-				new ReplicationState(
-						220,
-						220,
-						Arrays.asList(new Long[]{}),
-						Arrays.asList(new Long[]{}),
-						buildDate("2009-10-11 12:13:15"),
-						1),
-				state);
-		
-		// Verify that the correct changes were replicated.
-		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
-		predicates = source.getPredicatesList().get(0);
-		Assert.assertEquals("Incorrect active list.", Collections.emptyList(), predicates.getActiveList());
-		Assert.assertEquals("Incorrect ready list.", Collections.emptyList(), predicates.getReadyList());
-		Assert.assertEquals("Incorrect bottom transaction id.", 200, predicates.getBottomTransactionId());
-		Assert.assertEquals("Incorrect top transaction id.", 220, predicates.getTopTransactionId());
-	}
-
-
-	/**
-	 * Tests replication behaviour when active list manipulation is required.
-	 */
-	@Test
-	public void testInFlightTxnIncrement() {
-		Replicator replicator;
-		MockReplicationSource source;
-		MockReplicationDestination destination;
-		MockTransactionSnapshotLoader snapshotLoader;
-		MockSystemTimeLoader timeLoader;
-		ReplicationState state;
-		ReplicationQueryPredicates predicates;
-		
-		// Build initial replication state.
-		state = new ReplicationState(
-				200,
-				200,
-				Arrays.asList(new Long[]{180L, 185L}),
-				Arrays.asList(new Long[]{}),
-				buildDate("2009-10-11 12:13:14"),
-				0);
-		
-		// Build the mocks.
-		source = new MockReplicationSource();
-		destination = new MockReplicationDestination(state);
-		snapshotLoader = new MockTransactionSnapshotLoader();
-		timeLoader = new MockSystemTimeLoader();
-		
-		// Instantiate the new replicator.
-		replicator = new Replicator(source, destination, snapshotLoader, timeLoader);
-		
-		// Set the snapshot loader to return a snapshot with higher xMax.
-		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:220:185"));
-		// We also want the clock time to have progressed.
-		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
-		
-		// Launch the replication process.
-		replicator.replicate();
-		
-		// Verify that the final state is correct.
-		state = destination.loadState();
-		Assert.assertEquals("Incorrect final state.",
-				new ReplicationState(
-						220,
-						220,
-						Arrays.asList(new Long[]{185L}),
-						Arrays.asList(new Long[]{}),
-						buildDate("2009-10-11 12:13:15"),
-						1),
-				state);
-		
-		// Verify that the correct changes were replicated.
-		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
-		predicates = source.getPredicatesList().get(0);
-		Assert.assertEquals("Incorrect active list.", Arrays.asList(new Long[]{185L}), predicates.getActiveList());
-		Assert.assertEquals("Incorrect ready list.", Arrays.asList(new Long[]{180L}), predicates.getReadyList());
-		Assert.assertEquals("Incorrect bottom transaction id.", 200, predicates.getBottomTransactionId());
-		Assert.assertEquals("Incorrect top transaction id.", 220, predicates.getTopTransactionId());
-	}
-
-
-	/**
-	 * Tests replication behaviour when catching up from outage and some active transactions are overtaken.
-	 */
-	@Test
-	public void testOutageCatchupWithActiveTxns() {
-		Replicator replicator;
-		MockReplicationSource source;
-		MockReplicationDestination destination;
-		MockTransactionSnapshotLoader snapshotLoader;
-		MockSystemTimeLoader timeLoader;
-		ReplicationState state;
-		ReplicationQueryPredicates predicates;
-		
-		// Build initial replication state.
-		state = new ReplicationState(
-				5,
-				5,
-				Arrays.asList(new Long[]{24000L, 26000L}),
-				Arrays.asList(new Long[]{}),
-				buildDate("2009-10-11 12:13:14"),
-				0);
-		
-		// Build the mocks.
-		source = new MockReplicationSource();
-		destination = new MockReplicationDestination(state);
-		snapshotLoader = new MockTransactionSnapshotLoader();
-		timeLoader = new MockSystemTimeLoader();
-		
-		// Instantiate the new replicator.
-		replicator = new Replicator(source, destination, snapshotLoader, timeLoader);
-		
-		// Set the snapshot loader to return a snapshot with higher xMax.
-		snapshotLoader.getSnapshots().add(new TransactionSnapshot("20000:30000:26000"));
-		// We also want the clock time to have progressed.
-		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
-		
-		// Launch the replication process.
-		replicator.replicate();
-		
-		// Verify that the final state is correct.
-		state = destination.loadState();
-		Assert.assertEquals("Incorrect final state.",
-				new ReplicationState(
-						30000,
-						25005,
-						Arrays.asList(new Long[]{26000L}),
-						Arrays.asList(new Long[]{}),
-						buildDate("2009-10-11 12:13:14"),
-						1),
-				state);
-		
-		// Verify that the correct changes were replicated.
-		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
-		predicates = source.getPredicatesList().get(0);
-		Assert.assertEquals("Incorrect active list.", Arrays.asList(new Long[]{26000L}), predicates.getActiveList());
-		Assert.assertEquals("Incorrect ready list.", Arrays.asList(new Long[]{}), predicates.getReadyList());
-		Assert.assertEquals("Incorrect bottom transaction id.", 5, predicates.getBottomTransactionId());
-		Assert.assertEquals("Incorrect top transaction id.", 25005, predicates.getTopTransactionId());
-	}
-}
diff --git a/apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm
deleted file mode 100644
index d0486aa..0000000
--- a/apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1-revised"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <member type="node" ref="7" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm b/apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm
deleted file mode 100644
index 33faac2..0000000
--- a/apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/apidb/src/test/resources/data/template/v0_6/db-snapshot.osm b/apidb/src/test/resources/data/template/v0_6/db-snapshot.osm
deleted file mode 100644
index 0792662..0000000
--- a/apidb/src/test/resources/data/template/v0_6/db-snapshot.osm
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/areafilter/.checkstyle b/areafilter/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/areafilter/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/areafilter/.classpath b/areafilter/.classpath
deleted file mode 100644
index 95bf344..0000000
--- a/areafilter/.classpath
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/mysql-connector-java-5.1.18.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/areafilter/.gitignore b/areafilter/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/areafilter/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/areafilter/.project b/areafilter/.project
deleted file mode 100644
index 5d1d439..0000000
--- a/areafilter/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-AreaFilter</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/areafilter/build.xml b/areafilter/build.xml
deleted file mode 100644
index 2ad0a0b..0000000
--- a/areafilter/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.AreaFilter" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/areafilter/ivy.xml b/areafilter/ivy.xml
deleted file mode 100644
index e5f0ce0..0000000
--- a/areafilter/ivy.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-areafilter"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-    	
-    	<dependency org="postgresql" name="postgresql" rev="${dependency.version.postgresql}" conf="compile->default"/>
-    	<dependency org="mysql" name="mysql-connector-java" rev="${dependency.version.mysql}" conf="compile->default"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java b/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java
deleted file mode 100644
index c0f5ed3..0000000
--- a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java
+++ /dev/null
@@ -1,650 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.areafilter.v0_6;
-
-import java.util.Iterator;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.filter.common.IdTracker;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A base class for all tasks filter entities within an area.
- * 
- * @author Brett Henderson
- * @author Karl Newman
- */
-public abstract class AreaFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private IdTracker availableNodes; // Nodes within the area.
-	private IdTracker requiredNodes; // Nodes needed to complete referencing entities.
-	private IdTracker availableWays; // Ways within the area.
-	private IdTracker requiredWays; // Ways needed to complete referencing relations.
-	private IdTracker availableRelations; // Relations within the area.
-	private IdTracker requiredRelations; // Relations needed to complete referencing relations.
-	private boolean clipIncompleteEntities;
-	private boolean completeWays;
-	private boolean completeRelations;
-	private boolean storeEntities;
-    private boolean cascadingRelations;
-	private SimpleObjectStore<WayContainer> allWays;
-	private SimpleObjectStore<NodeContainer> allNodes;
-    // this duplicates as a container for held-back relations in the cascadingRelations case:
-	private SimpleObjectStore<RelationContainer> allRelations; 
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param idTrackerType
-	 *            Defines the id tracker implementation to use.
-	 * @param clipIncompleteEntities
-	 *            If true, entities referring to non-existent entities will be
-	 *            modified to ensure referential integrity. For example, ways
-	 *            will be modified to only include nodes inside the area.
-	 * @param completeWays
-	 *            Include all nodes for ways which have at least one node inside
-	 *            the filtered area.
-	 * @param completeRelations
-	 *            Include all relations referenced by other relations which have
-	 *            members inside the filtered area.
-	 * @param cascadingRelations
-	 *            Make sure that a relation referencing a relation which is included
-	 *            will also be included.
-	 */
-	public AreaFilter(
-			IdTrackerType idTrackerType, boolean clipIncompleteEntities, boolean completeWays,
-			boolean completeRelations, boolean cascadingRelations) {
-		this.clipIncompleteEntities = clipIncompleteEntities;
-		// Allowing complete relations without complete ways is very difficult and not allowed for
-		// now.
-		this.completeWays = completeWays || completeRelations;
-		this.completeRelations = completeRelations;
-        // cascadingRelations is included for free with any of the complete options so you don't
-        // need it if those are set.
-        this.cascadingRelations = cascadingRelations && !completeRelations && !completeWays;
-		
-		availableNodes = IdTrackerFactory.createInstance(idTrackerType);
-		requiredNodes = IdTrackerFactory.createInstance(idTrackerType);
-		availableWays = IdTrackerFactory.createInstance(idTrackerType);
-		requiredWays = IdTrackerFactory.createInstance(idTrackerType);
-		availableRelations = IdTrackerFactory.createInstance(idTrackerType);
-		requiredRelations = IdTrackerFactory.createInstance(idTrackerType);
-		
-		// If either complete ways or complete relations are required, then all data must be stored
-		// during processing.
-		storeEntities = completeWays || completeRelations;
-		if (storeEntities) {
-			allNodes = new SimpleObjectStore<NodeContainer>(
-					new SingleClassObjectSerializationFactory(NodeContainer.class), "afn", true);
-			allWays = new SimpleObjectStore<WayContainer>(
-					new SingleClassObjectSerializationFactory(WayContainer.class), "afw", true);
-			allRelations =
-				new SimpleObjectStore<RelationContainer>(
-						new SingleClassObjectSerializationFactory(RelationContainer.class), "afr", true);
-		} else if (cascadingRelations) {
-            allRelations = 
-				new SimpleObjectStore<RelationContainer>(
-						new SingleClassObjectSerializationFactory(RelationContainer.class), "afr", true);
-        }
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-	/**
-	 * Indicates if the node lies within the area required.
-	 * 
-	 * @param node
-	 *            The node to be checked.
-	 * @return True if the node lies within the area.
-	 */
-	protected abstract boolean isNodeWithinArea(Node node);
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		Node node;
-		
-		node = container.getEntity();
-		
-		// Check if we're storing entities for later.
-		if (storeEntities) {
-			allNodes.add(container);
-		}
-		
-		// Only add the node if it lies within the box boundaries.
-		if (isNodeWithinArea(node)) {
-			availableNodes.set(node.getId());
-			
-			// If we're not storing entities, we pass it on immediately.
-			if (!storeEntities) {
-				emitNode(container);
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		Way way;
-		boolean inArea;
-		
-		way = container.getEntity();
-
-		// Check if we're storing entities for later.
-		if (storeEntities) {
-			allWays.add(container);
-		}
-		
-		// First look through all the nodes to see if any are within the filtered area
-		inArea = false;
-		for (WayNode nodeReference : way.getWayNodes()) {
-			if (availableNodes.get(nodeReference.getNodeId())) {
-				inArea = true;
-				break;
-			}
-		}
-		
-		// If the way has at least one node in the filtered area.
-		if (inArea) {
-			availableWays.set(way.getId());
-			
-			// If complete ways are desired, mark any unavailable nodes as required.
-			if (completeWays) {
-				for (WayNode nodeReference : way.getWayNodes()) {
-					long nodeId = nodeReference.getNodeId();
-					
-					if (!availableNodes.get(nodeId)) {
-						requiredNodes.set(nodeId);
-					}
-				}
-			}
-			
-			// If we're not storing entities, we pass it on immediately.
-			if (!storeEntities) {
-				emitWay(container);
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		Relation relation;
-		boolean inArea;
-        boolean holdBackRelation;
-		
-		relation = container.getEntity();
-		
-		// First look through all the node and way members to see if any are within the filtered area
-		inArea = false;
-        holdBackRelation = false;
-
-		for (RelationMember member : relation.getMembers()) {
-			switch (member.getMemberType()) {
-			case Node:
-				inArea = availableNodes.get(member.getMemberId());
-				break;
-			case Way:
-				inArea = availableWays.get(member.getMemberId());
-				break;
-			case Relation:
-				inArea = availableRelations.get(member.getMemberId());
-				break;
-			default:
-				break;
-			}
-			
-			if (inArea) {
-				break;
-			}
-		}
-
-        if (cascadingRelations) { //  && referencesOtherRelation && (!inArea || clipIncompleteEntities)) {
-            holdBackRelation = true;
-        }
-
-		// Check if we're storing entities for later.
-		if (storeEntities || holdBackRelation) {
-			allRelations.add(container);
-        }
-		
-		// If the relation has at least one member in the filtered area.
-		if (inArea) {
-			availableRelations.set(relation.getId());
-			
-			// If we're not storing entities, we pass it on immediately.
-			if (!storeEntities && !holdBackRelation) {
-				emitRelation(container);
-			}
-		}
-	}
-
-
-	/**
-	 * Sends a node to the sink. This will perform any necessary transformations on the node before
-	 * sending it.
-	 * 
-	 * @param nodeContainer
-	 *            Node to be sent.
-	 */
-	private void emitNode(NodeContainer nodeContainer) {
-		sink.process(nodeContainer);
-	}
-
-
-	/**
-	 * Sends a way to the sink. This will perform any necessary transformations on the way before
-	 * sending it.
-	 * 
-	 * @param wayContainer
-	 *            Way to be sent.
-	 */
-	private void emitWay(WayContainer wayContainer) {
-		if (clipIncompleteEntities) {
-			WayContainer filteredWayContainer;
-			Way filteredWay;
-			
-			filteredWayContainer = wayContainer.getWriteableInstance();
-			filteredWay = filteredWayContainer.getEntity();
-			
-			// Remove node references for nodes that are unavailable.
-			for (Iterator<WayNode> i = filteredWay.getWayNodes().iterator(); i.hasNext();) {
-				WayNode nodeReference = i.next();
-				
-				if (!availableNodes.get(nodeReference.getNodeId())) {
-					i.remove();
-				}
-			}
-			
-			// Only add ways that contain nodes.
-			if (filteredWay.getWayNodes().size() > 0) {
-				sink.process(filteredWayContainer);
-			}
-			
-		} else {
-			sink.process(wayContainer);
-		}
-	}
-
-
-	/**
-	 * Sends a relation to the sink. This will perform any necessary transformations on the way before
-	 * sending it.
-	 * 
-	 * @param relationContainer
-	 *            Relation to be sent.
-	 */
-	private void emitRelation(RelationContainer relationContainer) {
-    	if (clipIncompleteEntities) {
-    		RelationContainer filteredRelationContainer;
-    		Relation filteredRelation;
-    		
-		    filteredRelationContainer = relationContainer.getWriteableInstance();
-		    filteredRelation = filteredRelationContainer.getEntity();
-		    
-		    // Remove members for entities that are unavailable.
-		    for (Iterator<RelationMember> i = filteredRelation.getMembers().iterator(); i.hasNext();) {
-		    	RelationMember member = i.next();
-		    	EntityType memberType;
-		    	long memberId;
-		    	
-		    	memberType = member.getMemberType();
-		    	memberId = member.getMemberId();
-		    	
-		    	switch (memberType) {
-		    	case Node:
-		    		if (!availableNodes.get(memberId)) {
-						i.remove();
-					}
-		    		break;
-		    	case Way:
-		    		if (!availableWays.get(memberId)) {
-						i.remove();
-					}
-		    		break;
-		    	case Relation:
-		    		if (!availableRelations.get(memberId)) {
-						i.remove();
-					}
-		    		break;
-		    	default:
-		    			break;
-		    	}
-		    }
-			
-			// Only add relations that contain entities.
-			if (filteredRelation.getMembers().size() > 0) {
-				sink.process(filteredRelationContainer);
-			}
-			
-    	} else {
-    		sink.process(relationContainer);
-    	}
-	}
-	
-	
-	private boolean selectParentRelationsPass() {
-		ReleasableIterator<RelationContainer> i = allRelations.iterate();
-		
-		try {
-			int selectionCount;
-			
-			selectionCount = 0;
-			
-			while (i.hasNext()) {
-				Relation relation = i.next().getEntity();
-				long relationId = relation.getId();
-				
-				// Ignore relations that have already been selected.
-				if (!availableRelations.get(relationId)) {
-					
-					// This relation becomes an available relation if one of its member
-					// relations is also available.
-					for (RelationMember member : relation.getMembers()) {
-						if (member.getMemberType().equals(EntityType.Relation)) {
-							if (availableRelations.get(member.getMemberId())) {
-								availableRelations.set(relationId);
-								selectionCount++;
-							}
-						}
-					}
-				}
-			}
-			
-			return selectionCount > 0;
-			
-		} finally {
-			i.release();
-		}
-	}
-
-
-	/**
-	 * Walk up the relation tree. This means iterating through relations until all parent relations
-	 * of existing relations are marked in the available list. We may have to do this multiple times
-	 * depending on the nesting level of relations.
-	 */
-	private void selectParentRelations() {
-		boolean selectionsMade;
-		
-		do {
-			selectionsMade = selectParentRelationsPass();
-		} while (selectionsMade);
-	}
-
-
-	/**
-	 * Select all relation members of type relation for existing selected relations. This may need
-	 * to be called several times until all children are selected.
-	 * 
-	 * @return True if additional selections were made an another pass is needed.
-	 */
-	private boolean selectChildRelationsPass() {
-		ReleasableIterator<RelationContainer> i = allRelations.iterate();
-		
-		try {
-			int selectionCount;
-			
-			selectionCount = 0;
-			
-			while (i.hasNext()) {
-				Relation relation = i.next().getEntity();
-				long relationId = relation.getId();
-				
-				// Only examine available relations.
-				if (availableRelations.get(relationId)) {
-					// Select the child if it hasn't already been selected.
-					for (RelationMember member : relation.getMembers()) {
-						if (member.getMemberType().equals(EntityType.Relation)) {
-							long memberId = member.getMemberId();
-							
-							if (!availableRelations.get(memberId)) {
-								availableRelations.set(memberId);
-								selectionCount++;
-							}
-						}
-					}
-				}
-			}
-			
-			return selectionCount > 0;
-			
-		} finally {
-			i.release();
-		}
-	}
-	
-	
-	/**
-	 * Select all relation members of type node or way for existing selected relations.
-	 */
-	private void selectChildNonRelationsPass() {
-		ReleasableIterator<RelationContainer> i = allRelations.iterate();
-		
-		try {
-			while (i.hasNext()) {
-				Relation relation = i.next().getEntity();
-				long relationId = relation.getId();
-				
-				// Only examine available relations.
-				if (availableRelations.get(relationId)) {
-					// Select the member if it hasn't already been selected.
-					for (RelationMember member : relation.getMembers()) {
-						switch (member.getMemberType()) {
-						case Node:
-							availableNodes.set(member.getMemberId());
-							break;
-						case Way:
-							availableWays.set(member.getMemberId());
-							break;
-						default:
-							break;
-						}
-					}
-				}
-			}
-			
-		} finally {
-			i.release();
-		}
-	}
-	
-	
-	/**
-	 * Select all nodes within already selected ways.
-	 */
-	private void selectWayNodes() {
-		ReleasableIterator<WayContainer> i = allWays.iterate();
-		
-		try {
-			while (i.hasNext()) {
-				Way way = i.next().getEntity();
-				long wayId = way.getId();
-				
-				// Only examine available relations.
-				if (availableWays.get(wayId)) {
-					// Select all nodes within the way.
-					for (WayNode wayNode : way.getWayNodes()) {
-						availableNodes.set(wayNode.getNodeId());
-					}
-				}
-			}
-			
-		} finally {
-			i.release();
-		}
-	}
-	
-	
-	private void buildCompleteRelations() {
-		boolean selectionsMade;
-		
-		// Select all child relation members of type relation. 
-		do {
-			selectionsMade = selectChildRelationsPass();
-		} while (selectionsMade);
-		
-		// Select all child relation members of type way or node.
-		selectChildNonRelationsPass();
-		
-		// Select all way nodes of existing nodes.
-		selectWayNodes();
-	}
-    
-    
-    private void pumpNodesToSink() {
-    	ReleasableIterator<NodeContainer> i = allNodes.iterate();
-    	
-    	try {
-    		while (i.hasNext()) {
-				NodeContainer nodeContainer = i.next();
-				if (availableNodes.get(nodeContainer.getEntity().getId())) {
-					emitNode(nodeContainer);
-				}
-			}
-    		
-    	} finally {
-    		i.release();
-    	}
-    }
-    
-    
-    private void pumpWaysToSink() {
-    	ReleasableIterator<WayContainer> i = allWays.iterate();
-    	
-    	try {
-    		while (i.hasNext()) {
-				WayContainer wayContainer = i.next();
-				if (availableWays.get(wayContainer.getEntity().getId())) {
-					emitWay(wayContainer);
-				}
-			}
-    		
-    	} finally {
-    		i.release();
-    	}
-    }
-    
-    
-    private void pumpRelationsToSink() {
-    	ReleasableIterator<RelationContainer> i = allRelations.iterate();
-    	
-    	try {
-    		while (i.hasNext()) {
-				RelationContainer relationContainer = i.next();
-				if (availableRelations.get(relationContainer.getEntity().getId())) {
-					emitRelation(relationContainer);
-				}
-			}
-    		
-    	} finally {
-    		i.release();
-    	}
-    }
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		// If we've stored entities temporarily, we now need to forward the selected ones to the output.
-		if (storeEntities) {
-			// Select all parents of current relations.
-			selectParentRelations();
-			
-			// Merge required ids into available ids.
-			availableNodes.setAll(requiredNodes);
-			availableWays.setAll(requiredWays);
-			availableRelations.setAll(requiredRelations);
-			requiredNodes = null;
-			requiredWays = null;
-			requiredRelations = null;
-			
-			if (completeRelations) {
-				buildCompleteRelations();
-			}
-			
-			// Send the selected entities to the output.
-			pumpNodesToSink();
-			pumpWaysToSink();
-			pumpRelationsToSink();
-		} else if (cascadingRelations) {
-			// Select all parents of current relations.
-			selectParentRelations();
-			availableRelations.setAll(requiredRelations);
-			
-			// nodes, ways, and relations *not* referencing other relations will already have
-            // been written in this mode. we only pump the remaining ones, relations that 
-            // reference other relations. this may result in an un-ordered relation stream.
-			pumpRelationsToSink();
-        }
-		
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		if (allNodes != null) {
-			allNodes.release();
-		}
-		if (allWays != null) {
-			allWays.release();			
-		}
-		if (allRelations != null) {
-			allRelations.release();
-		}
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java b/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java
deleted file mode 100644
index 73c3839..0000000
--- a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.areafilter.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-
-
-/**
- * Extends the basic task manager factory functionality with area filter task
- * specific common methods.
- * 
- * @author Brett Henderson
- */
-public abstract class AreaFilterTaskManagerFactory extends TaskManagerFactory {
-	private static final String ARG_ID_TRACKER_TYPE = "idTrackerType";
-	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.Dynamic;
-	
-	
-	/**
-	 * Utility method for retrieving the login credentials for a database connection.
-	 * 
-	 * @param taskConfig
-	 *            Contains all information required to instantiate and configure
-	 *            the task.
-	 * @return The entity identifier tracker type.
-	 */
-	protected IdTrackerType getIdTrackerType(
-			TaskConfiguration taskConfig) {
-		if (doesArgumentExist(taskConfig, ARG_ID_TRACKER_TYPE)) {
-			String idTrackerType;
-			
-			idTrackerType = getStringArgument(taskConfig, ARG_ID_TRACKER_TYPE);
-			
-			try {
-				return IdTrackerType.valueOf(idTrackerType);
-			} catch (IllegalArgumentException e) {
-				throw new OsmosisRuntimeException(
-					"Argument " + ARG_ID_TRACKER_TYPE + " for task " + taskConfig.getId()
-					+ " must contain a valid id tracker type.", e);
-			}
-			
-		} else {
-			return DEFAULT_ID_TRACKER_TYPE;
-		}
-	}
-}
diff --git a/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java b/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java
deleted file mode 100644
index 0b555fa..0000000
--- a/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.areafilter.v0_6;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-
-/**
- * @author Karl Newman
- * 
- */
-public class BoundingBoxFilterTest {
-
-	private SinkEntityInspector entityInspector;
-	private AreaFilter simpleAreaFilter;
-	private Bound intersectingBound;
-	private Bound nonIntersectingBound;
-	private Node inAreaNode;
-	private Node outOfAreaNode;
-	private Node edgeNodeEast;
-	private Node edgeNodeWest;
-	private Node edgeNodeNorth;
-	private Node edgeNodeSouth;
-
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		OsmUser user;
-		List<Tag> tags;
-		
-		user = new OsmUser(12, "OsmosisTest");
-		
-		// All nodes have an empty tags list.
-		tags = new ArrayList<Tag>();
-		
-		entityInspector = new SinkEntityInspector();
-		// simpleAreaFilter doesn't cross antimeridian; no complete ways or relations
-		simpleAreaFilter = new BoundingBoxFilter(
-		        IdTrackerType.IdList,
-		        -20,
-		        20,
-		        20,
-		        -20,
-		        false,
-		        false,
-		        false,
-		        false);
-		simpleAreaFilter.setSink(entityInspector);
-		intersectingBound = new Bound(30, 10, 30, 10, "intersecting");
-		nonIntersectingBound = new Bound(-30, -50, 10, -10, "nonintersecting");
-		inAreaNode = new Node(new CommonEntityData(1234, 0, new Date(), user, 0, tags), 10, 10);
-		outOfAreaNode = new Node(new CommonEntityData(1235, 0, new Date(), user, 0, tags), 30, 30);
-		edgeNodeEast = new Node(new CommonEntityData(1236, 0, new Date(), user, 0, tags), 10, 20);
-		edgeNodeWest = new Node(new CommonEntityData(1237, 0, new Date(), user, 0, tags), 10, -20);
-		edgeNodeNorth = new Node(new CommonEntityData(1238, 0, new Date(), user, 0, tags), 20, 10);
-		edgeNodeSouth = new Node(new CommonEntityData(1239, 0, new Date(), user, 0, tags), -20, 10);
-	}
-
-
-	/**
-	 * Performs post-test activities.
-	 */
-	@After
-	public void tearDown() {
-		simpleAreaFilter.release();
-	}
-
-
-	/**
-	 * Test passing a Bound which intersects the filter area.
-	 */
-	@Test
-	public final void testProcessBoundContainer1() {
-		Bound compareBound;
-		simpleAreaFilter.process(new BoundContainer(intersectingBound));
-		simpleAreaFilter.complete();
-		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
-		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getLeft(), 10) == 0);
-		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getBottom(), 10) == 0);
-		assertTrue(compareBound.getOrigin().equals("intersecting"));
-	}
-
-
-	/**
-	 * Test the non-passing of a Bound which does not intersect the filter area.
-	 */
-	@Test
-	public final void testProcessBoundContainer2() {
-		simpleAreaFilter.process(new BoundContainer(nonIntersectingBound));
-		simpleAreaFilter.complete();
-		assertNull(entityInspector.getLastEntityContainer());
-	}
-
-
-	/**
-	 * Test a node inside the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea1() {
-		assertTrue(
-		        "Node lying inside filter area not considered inside area",
-		        simpleAreaFilter.isNodeWithinArea(inAreaNode));
-	}
-
-
-	/**
-	 * Test a node outside the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea2() {
-		assertFalse(
-		        "Node lying outside filter area not considered outside area",
-		        simpleAreaFilter.isNodeWithinArea(outOfAreaNode));
-	}
-
-
-	/**
-	 * Test a node on the East edge of the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea3() {
-		assertTrue(
-		        "Node lying on East edge of filter area not considered inside area",
-		        simpleAreaFilter.isNodeWithinArea(edgeNodeEast));
-	}
-
-
-	/**
-	 * Test a node on the West edge of the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea4() {
-		assertTrue(
-		        "Node lying on West edge of filter area not considered inside area",
-		        simpleAreaFilter.isNodeWithinArea(edgeNodeWest));
-	}
-
-
-	/**
-	 * Test a node on the North edge of the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea5() {
-		assertTrue(
-		        "Node lying on North edge of filter area not considered inside area",
-		        simpleAreaFilter.isNodeWithinArea(edgeNodeNorth));
-	}
-
-
-	/**
-	 * Test a node on the South edge of the area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea6() {
-		assertTrue(
-		        "Node lying on South edge of filter area not considered inside area",
-		        simpleAreaFilter.isNodeWithinArea(edgeNodeSouth));
-	}
-}
diff --git a/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java b/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java
deleted file mode 100644
index 79c9712..0000000
--- a/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.areafilter.v0_6;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-
-/**
- * Tests the polygon area filter implementation.
- * 
- * @author Karl Newman
- */
-public class PolygonFilterTest {
-
-	private File polygonFile;
-	private SinkEntityInspector entityInspector;
-	private AreaFilter polyAreaFilter;
-	private Bound intersectingBound;
-	private Bound crossingIntersectingBound;
-	private Bound nonIntersectingBound;
-	private Node inAreaNode;
-	private Node outOfAreaNode;
-	private Node edgeNode;
-
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		OsmUser user;
-		List<Tag> tags;
-		
-		user = new OsmUser(12, "OsmosisTest");
-		
-		// All nodes have an empty tags list.
-		tags = new ArrayList<Tag>();
-		
-		polygonFile = new File(getClass().getResource("testPolygon.txt").getFile());
-		entityInspector = new SinkEntityInspector();
-		// polyAreaFilter has a notch out of the Northeast corner.
-		polyAreaFilter = new PolygonFilter(IdTrackerType.IdList, polygonFile, false, false, false, false);
-		polyAreaFilter.setSink(entityInspector);
-		intersectingBound = new Bound(30, 0, 30, 0, "intersecting");
-		crossingIntersectingBound = new Bound(-10, 10, 30, -30, "crossing intersecting");
-		nonIntersectingBound = new Bound(30, 15, 30, 15, "nonintersecting");
-		inAreaNode = new Node(new CommonEntityData(1234, 0, new Date(), user, 0, tags), 5, 10);
-		outOfAreaNode = new Node(new CommonEntityData(1235, 0, new Date(), user, 0, tags), 15, 15);
-		edgeNode = new Node(new CommonEntityData(1236, 0, new Date(), user, 0, tags), 15, 10);
-	}
-
-
-	/**
-	 * Performs post-test activities.
-	 */
-	@After
-	public void tearDown() {
-		polyAreaFilter.release();
-	}
-
-
-	/**
-	 * Test passing a Bound which intersects the filter area.
-	 */
-	@Test
-	public final void testProcessBoundContainer1() {
-		Bound compareBound;
-		polyAreaFilter.process(new BoundContainer(intersectingBound));
-		polyAreaFilter.complete();
-		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
-		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getLeft(), 0) == 0);
-		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getBottom(), 0) == 0);
-		assertTrue(compareBound.getOrigin().equals("intersecting"));
-	}
-
-
-	/**
-	 * Test passing a Bound which crosses the antimeredian and intersects the filter area.
-	 */
-	@Test
-	public final void testProcessBoundContainer2() {
-		Bound compareBound;
-		polyAreaFilter.process(new BoundContainer(crossingIntersectingBound));
-		polyAreaFilter.complete();
-		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
-		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getLeft(), -20) == 0);
-		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
-		assertTrue(Double.compare(compareBound.getBottom(), -20) == 0);
-		assertTrue(compareBound.getOrigin().equals("crossing intersecting"));
-	}
-
-
-	/**
-	 * Test the non-passing of a Bound which does not intersect the filter area.
-	 */
-	@Test
-	public final void testProcessBoundContainer3() {
-		polyAreaFilter.process(new BoundContainer(nonIntersectingBound));
-		polyAreaFilter.complete();
-		assertNull(entityInspector.getLastEntityContainer());
-	}
-
-
-	/**
-	 * Test a Node that falls inside the filter area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea1() {
-		assertTrue(
-		        "Node lying inside filter area not considered inside area.",
-		        polyAreaFilter.isNodeWithinArea(inAreaNode));
-	}
-
-
-	/**
-	 * Test a Node that falls outside the filter area (inside the notched-out area of the polygon).
-	 */
-	@Test
-	public final void testIsNodeWithinArea2() {
-		assertFalse(
-		        "Node lying outside filter area not considered outside area.",
-		        polyAreaFilter.isNodeWithinArea(outOfAreaNode));
-	}
-
-
-	/**
-	 * Test a Node that falls on the edge of the filter area.
-	 */
-	@Test
-	public final void testIsNodeWithinArea3() {
-		assertFalse(
-		        "Node lying on edge of filter area not considered inside area.",
-		        polyAreaFilter.isNodeWithinArea(edgeNode));
-	}
-}
diff --git a/areafilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java b/areafilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
deleted file mode 100644
index 737d24e..0000000
--- a/areafilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.test.task.v0_6;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-/**
- * Mock object for inspecting the resulting entities after passing through a pipeline task.
- * 
- * @author Karl Newman
- */
-public class SinkEntityInspector implements Sink {
-
-	private List<EntityContainer> processedEntities;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SinkEntityInspector() {
-		processedEntities = new LinkedList<EntityContainer>();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Catch all passed entities and save them for later inspection.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		processedEntities.add(entityContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Shortcut method if you only care about the most recent EntityContainer.
-	 * 
-	 * @return the lastEntityContainer
-	 */
-	public EntityContainer getLastEntityContainer() {
-		if (processedEntities.isEmpty()) {
-			return null;
-		} else {
-			return processedEntities.get(processedEntities.size() - 1);
-		}
-	}
-
-
-	/**
-	 * Retrieve an Iterable of all the processed EntityContainers.
-	 * 
-	 * @return the processedEntities
-	 */
-	public Iterable<EntityContainer> getProcessedEntities() {
-		return Collections.unmodifiableList(processedEntities);
-	}
-
-}
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm
deleted file mode 100644
index e710085..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode1"/>
-  </node>
-  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="name" v="outAreaWay1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="5" role="relationrole"/>
-    <tag k="name" v="inAreaRelation1"/>
-    <tag k="comment" v="this relation is pulled in by relation 5"/>
-  </relation>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="2" role="wayrole"/>
-    <tag k="name" v="outAreaRelation4"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm
deleted file mode 100644
index 1fb7702..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-10.00000,-10.00000,10.00000,10.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="5" role="relationrole"/>
-    <tag k="comment" v="this relation is pulled in by relation 5"/>
-    <tag k="name" v="inAreaRelation1"/>
-  </relation>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm
deleted file mode 100644
index 6de9e37..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-10.00000,-10.00000,10.00000,10.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm
deleted file mode 100644
index e81894b..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-10.00000,-10.00000,10.00000,10.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode1"/>
-  </node>
-  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="name" v="outAreaWay1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="5" role="relationrole"/>
-    <tag k="comment" v="this relation is pulled in by relation 5"/>
-    <tag k="name" v="inAreaRelation1"/>
-  </relation>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="2" role="wayrole"/>
-    <tag k="name" v="outAreaRelation4"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm
deleted file mode 100644
index c896314..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-10.00000,-10.00000,10.00000,10.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode1"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="5" role="relationrole"/>
-    <tag k="comment" v="this relation is pulled in by relation 5"/>
-    <tag k="name" v="inAreaRelation1"/>
-  </relation>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm
deleted file mode 100644
index a96b15e..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-10.00000,-10.00000,10.00000,10.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm b/areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm
deleted file mode 100644
index 109d99c..0000000
--- a/areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode1"/>
-  </node>
-  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
-    <tag k="name" v="inAreaNode2"/>
-  </node>
-  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode1"/>
-  </node>
-  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
-    <tag k="name" v="outAreaNode2"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="name" v="inAreaWay1"/>
-  </way>
-  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="name" v="outAreaWay1"/>
-  </way>
-  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="1" role="noderole"/>
-    <tag k="name" v="inAreaRelation2"/>
-  </relation>
-  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="1" role="wayrole"/>
-    <tag k="name" v="inAreaRelation3"/>
-  </relation>
-  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="way" ref="2" role="wayrole"/>
-    <tag k="name" v="outAreaRelation4"/>
-  </relation>
-  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="relation" ref="2" role="relationrole"/>
-    <member type="relation" ref="4" role="relationrole"/>
-    <tag k="name" v="inAreaRelation5"/>
-  </relation>
-</osm>
diff --git a/build-support/.gitignore b/build-support/.gitignore
index cd23f75..c60b49a 100644
--- a/build-support/.gitignore
+++ b/build-support/.gitignore
@@ -1 +1,3 @@
+.project
 /ivy
+
diff --git a/build-support/.project b/build-support/.project
deleted file mode 100644
index 22e9b55..0000000
--- a/build-support/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-BuildSupport</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-	</buildSpec>
-	<natures>
-	</natures>
-</projectDescription>
diff --git a/build-support/checkstyle.xml b/build-support/checkstyle.xml
index e77c3a5..2a68901 100644
--- a/build-support/checkstyle.xml
+++ b/build-support/checkstyle.xml
@@ -7,7 +7,9 @@
 
     <!-- Checks whether files end with a new line.                        -->
     <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
-    <module name="NewlineAtEndOfFile"/>
+    <module name="NewlineAtEndOfFile">
+        <property name="fileExtensions" value="java"/>
+    </module>
 
     <!-- Checks that property files contain the same keys.         -->
     <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
@@ -23,6 +25,7 @@
         <property
              name="header"
              value="// This software is released into the Public Domain.  See copying.txt for details."/>
+        <property name="fileExtensions" value="java"/>
     </module>
 
 
@@ -111,7 +114,6 @@
         <!-- Checks for common coding problems               -->
         <!-- See http://checkstyle.sf.net/config_coding.html -->
         <module name="AvoidInlineConditionals"/>
-        <module name="DoubleCheckedLocking"/>    <!-- MY FAVOURITE -->
         <module name="EmptyStatement"/>
         <module name="EqualsHashCode"/>
         <module name="HiddenField">
@@ -124,7 +126,10 @@
         <!-- Disabled due to large number of tests using hard-coded numbers.
         <module name="MagicNumber"/>-->
         <module name="MissingSwitchDefault"/>
-        <module name="RedundantThrows"/>
+        <!-- Disabled due to gradle not appearing to include project
+        dependencies on classpath, thus check fails if an exception is outside
+        the project.
+        <module name="RedundantThrows"/>-->
         <module name="SimplifyBooleanExpression"/>
         <module name="SimplifyBooleanReturn"/>
 
diff --git a/build-support/config/ant-build-common.properties b/build-support/config/ant-build-common.properties
deleted file mode 100644
index 3925d9c..0000000
--- a/build-support/config/ant-build-common.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# Contains common properties that don't vary between users.
-
-# 3rd Party Library Versions
-dependency.version.checkstyle=5.4
-dependency.version.classworlds=2.4
-dependency.version.commons-codec=1.5
-dependency.version.commons-compress=1.2
-dependency.version.commons-dbcp=1.4
-dependency.version.osmpbf=1.1.1-754a33af
-dependency.version.ivy=2.2.0
-dependency.version.jpf=1.5
-dependency.version.junit=4.10
-dependency.version.mysql=5.1.18
-dependency.version.postgis=1.3.3
-dependency.version.postgresql=9.0-801.jdbc4
-dependency.version.protobuf=2.4.1
-dependency.version.spring=3.0.6.RELEASE
-dependency.version.woodstox-core=4.1.2
-dependency.version.woodstox-stax2=3.1.1
-dependency.version.xerces=2.9.1
diff --git a/build-support/config/ant-build.properties b/build-support/config/ant-build.properties
deleted file mode 100644
index 3eedf44..0000000
--- a/build-support/config/ant-build.properties
+++ /dev/null
@@ -1 +0,0 @@
-# User specific build settings are placed in here.
diff --git a/build-support/create_changes_xml.pl b/build-support/create_changes_xml.pl
deleted file mode 100755
index a399581..0000000
--- a/build-support/create_changes_xml.pl
+++ /dev/null
@@ -1,37 +0,0 @@
-#! /usr/bin/perl -w
-
-print "<document xmlns=\"http://maven.apache.org/changes/1.0.0\"\n";
-print "          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
-print "          xsi:schemaLocation=\"http://maven.apache.org/changes/1.0.0 http://maven.apache.org/xsd/changes-1.0.0.xsd\">\n";
-
-print "<body>\n";
-
-while (<STDIN>)
-{
-    chomp;
-
-    if (/^$/)
-    {
-    }
-    elsif (/^0\.\d/)
-    {
-	# Hack for now...
-	print "</release>\n" if (!($_ eq "0.32.1"));
-	print "<release version=\"" . $_ . "\" >\n";
-    }
-    else
-    {
-	$action = "add";
-	$action = "fix"    if (/^Fixed/);
-	$action = "update" if (/^Removed/);
-	$action = "update" if (/^Switch/);
-	$action = "update" if (/^Use/);
-
-	print "<action type=\"" . $action . "\">" . $_ . "</action>\n";
-    }
-}
-
-print "</body>\n";
-
-print "</document>\n";
-
diff --git a/build-support/hudson_post_build.sh b/build-support/hudson_post_build.sh
deleted file mode 100755
index 6087d0d..0000000
--- a/build-support/hudson_post_build.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#! /bin/sh -ex
-
-export TARGET=/osm/osmosis-continuous-integration/web
-
-# remove '-maven' from the name
-JOB_NAME=$(echo ${JOB_NAME} | sed -e 's|-maven||')
-
-if [ -d ${WORKSPACE}/trunk/target/site ]
-then
-
-	if [ -d ${TARGET}/${JOB_NAME} ]
-	then
-		rm -rf ${TARGET}/${JOB_NAME}
-	fi
-
-	cp -ar ${WORKSPACE}/trunk/target/site ${TARGET}/${JOB_NAME}
-
-fi
-
-# Clean up residual data
-/bin/rm /tmp/test*.osc
-/bin/rm /tmp/test*.osm
-/bin/rm /tmp/test*.osm.gz
diff --git a/build-support/hudson_pre_build.sh b/build-support/hudson_pre_build.sh
deleted file mode 100755
index 2ea39d7..0000000
--- a/build-support/hudson_pre_build.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/sh -ex
-
-mkdir ${WORKSPACE}/trunk/src/changes
-
-${WORKSPACE}/trunk/build_support/create_changes_xml.pl < ${WORKSPACE}/trunk/changes.txt > ${WORKSPACE}/trunk/src/changes/changes.xml
diff --git a/build-support/script/build-init.xml b/build-support/script/build-init.xml
deleted file mode 100644
index dae3a30..0000000
--- a/build-support/script/build-init.xml
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Build.Init">
-
-	<description>
-		Initialises the ant environment.
-	</description>
-	
-	<target name="init-time">
-		<!-- Create the time stamp -->
-		<tstamp/>
-	</target>
-	
-	<target name="init-build">
-		<!-- Create the build directory. -->
-		<mkdir dir="build"/>
-	</target>
-	
-	<target name="init-properties">
-		<!-- Load properties file defining configuration settings for the build. -->
-		<property name="build.properties" location="${build-support.dir}/config/ant-build.properties"/>
-		<property name="build-common.properties" location="${build-support.dir}/config/ant-build-common.properties"/>
-		<loadproperties srcFile="${build.properties}"/>
-		<loadproperties srcFile="${build-common.properties}"/>
-	</target>
-	
-	<target name="init-check-for-scm">
-		<property name="checkout.basedir" location="${basedir}/.."/>
-		<echo message="checkout.basedir=${checkout.basedir}"/>
-		<echo message="checkout.basedir .git=${checkout.basedir}/.git"/>
-
-		<available file="${checkout.basedir}/.git" property="checkout.git"/>
-		<available file="${checkout.basedir}/.svn" property="checkout.svn"/>
-		<echo message="checkout.git=${checkout.git}"/>
-		<echo message="checkout.svn=${checkout.svn}"/>
-	</target>
-	
-	<target name="init-version-svn" if="checkout.svn">
-		<!-- Determine the SVN revision number. -->
-		<exec executable="svnversion" output="build/version.txt" failonerror="true">
-			<arg value="-n"/>
-			<arg value="${checkout.basedir}"/>
-		</exec>
-		<replace file="build/version.txt" token=":" value="-"/>
-		<loadfile property="svn.version" srcfile="build/version.txt"/>
-
-		<!--
-			Define the project version.
-			It is assumed to be a development snapshot version.
-			Release versions will be overridden explicitly.
-		-->
-		<property name="project.version" value="SNAPSHOT-r${svn.version}"/>
-	</target>
-	
-	<target name="init-version-git" if="checkout.git">
-		<condition property="git.command" value="git.cmd">
-			<os family="windows" />
-		</condition>
-		<condition property="git.command" value="git">
-			<not><os family="windows" /></not>
-		</condition>
-
-		<!-- Determine the GIT revision. -->
-		<exec executable="${git.command}" output="build/version.txt" failonerror="true">
-			<arg value="describe"/>
-			<arg value="--tags"/>
-			<arg value="--always"/>
-			<arg value="--dirty"/>
-		</exec>
-		<replaceregexp file="build/version.txt" match="\s+" replace=""/>
-		<loadfile property="git.version" srcfile="build/version.txt"/>
-
-		<!--
-			Define the project version.
-		-->
-		<property name="project.version" value="${git.version}"/>
-	</target>
-	
-	<target name="init-version-default">
-		<property name="project.version" value="SNAPSHOT-unknown"/>
-	</target>
-	
-	<target name="init-version" depends="init-check-for-scm, init-version-svn, init-version-git, init-version-default">
-	</target>
-
-	<target name="init" depends="init-time, init-build, init-properties, init-version" description="Perform initialisation required by all other build steps.">
-		<echo message="Project Version: ${project.version}"/>
-	</target>
-</project>
diff --git a/build-support/script/build-ivy-base.xml b/build-support/script/build-ivy-base.xml
deleted file mode 100644
index 578d53e..0000000
--- a/build-support/script/build-ivy-base.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Build.Ivy.Base" default="init-ivy" basedir="."
-	xmlns:ivy="antlib:org.apache.ivy.ant">
-
-	<description>
-		Installs and configures the ivy build dependencies system.
-	</description>
-	
-	<!-- Include common build components. -->
-	<property name="build-support.dir" location="../"/>
-	<import file="${build-support.dir}/script/build-init.xml"/>
-	
-	<target name="ivy-availability" description="Checks if the ivy library is available">
-		<property name="ivy.version" value="${dependency.version.ivy}" />
-		<property name="ivy.jar.dir" value="${build-support.dir}/ivy" />
-		<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-${ivy.version}.jar" />
-		
-		<!-- Determine if the ivy jar is already available. -->
-		<available property="ivy.available" file="${ivy.jar.file}" />
-	</target>
-
-	<target name="download-ivy" unless="ivy.available" description="Downloads the ivy library from public repositories.">
-		<!-- Delete any existing ivy files -->
-		<delete dir="${ivy.jar.dir}"/>
-		
-		<mkdir dir="${ivy.jar.dir}" />
-		
-		<!--
-			Download Ivy from web site so that it can be used even without any
-			special installation
-		-->
-		<get
-			src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.version}/ivy-${ivy.version}.jar"
-			dest="${ivy.jar.file}" usetimestamp="true"/>
-	</target>
-
-	<target name="init-ivy" depends="init, ivy-availability, download-ivy" description="Registers ivy with ant and initializes it." unless="ivy.initialized">
-		<!--
-			Try to load ivy in case the user has not already
-			dropped it into ant's lib dir (note that the latter copy will always
-			take precedence). We will not fail as long as local lib dir exists
-			(it may be empty) and ivy is in at least one of ant's lib dir or the
-			local lib dir.
-		-->
-		<path id="ivy.lib.path">
-			<fileset dir="${ivy.jar.dir}" includes="*.jar" />
-		</path>
-		<taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant"
-			classpathref="ivy.lib.path" />
-		
-		<!-- Override the shared repo location to point at the svn-based ivy repo. -->
-		<property name="ivy.shared.default.root" location="${build-support.dir}/repo"/>
-		<ivy:configure />
-		
-		<!-- Retrieve ivy details from the config file. -->
-		<ivy:info />
-		
-		<!-- Set the project name based on the ivy name. -->
-		<property name="project.name" value="${ivy.module}"/>
-		<echo message="****************************************"/>
-		<echo message="Project: ${project.name}"/>
-		<echo message="****************************************"/>
-		<sleep seconds="1"/>
-		
-		<property name="ivy.initialized" value="true"/>
-	</target>
-	
-	<target name="clean-cache" depends="init-ivy" description="Clean the ivy cache.">
-		<ivy:cleancache />
-	</target>
-	
-	<target name="clean-ivy" description="Clean the ivy installation.">
-		<delete dir="${ivy.jar.dir}"/>
-	</target>
-</project>
diff --git a/build-support/script/build-ivy.xml b/build-support/script/build-ivy.xml
deleted file mode 100644
index 50e748c..0000000
--- a/build-support/script/build-ivy.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Build.Ivy" default="init-ivy" basedir="."
-	xmlns:ivy="antlib:org.apache.ivy.ant">
-
-	<description>
-		Extends the basic ivy functionality with re-usable tasks for all projects.
-	</description>
-	
-	<!-- Include common build components. -->
-	<property name="build-support.dir" location="../"/>
-	<import file="${build-support.dir}/script/build-ivy-base.xml"/>
-
-	<target name="resolve" depends="init-ivy" description="Downloads all program dependencies using ivy.">
-		<ivy:resolve file="${ivy.dep.file}" conf="${ivy.configurations}" />
-		
-		<ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact]-[revision].[ext]" sync="true" conf="${ivy.configurations}" symlink="true" />
-		
-		<ivy:report todir="report/ivy"/>
-	</target>
-	
-	<target name="publish" depends="resolve, build">
-		<ivy:deliver pubrevision="${project.version}"/>
-		<ivy:publish resolver="local" pubrevision="${project.version}" overwrite="true"/>
-	</target>
-</project>
diff --git a/build-support/script/build-java.xml b/build-support/script/build-java.xml
deleted file mode 100644
index 0c23b8f..0000000
--- a/build-support/script/build-java.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Build.Java" default="init-ivy" basedir="."
-	xmlns:ivy="antlib:org.apache.ivy.ant"
-	xmlns:cs="antlib:com.puppycrawl.tools.checkstyle">
-	
-	<description>
-		Performs a build for a standard java project.
-	</description>
-	
-	<!-- Include common build components. -->
-	<property name="build-support.dir" location="../"/>
-	<import file="${build-support.dir}/script/build-ivy.xml"/>
-	
-	<!-- Define classpaths for each of the lib directories. -->
-	<path id="classpath.compile">
-		<fileset dir="lib/compile" includes="**/*.jar"/>
-	</path>
-	<path id="classpath.default">
-		<fileset dir="lib/default" includes="**/*.jar"/>
-	</path>
-	<path id="classpath.test">
-		<fileset dir="lib/test" includes="**/*.jar"/>
-	</path>
-	
-	<target name="build_src" depends="resolve" description="Compile source code into class files.">
-		<!-- Create the build directory structure. -->
-		<mkdir dir="build"/>
-		<mkdir dir="build/src"/>
-		<!-- Compile the java code into build/src -->
-		<javac srcdir="src/main/java" destdir="build/src" debug="on" debuglevel="lines,vars,source" classpathref="classpath.compile" includeantruntime="false"/>
-		<!-- Copy all resource files into build/src -->
-		<copy todir="build/src">
-			<fileset dir="src/main/resources" erroronmissingdir="false"/>
-		</copy>
-		<!-- Put a version number file in the build/src directory. -->
-		<touch file="build/src/version-${project.version}"/>
-	</target>
-	
-	<target name="build_test" depends="build_src" description="Compile test source code into class files.">
-		<!-- Create the build directory structure. -->
-		<mkdir dir="build/test"/>
-		
-		<!-- Compile the java test code with the main project classes as a dependency. -->
-		<mkdir dir="src/test/java"/>
-		<javac srcdir="src/test/java" destdir="build/test" debug="on" debuglevel="lines,vars,source" includeantruntime="false">
-			<classpath>
-				<path refid="classpath.test"/>
-				<path location="build/src"/>
-			</classpath>
-		</javac>
-		<!-- Copy all resource files from the test directory into the build test directory. -->
-		<copy todir="build/test">
-			<fileset dir="src/test/resources" erroronmissingdir="false"/>
-		</copy>
-	</target>
-	
-	<target name="_build" depends="build_src" description="Generates the binaries for the distribution.">
-		<!-- Create the binary directory -->
-		<mkdir dir="build/binary"/>
-		
-		<!-- Create a manifest for the jar file. -->
-		<manifest file="build/binary/jar.txt">
-			<attribute name="Main-Class" value="org.openstreetmap.osmosis.core.Osmosis"/>
-			<attribute name="Built-By" value="${user.name}"/>
-			<attribute name="Implementation-Title" value="Osmosis Library"/>
-			<attribute name="Implementation-Version" value="${project.version} (${TODAY})"/> 
-			<attribute name="Implementation-Vendor" value="Brett Henderson"/>
-		</manifest>
-		
-		<!-- Create the jar archive. -->
-		<jar
-			destfile="build/binary/${project.name}.jar"
-			basedir="build/src"
-			manifest="build/binary/jar.txt"/>
-		
-		<!-- Copy the jar to the distrib directory where it will be found by ivy. -->
-		<mkdir dir="distrib"/>
-		<copy
-			file="build/binary/${project.name}.jar"
-			tofile="distrib/jars/${project.name}-${project.version}.jar"/>
-	</target>
-	
-	<!-- Produces javadoc output from the source code. -->
-	<target name="javadoc" depends="init" description="Products javadoc documentation from the source code.">
-		<javadoc packagenames="*" sourcepath="src" destdir="doc/api" classpathref="classpath.compile"/>
-	</target>
-	
-	<!-- Performs checkstyle analysis of all source files in the project. -->
-	<target name="checkstyle">
-		<taskdef uri="antlib:com.puppycrawl.tools.checkstyle" resource="checkstyletask.properties" classpathref="classpath.test" />
-		
-		<mkdir dir="report"/>
-		<cs:checkstyle config="${build-support.dir}/checkstyle.xml">
-			<fileset dir="src" includes="**/*.java"/>
-			<fileset dir="test" includes="**/*.java" erroronmissingdir="false"/>
-			<formatter type="plain"/>
-			<formatter type="xml" toFile="report/checkstyle.xml"/>
-		</cs:checkstyle>
-	</target>
-	
-	<!-- Runs all of the unit tests in the application. -->
-	<target name="test" depends="build_test" description="Run automated test cases.">
-		<mkdir dir="report/test"/>
-		
-		<!-- Determine the location of the database authorisation file.  This can be overridden outside the build if necessary. -->
-		<property name="db.apidb.authfile" location="src/test/resources/data/template/v0_6/apidb-authfile.txt"/>
-		<property name="db.pgsql.authfile" location="src/test/resources/data/template/v0_6/pgsql-authfile.txt"/>
-		
-		<junit fork="no" maxmemory="512m" printsummary="on" haltonerror="off" haltonfailure="off" filtertrace="on" failureproperty="test.failure" includeantruntime="false">
-			<formatter type="plain" usefile="true"/>
-			<formatter type="xml" usefile="true"/>
-			<classpath>
-				<path refid="classpath.test"/>
-				<path location="build/src"/>
-				<path location="build/test"/>
-			</classpath>
-			<sysproperty key="db.apidb.authfile" value="${db.apidb.authfile}"/>
-			<sysproperty key="db.pgsql.authfile" value="${db.pgsql.authfile}"/>
-			<batchtest todir="report/test">
-				<fileset dir="build/test">
-					<include name="**/*Test*.class"/>
-					<exclude name="**/*TestSuite*.class"/>
-					<exclude name="**/*$*.class"/>
-				</fileset>
-			</batchtest>
-		</junit>
-
-		<fail message="One or more junit tests failed." if="test.failure" />
-	</target>
-	
-	<target name="_clean">
-		<!-- Delete ivy resolved libraries. -->
-		<delete dir="lib"/>
-		<!-- Delete the generated directory trees. -->
-		<delete dir="build"/>
-		<delete dir="report"/>
-		<delete dir="distrib"/>
-		<!-- Delete the doc/api directory tree. -->
-		<delete dir="doc/api"/>
-	</target>
-	
-	<!-- Public Targets -->
-	<target name="all" depends="build, checkstyle, test, publish" description="Executes all major build targets."/>
-	<target name="clean" depends="_clean" description="Clean up the project tree."/>
-	<target name="build" depends="_build" description="Builds the main project target."/>
-</project>
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..6a06f69
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,131 @@
+task wrapper(type: Wrapper) {
+	gradleVersion = '1.4'
+}
+
+
+/* Build collections containing each type of project.  These collections will
+ * be used to apply common configurations to projects of the same type.
+ */
+def packageProjects = allprojects.findAll { project -> project.path.equals(':package') }
+def buildProjects = allprojects.findAll { project -> project.path.equals(':build-support') }
+// Java projects are all those that aren't in the previous collections.
+def javaProjects = subprojects.findAll { project -> !packageProjects.contains(project) && !buildProjects.contains(project) }
+
+// Apply common project configuration
+subprojects {
+	apply plugin: 'eclipse-wtp'
+
+	// All projects use a common group id.
+	group = 'org.openstreetmap.osmosis'
+
+	// Load the project version dynamically from Git.  For release builds, don't add a suffix.
+	def versionSuffix = "RELEASE".equals(osmosisBuildType) ? '' : '-' + osmosisBuildType
+	version = 'git describe --always --dirty'.execute().in.text.trim() + versionSuffix
+
+	// Enable access to artefact dependency repositories.
+	repositories {
+		// Standard Maven repository.
+		mavenCentral()
+	}
+}
+
+// Apply common configurations to all projects supporting Java.
+configure(javaProjects) {
+	apply plugin: 'checkstyle'
+	apply plugin: 'java'
+	apply plugin: 'jdepend'
+	apply plugin: 'maven'
+	apply plugin: 'signing'
+
+	sourceCompatibility = 1.6
+
+	test {
+		/*
+		 * Pass on each of our custom properties to the unit tests if they have
+		 * been provided.
+		 */
+		['db.apidb.authfile', 'db.pgsql.authfile'].each {
+			propName ->
+				if (System.getProperties().containsKey(propName)) {
+					jvmArgs '-D' + propName + '=' + System.getProperty(propName)
+				}
+		}
+		//testLogging.showStandardStreams = true
+	}
+
+	dependencies {
+		testCompile group: 'junit', name: 'junit', version: dependencyVersionJunit
+	}
+
+	checkstyle {
+		configFile = new File(rootDir, 'build-support/checkstyle.xml')
+		configProperties.samedir = configFile.parentFile
+	}
+
+	// Build javadoc and source jars and include in published artifacts.
+	task javadocJar(type: Jar, dependsOn: javadoc) {
+		classifier = 'javadoc'
+		from 'build/docs/javadoc'
+	}
+	task sourcesJar(type: Jar) {
+		from sourceSets.main.allSource
+		classifier = 'sources'
+	}
+	artifacts {
+	    archives jar
+	
+	    archives javadocJar
+	    archives sourcesJar
+	}
+
+	// Sign all published artifacts if signing is enabled.
+	signing {
+		sign configurations.archives
+		required = Boolean.valueOf(osmosisSigningEnabled)
+	}
+
+	// Configure the maven plugin to upload artefacts to the Sonatype repository.
+	uploadArchives {
+		repositories {
+			mavenDeployer {
+				beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+
+				// We upload to the Sonatype SNAPSHOT repository unless it is a release build in
+				// which case we upload to the staging repository.
+				def sonatypeRepoUrl = "RELEASE".equals(osmosisBuildType) ?
+					'https://oss.sonatype.org/service/local/staging/deploy/maven2/' :
+					'https://oss.sonatype.org/content/repositories/snapshots/'
+				repository(url: sonatypeRepoUrl) {
+					authentication(userName: sonatypeUsername, password: sonatypePassword)
+				}
+
+				pom.project {
+					name project.name
+					packaging 'jar'
+					description 'Osmosis is a Java application and library for processing OSM data.'
+					url 'http://wiki.openstreetmap.org/wiki/Osmosis'
+
+					scm {
+						url 'https://github.com/openstreetmap/osmosis'
+						connection 'scm:git:git://github.com/openstreetmap/osmosis.git'
+						developerConnection 'scm:git:ssh://git@github.com/openstreetmap/osmosis.git'
+					}
+
+					licenses {
+						license {
+							name 'Public Domain'
+						}
+					}
+
+					developers {
+						developer {
+							id 'brett'
+							name 'Brett Henderson'
+							email 'brett at bretth.com'
+						}
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 564e782..0000000
--- a/build.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis" default="all" basedir="."
-	xmlns:ivy="antlib:org.apache.ivy.ant">
-
-	<property name="build-support.dir" location="build-support"/>
-	<import file="${build-support.dir}/script/build-ivy-base.xml"/>
-	
-	<target name="find-projects" depends="init-ivy" description="Locate all of the sub-projects.">
-		<ivy:buildlist reference="build-path">
-			<fileset dir=".">
-				<include name="**/build.xml"/>
-				<exclude name="build.xml"/>
-			</fileset>
-		</ivy:buildlist>
-	</target>
-	
-	<target name="clean" depends="find-projects">
-		<subant target="clean" buildpathref="build-path" />
-	</target>
-	
-	<target name="publish" depends="find-projects">
-		<subant target="publish" buildpathref="build-path" />
-	</target>
-	
-	<target name="test" depends="find-projects">
-		<subant target="test" buildpathref="build-path" />
-	</target>
-	
-	<target name="all" depends="find-projects">
-		<subant target="all" buildpathref="build-path" />
-	</target>
-</project>
diff --git a/core/.checkstyle b/core/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/core/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/core/.classpath b/core/.classpath
deleted file mode 100644
index 4211949..0000000
--- a/core/.classpath
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/core/.gitignore b/core/.gitignore
deleted file mode 100644
index e8e0cdb..0000000
--- a/core/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
-/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java
-/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml
diff --git a/core/.project b/core/.project
deleted file mode 100644
index 3609c1c..0000000
--- a/core/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Core</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/core/build.xml b/core/build.xml
deleted file mode 100644
index 583f78d..0000000
--- a/core/build.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Core" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-	
-	<!-- Override the normal build to perform some pre-compilation steps. -->
-	<target name="build" depends="init, _update_version, _build"/>
-	
-	<target name="_update_version" description="Modifies some source code files to include the project version.">
-		<!-- Update the version number in the main class. -->
-		<copy file="src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java.template"
-			tofile="src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java"/>
-		<replaceregexp
-			byline="true"
-			file="src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java"
-			match="String VERSION = "(.*)""
-			replace="String VERSION = "${project.version}""
-		/>
-		<!-- Update the version number in the plugin descriptor. -->
-		<copy file="src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml.template"
-			tofile="src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml"/>
-		<replaceregexp
-			byline="true"
-			file="src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml"
-			match="id="org.openstreetmap.osmosis.core.plugin.Core" version="(.*)""
-			replace="id="org.openstreetmap.osmosis.core.plugin.Core" version="${project.version}""
-		/>
-	</target>
-</project>
diff --git a/core/ivy.xml b/core/ivy.xml
deleted file mode 100644
index ac298d8..0000000
--- a/core/ivy.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-core"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-        <dependency org="net.sf.jpf" name="jpf" rev="${dependency.version.jpf}" conf="compile->default"/>
-        <dependency org="org.codehaus.woodstox" name="woodstox-core-lgpl" rev="${dependency.version.woodstox-core}" conf="compile->default">
-			<!-- Stax is included in the JDK from java 1.6 onwards. -->
-        	<exclude module="stax-api"/>
-        </dependency>
-        <!-- Include this explicitly to increase the version number.  The default transitively included one has incorrect sha-1 checksums. -->
-        <dependency org="org.codehaus.woodstox" name="stax2-api" rev="${dependency.version.woodstox-stax2}" conf="compile->default">
-			<!-- Stax is included in the JDK from java 1.6 onwards. -->
-        	<exclude module="stax-api"/>
-        </dependency>
-        <dependency org="org.apache.commons" name="commons-compress" rev="${dependency.version.commons-compress}" conf="compile->default"/>
-        <dependency org="xerces" name="xercesImpl" rev="${dependency.version.xerces}" conf="compile->default">
-        	<!-- All necessary APIs are included in the JDK. -->
-        	<exclude module="xml-apis"/>
-        </dependency>
-        
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/core/pom.xml b/core/pom.xml
deleted file mode 100644
index 607d9fe..0000000
--- a/core/pom.xml
+++ /dev/null
@@ -1,417 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-	<modelVersion>4.0.0</modelVersion>
-
-	<groupId>org.openstreetmap.osmosis</groupId>
-	<artifactId>osmosis</artifactId>
-	<version>0.36-SNAPSHOT</version>
-
-	<packaging>jar</packaging>
-
-	<name>Osmosis</name>
-	<description>Osmosis is an application to create and handle differential data updates from the OpenStreetMap project</description>
-	<url>http://wiki.openstreetmap.org/wiki/Osmosis</url>
-	<inceptionYear>2007</inceptionYear>
-	<!-- First commit 28/09/2007, http://trac.openstreetmap.org/changeset/4742 -->
-
-	<organization>
-		<name>OpenStreetMap</name>
-		<url>http://www.openstreetmap.org/</url>
-	</organization>
-
-	<licenses>
-		<!--
-			TODO find applicable licence, "copying.txt" places it into public
-			domain
-		-->
-	</licenses>
-
-	<developers>
-		<!-- TODO full list of developers, if applicable -->
-	</developers>
-
-	<properties>
-		<!-- project defaults -->
-		<java.version>1.6</java.version>
-		<java.version.javadoc>http://java.sun.com/javase/6/docs/api/</java.version.javadoc>
-		<file.encoding>UTF-8</file.encoding>
-		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-
-		<spring.version>2.5.6</spring.version>
-
-	</properties>
-
-	<!-- contributors -->
-	<contributors>
-	</contributors>
-
-	<issueManagement>
-		<system>Trac</system>
-		<url>http://trac.openstreetmap.org/query?status=new&status=assigned&status=reopened&component=osmosis&order=priority</url>
-	</issueManagement>
-
-	<scm>
-		<url>http://trac.openstreetmap.org/browser/applications/utils/osmosis/trunk</url>
-		<connection>scm:svn:http://svn.openstreetmap.org/applications/utils/osmosis/trunk</connection>
-		<developerConnection>scm:svn:http://svn.openstreetmap.org/applications/utils/osmosis/trunk</developerConnection>
-	</scm>
-
-	<ciManagement>
-		<system>Hudson</system>
-		<url>http://dev.openstreetmap.de:23457/hudson/</url>
-	</ciManagement>
-
-	<!-- Mailing Lists -->
-	<mailingLists>
-		<mailingList>
-			<name>Developer List</name>
-			<post>osmosis-dev at openstreetmap.org</post>
-			<archive>http://lists.openstreetmap.org/pipermail/osmosis-dev/</archive>
-		</mailingList>
-	</mailingLists>
-
-	<!-- Global Build Settings -->
-	<build>
-		<sourceDirectory>src</sourceDirectory>
-
-		<testSourceDirectory>test</testSourceDirectory>
-
-		<testResources>
-			<testResource>
-				<directory>test/data/template</directory>
-				<targetPath>data/input</targetPath>
-			</testResource>
-		</testResources>
-
-		<pluginManagement>
-			<plugins>
-			</plugins>
-		</pluginManagement>
-
-		<plugins>
-
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-eclipse-plugin</artifactId>
-				<configuration>
-					<addVersionToProjectName>true</addVersionToProjectName>
-					<downloadSources>true</downloadSources>
-					<downloadJavadocs>true</downloadJavadocs>
-					<useProjectReferences>true</useProjectReferences>
-					<m2eclipse>true</m2eclipse>
-					<classpathContainer>org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER</classpathContainer>
-					<additionalProjectnatures>
-						<projectnature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</projectnature>
-					</additionalProjectnatures>
-					<additionalBuildcommands>
-						<buildcommand>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</buildcommand>
-					</additionalBuildcommands>
-					<ajdtVersion>none</ajdtVersion>
-				</configuration>
-			</plugin>
-
-			<plugin>
-				<!-- copy plugin.xml manually until the directory layout is fixed -->
-				<artifactId>maven-antrun-plugin</artifactId>
-				<executions>
-					<execution>
-						<phase>process-sources</phase>
-						<goals>
-							<goal>run</goal>
-						</goals>
-						<configuration>
-							<tasks>
-								<copy file="src/org/openstreetmap/osmosis/core/plugin/plugin.xml"
-									tofile="target/classes/org/openstreetmap/osmosis/core/plugin/plugin.xml" />
-							</tasks>
-						</configuration>
-					</execution>
-				</executions>
-			</plugin>
-
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-compiler-plugin</artifactId>
-				<configuration>
-					<encoding>${project.build.sourceEncoding}</encoding>
-					<source>${java.version}</source>
-					<target>${java.version}</target>
-				</configuration>
-			</plugin>
-
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-source-plugin</artifactId>
-				<executions>
-					<execution>
-						<id>attach-sources</id>
-						<phase>verify</phase>
-						<goals>
-							<goal>jar</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-javadoc-plugin</artifactId>
-				<executions>
-					<execution>
-						<id>attach-javadocs</id>
-						<phase>verify</phase>
-						<goals>
-							<goal>jar</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-checkstyle-plugin</artifactId>
-				<version>2.3</version>
-			</plugin>
-
-			<plugin>
-				<artifactId>maven-surefire-plugin</artifactId>
-				<configuration>
-					<systemProperties>
-						<property>
-							<name>db.apidb.authfile</name>
-							<value>${db.apidb.authfile}</value>
-						</property>
-						<property>
-							<name>db.pgsql.authfile</name>
-							<value>${db.pgsql.authfile}</value>
-						</property>
-				  	</systemProperties>
-				</configuration>
-			</plugin>
-
-			<plugin>
-				<artifactId>maven-assembly-plugin</artifactId>
-				<executions>
-					<execution>
-						<id>distribution</id>
-						<phase>package</phase>
-						<goals>
-							<goal>single</goal>
-						</goals>
-						<configuration>
-							<descriptors>
-								<descriptorRef>src/assembly/distribution.xml</descriptorRef>
-							</descriptors>
-						</configuration>
-					</execution>
-				</executions>
-			</plugin>
-
-		</plugins>
-
-	</build>
-
-	<dependencies>
-
-		<!-- scope COMPILE -->
-		<dependency>
-			<groupId>commons-beanutils</groupId>
-			<artifactId>commons-beanutils-core</artifactId>
-			<version>1.8.2</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-codec</groupId>
-			<artifactId>commons-codec</artifactId>
-			<version>1.4</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-cli</groupId>
-			<artifactId>commons-cli</artifactId>
-			<version>1.2</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-collections</groupId>
-			<artifactId>commons-collections</artifactId>
-			<version>2.1.1</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.apache.commons</groupId>
-			<artifactId>commons-compress</artifactId>
-			<version>1.0</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-dbcp</groupId>
-			<artifactId>commons-dbcp</artifactId>
-			<version>1.2.2</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-pool</groupId>
-			<artifactId>commons-pool</artifactId>
-			<version>1.5.4</version>
-		</dependency>
-
-		<dependency>
-			<groupId>commons-logging</groupId>
-			<artifactId>commons-logging</artifactId>
-			<version>1.1.1</version>
-		</dependency>
-
-		<dependency>
-			<groupId>net.sf.jpf</groupId>
-			<artifactId>jpf</artifactId>
-			<version>1.5</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.codehaus.plexus</groupId>
-			<artifactId>plexus-classworlds</artifactId>
-			<version>2.2.2</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.codehaus.woodstox</groupId>
-			<artifactId>stax2-api</artifactId>
-			<version>3.0.1</version>
-		</dependency>
-
-		<dependency>
-			<groupId>com.google.collections</groupId>
-			<artifactId>google-collections</artifactId>
-			<version>1.0-rc4</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.postgis</groupId>
-			<artifactId>postgis-jdbc</artifactId>
-			<version>1.3.3</version>
-			<!-- TODO fix postgis pom to exclude stubs -->
-			<exclusions>
-				<exclusion>
-					<groupId>org.postgis</groupId>
-					<artifactId>postgis-stubs</artifactId>
-				</exclusion>
-			</exclusions>
-		</dependency>
-
-		<!-- JDBC Drivers -->
-		<dependency>
-			<groupId>mysql</groupId>
-			<artifactId>mysql-connector-java</artifactId>
-			<version>5.1.10</version>
-		</dependency>
-
-		<dependency>
-			<groupId>postgresql</groupId>
-			<artifactId>postgresql</artifactId>
-			<version>8.3-603.jdbc4</version>
-		</dependency>
-
-		<!-- Springy Stuff -->
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-beans</artifactId>
-			<version>${spring.version}</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-context</artifactId>
-			<version>${spring.version}</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-core</artifactId>
-			<version>${spring.version}</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-jdbc</artifactId>
-			<version>${spring.version}</version>
-		</dependency>
-
-		<dependency>
-			<groupId>org.springframework</groupId>
-			<artifactId>spring-tx</artifactId>
-			<version>${spring.version}</version>
-		</dependency>
-
-		<!-- scope TEST -->
-		<dependency>
-			<groupId>junit</groupId>
-			<artifactId>junit</artifactId>
-			<version>4.4</version>
-			<scope>test</scope>
-		</dependency>
-
-	</dependencies>
-
-	<!-- Global Reporting Settings -->
-	<reporting>
-		<plugins>
-			<plugin>
-				<artifactId>maven-javadoc-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<artifactId>maven-dependency-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<artifactId>maven-project-info-reports-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<artifactId>maven-surefire-report-plugin</artifactId>
-			</plugin>
-			<!--
-			<plugin>
-				<artifactId>maven-changes-plugin</artifactId>
-			</plugin>
-			-->
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>dashboard-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>findbugs-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>javancss-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>jdepend-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>jxr-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>taglist-maven-plugin</artifactId>
-			</plugin>
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>versions-maven-plugin</artifactId>
-			</plugin>
-		</plugins>
-	</reporting>
-
-	<distributionManagement>
-		<repository>
-			<id>sonatype-openstreetmap-snapshots</id>
-			<name>Sonatype OpenStreetMap Snapshots Repo</name>
-			<url>http://oss.sonatype.org/content/repositories/openstreetmap-snapshots</url>
-		</repository>
-	</distributionManagement>
-
-</project>
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java b/core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java
deleted file mode 100644
index 35eb37c..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.bound.v0_6.BoundComputerFactory;
-import org.openstreetmap.osmosis.core.bound.v0_6.BoundSetterFactory;
-import org.openstreetmap.osmosis.core.buffer.v0_6.ChangeBufferFactory;
-import org.openstreetmap.osmosis.core.buffer.v0_6.EntityBufferFactory;
-import org.openstreetmap.osmosis.core.misc.v0_6.EmptyChangeReaderFactory;
-import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReaderFactory;
-import org.openstreetmap.osmosis.core.misc.v0_6.NullChangeWriterFactory;
-import org.openstreetmap.osmosis.core.misc.v0_6.NullWriterFactory;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.plugin.PluginLoader;
-import org.openstreetmap.osmosis.core.progress.v0_6.ChangeProgressLoggerFactory;
-import org.openstreetmap.osmosis.core.progress.v0_6.EntityProgressLoggerFactory;
-import org.openstreetmap.osmosis.core.report.v0_6.EntityReporterFactory;
-import org.openstreetmap.osmosis.core.report.v0_6.IntegrityReporterFactory;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForSeekableApplierComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorterFactory;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeTagSorterFactory;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntitySorterFactory;
-import org.openstreetmap.osmosis.core.sort.v0_6.TagSorterFactory;
-import org.openstreetmap.osmosis.core.tee.v0_6.ChangeTeeFactory;
-import org.openstreetmap.osmosis.core.tee.v0_6.EntityTeeFactory;
-
-
-/**
- * The plugin loader for the core tasks.
- * 
- * @author Brett Henderson
- */
-public class CorePluginLoader implements PluginLoader {
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Map<String, TaskManagerFactory> loadTaskFactories() {
-		Map<String, TaskManagerFactory> factoryMap;
-		EntitySorterFactory entitySorterFactory06;
-		ChangeSorterFactory changeSorterFactory06;
-
-		factoryMap = new HashMap<String, TaskManagerFactory>();
-
-		// Configure factories that require additional information.
-		entitySorterFactory06 = new EntitySorterFactory();
-		entitySorterFactory06.registerComparator("TypeThenId", new EntityContainerComparator(
-				new EntityByTypeThenIdComparator()), true);
-		changeSorterFactory06 = new ChangeSorterFactory();
-		changeSorterFactory06.registerComparator("streamable", new ChangeForStreamableApplierComparator(), true);
-		changeSorterFactory06.registerComparator("seekable", new ChangeForSeekableApplierComparator(), false);
-
-		// Register factories.
-		factoryMap.put("sort", entitySorterFactory06);
-		factoryMap.put("s", entitySorterFactory06);
-		factoryMap.put("sort-change", changeSorterFactory06);
-		factoryMap.put("sc", changeSorterFactory06);
-		factoryMap.put("write-null", new NullWriterFactory());
-		factoryMap.put("wn", new NullWriterFactory());
-		factoryMap.put("write-null-change", new NullChangeWriterFactory());
-		factoryMap.put("wnc", new NullChangeWriterFactory());
-		factoryMap.put("buffer", new EntityBufferFactory());
-		factoryMap.put("b", new EntityBufferFactory());
-		factoryMap.put("buffer-change", new ChangeBufferFactory());
-		factoryMap.put("bc", new ChangeBufferFactory());
-		factoryMap.put("report-entity", new EntityReporterFactory());
-		factoryMap.put("re", new EntityReporterFactory());
-		factoryMap.put("report-integrity", new IntegrityReporterFactory());
-		factoryMap.put("ri", new IntegrityReporterFactory());
-		factoryMap.put("log-progress", new EntityProgressLoggerFactory());
-		factoryMap.put("lp", new EntityProgressLoggerFactory());
-		factoryMap.put("log-progress-change", new ChangeProgressLoggerFactory());
-		factoryMap.put("lpc", new ChangeProgressLoggerFactory());
-		factoryMap.put("tee", new EntityTeeFactory());
-		factoryMap.put("t", new EntityTeeFactory());
-		factoryMap.put("tee-change", new ChangeTeeFactory());
-		factoryMap.put("tc", new ChangeTeeFactory());
-		factoryMap.put("read-empty", new EmptyReaderFactory());
-		factoryMap.put("re", new EmptyReaderFactory());
-		factoryMap.put("read-empty-change", new EmptyChangeReaderFactory());
-		factoryMap.put("rec", new EmptyChangeReaderFactory());
-
-		factoryMap.put("compute-bounding-box", new BoundComputerFactory());
-		factoryMap.put("cbb", new BoundComputerFactory());
-		factoryMap.put("set-bounding-box", new BoundSetterFactory());
-		factoryMap.put("sbb", new BoundSetterFactory());
-
-		factoryMap.put("sort-0.6", entitySorterFactory06);
-		factoryMap.put("sort-change-0.6", changeSorterFactory06);
-		factoryMap.put("write-null-0.6", new NullWriterFactory());
-		factoryMap.put("write-null-change-0.6", new NullChangeWriterFactory());
-		factoryMap.put("buffer-0.6", new EntityBufferFactory());
-		factoryMap.put("buffer-change-0.6", new ChangeBufferFactory());
-		factoryMap.put("report-entity-0.6", new EntityReporterFactory());
-		factoryMap.put("report-integrity-0.6", new IntegrityReporterFactory());
-		factoryMap.put("log-progress-0.6", new EntityProgressLoggerFactory());
-		factoryMap.put("log-progress-change-0.6", new ChangeProgressLoggerFactory());
-		factoryMap.put("tee-0.6", new EntityTeeFactory());
-		factoryMap.put("tee-change-0.6", new ChangeTeeFactory());
-		factoryMap.put("read-empty-0.6", new EmptyReaderFactory());
-		factoryMap.put("read-empty-change-0.6", new EmptyChangeReaderFactory());
-		factoryMap.put("tag-sort-0.6", new TagSorterFactory());
-		factoryMap.put("tag-sort-change-0.6", new ChangeTagSorterFactory());
-
-		factoryMap.put("compute-bounding-box-0.6", new BoundComputerFactory());
-		factoryMap.put("set-bounding-box-0.6", new BoundSetterFactory());
-		
-		return factoryMap;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java b/core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java
deleted file mode 100644
index bf324de..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java
+++ /dev/null
@@ -1,140 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core; 
-
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.cli.CommandLineParser;
-import org.openstreetmap.osmosis.core.pipeline.common.Pipeline;
-
-
-/**
- * The main entry point for the command line application.
- * 
- * @author Brett Henderson
- */
-public final class Osmosis {
-	private static final Logger LOG = Logger.getLogger(Osmosis.class.getName());
-	
-	
-	/**
-	 * This class cannot be instantiated.
-	 */
-	private Osmosis() {
-	}
-	
-	
-	/**
-	 * The entry point to the application.
-	 * 
-	 * @param args
-	 *            The command line arguments.
-	 */
-	public static void main(String[] args) {
-		try {
-			run(args);
-			
-			System.exit(0);
-			
-		} catch (Throwable t) {
-			LOG.log(Level.SEVERE, "Execution aborted.", t);
-		}
-		
-		System.exit(1);
-	}
-	
-	
-	/**
-	 * This contains the real functionality of the main method. It is kept
-	 * separate to allow the application to be invoked within other applications
-	 * without a System.exit being called.
-	 * <p>
-	 * Typically an application shouldn't directly invoke this method, it should
-	 * instantiate its own pipeline.
-	 * 
-	 * @param args
-	 *            The command line arguments.
-	 */
-	public static void run(String[] args) {
-		CommandLineParser commandLineParser;
-		TaskRegistrar taskRegistrar;
-		Pipeline pipeline;
-		long startTime;
-		long finishTime;
-		
-		startTime = System.currentTimeMillis();
-		
-		configureLoggingConsole();
-		
-		commandLineParser = new CommandLineParser();
-		
-		// Parse the command line arguments into a consumable form.
-		commandLineParser.parse(args);
-		
-		// Configure the new logging level.
-		configureLoggingLevel(commandLineParser.getLogLevel());
-		
-		LOG.info("Osmosis Version " + OsmosisConstants.VERSION);
-		taskRegistrar = new TaskRegistrar();
-		taskRegistrar.initialize(commandLineParser.getPlugins());
-		
-		pipeline = new Pipeline(taskRegistrar.getFactoryRegister());
-		
-		LOG.info("Preparing pipeline.");
-		pipeline.prepare(commandLineParser.getTaskInfoList());
-		
-		LOG.info("Launching pipeline execution.");
-		pipeline.execute();
-		
-		LOG.info("Pipeline executing, waiting for completion.");
-		pipeline.waitForCompletion();
-		
-		LOG.info("Pipeline complete.");
-		
-		finishTime = System.currentTimeMillis();
-		
-		LOG.info("Total execution time: " + (finishTime - startTime) + " milliseconds.");
-	}
-	
-	
-	/**
-	 * Configures logging to write all output to the console.
-	 */
-	private static void configureLoggingConsole() {
-		Logger rootLogger;
-		Handler consoleHandler;
-		
-		rootLogger = Logger.getLogger("");
-		
-		// Remove any existing handlers.
-		for (Handler handler : rootLogger.getHandlers()) {
-			rootLogger.removeHandler(handler);
-		}
-		
-		// Add a new console handler.
-		consoleHandler = new ConsoleHandler();
-		consoleHandler.setLevel(Level.ALL);
-		rootLogger.addHandler(consoleHandler);
-	}
-	
-	
-	/**
-	 * Configures the logging level.
-	 * 
-	 * @param level
-	 *            The new logging level to apply.
-	 */
-	private static void configureLoggingLevel(Level level) {
-		Logger rootLogger;
-		
-		rootLogger = Logger.getLogger("");
-		
-		// Set the required logging level.
-		rootLogger.setLevel(level);
-		
-		// Set the JPF logger to one level lower.
-		Logger.getLogger("org.java.plugin").setLevel(Level.WARNING);
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java b/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java
deleted file mode 100644
index c2cb799..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.bound.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.GenericObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Computes the minimal bounding box of an entity stream.
- * 
- * A bound entity is emitted iff there are nodes on the input stream.
- * 
- * Upstream bound entities are never passed through. If there is a bound entity
- * on the input stream, it is overwritten if there are any nodes or removed if
- * there are no nodes on the input stream.
- * 
- * This implementation caches all objects of the input stream in a simple object
- * store.
- * 
- * @author Igor Podolskiy
- */
-public class BoundComputer implements SinkSource, EntityProcessor {
-
-	private Sink sink;
-	private SimpleObjectStore<EntityContainer> objects;
-	private double top;
-	private double bottom;
-	private double left;
-	private double right;
-	private boolean nodesSeen;
-	private String origin;
-
-
-	/**
-	 * Creates a new bounding box computer instance.
-	 * 
-	 * @param origin
-	 *            The origin for the bound to set.
-	 */
-
-	public BoundComputer(String origin) {
-		objects = new SimpleObjectStore<EntityContainer>(new GenericObjectSerializationFactory(), "cbbo", true);
-		bottom = 0;
-		top = 0;
-		left = 0;
-		right = 0;
-		nodesSeen = false;
-		this.origin = origin;
-	}
-
-
-	@Override
-	public void process(EntityContainer entityContainer) {
-		entityContainer.process(this);
-	}
-
-
-	@Override
-	public void complete() {
-		objects.complete();
-
-		if (nodesSeen) {
-			sink.process(new BoundContainer(new Bound(right, left, top, bottom, this.origin)));
-		}
-
-		ReleasableIterator<EntityContainer> iter = null;
-
-		try {
-			iter = objects.iterate();
-
-			while (iter.hasNext()) {
-				sink.process(iter.next());
-			}
-		} finally {
-			if (iter != null) {
-				iter.release();
-			}
-		}
-
-		sink.complete();
-	}
-
-
-	@Override
-	public void release() {
-		sink.release();
-		objects.release();
-	}
-
-
-	@Override
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-
-
-	@Override
-	public void process(BoundContainer bound) {
-		// Do nothing, we'll generate a new bound later on
-	}
-
-
-	@Override
-	public void process(NodeContainer nodeContainer) {
-		Node node = nodeContainer.getEntity();
-
-		if (nodesSeen) {
-			left = Math.min(left, node.getLongitude());
-			right = Math.max(right, node.getLongitude());
-
-			bottom = Math.min(bottom, node.getLatitude());
-			top = Math.max(top, node.getLatitude());
-		} else {
-			left = node.getLongitude();
-			right = node.getLongitude();
-			top = node.getLatitude();
-			bottom = node.getLatitude();
-			nodesSeen = true;
-		}
-
-		objects.add(nodeContainer);
-	}
-
-
-	@Override
-	public void process(WayContainer way) {
-		objects.add(way);
-	}
-
-
-	@Override
-	public void process(RelationContainer relation) {
-		objects.add(relation);
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java b/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java
deleted file mode 100644
index 4328a5b..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.bound.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Ensures the bound entity in the output stream exists with a specific value or
- * is not present.
- * 
- * @author Igor Podolskiy
- */
-public class BoundSetter implements SinkSource {
-
-	private Sink sink;
-	private boolean boundProcessed;
-	private Bound newBound;
-
-
-	/**
-	 * Creates a new instance of the bound setter.
-	 * 
-	 * @param newBound
-	 *            the new bound to set, or <pre>null</pre> to remove the bound
-	 */
-	public BoundSetter(Bound newBound) {
-		this.newBound = newBound;
-		this.boundProcessed = false;
-	}
-
-
-	@Override
-	public void process(EntityContainer entityContainer) {
-		if (boundProcessed) {
-			sink.process(entityContainer);
-		} else {
-			// processFirstEntity will send all data downstream as needed
-			processFirstEntity(entityContainer);
-			boundProcessed = true;
-		}
-	}
-
-
-	private void processFirstEntity(EntityContainer entityContainer) {
-		if (entityContainer.getEntity().getType() == EntityType.Bound) {
-			if (newBound == null) {
-				// Just returning won't pass the entity downstream
-				return;
-			} else {
-				sink.process(new BoundContainer(newBound));
-			}
-		} else {
-			if (newBound != null) {
-				sink.process(new BoundContainer(newBound));
-			}
-			sink.process(entityContainer);
-		}
-	}
-
-
-	@Override
-	public void complete() {
-		sink.complete();
-	}
-
-
-	@Override
-	public void release() {
-		sink.release();
-	}
-
-
-	@Override
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java b/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java
deleted file mode 100644
index 82ac7a1..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.buffer.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkRunnableChangeSource;
-
-
-/**
- * Splits the pipeline so that it can be processed on multiple threads. The
- * input thread to this task stores data in a buffer which blocks if it fills
- * up. This task runs on a new thread which reads data from the buffer and
- * writes it to the destination.
- * 
- * @author Brett Henderson
- */
-public class ChangeBuffer implements ChangeSinkRunnableChangeSource {
-	private ChangeSink changeSink;
-	private DataPostbox<ChangeContainer> buffer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param bufferCapacity
-	 *            The size of the buffer to use.
-	 */
-	public ChangeBuffer(int bufferCapacity) {
-		buffer = new DataPostbox<ChangeContainer>(bufferCapacity);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		buffer.put(changeContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		buffer.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		buffer.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * Sends all input data to the sink.
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			while (buffer.hasNext()) {
-				changeSink.process(buffer.getNext());
-			}
-			
-			changeSink.complete();
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				buffer.setOutputError();
-			}
-			
-			changeSink.release();
-		}
-	}
-	
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java b/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java
deleted file mode 100644
index f707d83..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.buffer.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkRunnableSource;
-
-
-/**
- * Splits the pipeline so that it can be processed on multiple threads. The
- * input thread to this task stores data in a buffer which blocks if it fills
- * up. This task runs on a new thread which reads data from the buffer and
- * writes it to the destination.
- * 
- * @author Brett Henderson
- */
-public class EntityBuffer implements SinkRunnableSource {
-	private Sink sink;
-	private DataPostbox<EntityContainer> buffer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param bufferCapacity
-	 *            The size of the buffer to use.
-	 */
-	public EntityBuffer(int bufferCapacity) {
-		buffer = new DataPostbox<EntityContainer>(bufferCapacity);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		buffer.put(entityContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		buffer.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		buffer.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * Sends all input data to the sink.
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			while (buffer.hasNext()) {
-				sink.process(buffer.getNext());
-			}
-			
-			sink.complete();
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				buffer.setOutputError();
-			}
-			
-			sink.release();
-		}
-	}
-	
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java b/core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java
deleted file mode 100644
index 0995c0b..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java
+++ /dev/null
@@ -1,408 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.cli;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.pipeline.common.PipelineConstants;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-
-
-/**
- * Parses command line arguments into a form that can be consumed by the rest of
- * the application.
- * 
- * @author Brett Henderson
- */
-public class CommandLineParser {
-	
-	private static final String GLOBAL_ARGUMENT_PREFIX = "-";
-	private static final String TASK_ARGUMENT_PREFIX = "--";
-	private static final String OPTION_QUIET_SHORT = "q";
-	private static final String OPTION_QUIET_LONG = "quiet";
-	private static final String OPTION_VERBOSE_SHORT = "v";
-	private static final String OPTION_VERBOSE_LONG = "verbose";
-	private static final String OPTION_PLUGIN_SHORT = "p";
-	private static final String OPTION_PLUGIN_LONG = "plugin";
-	
-	
-	/**
-	 * Defines the log levels supported from the command line.
-	 */
-	private static final Level [] LOG_LEVELS = {
-		Level.OFF,
-		Level.SEVERE,
-		Level.WARNING,
-		Level.INFO,
-		Level.FINE,
-		Level.FINER,
-		Level.FINEST
-	};
-	
-	
-	/**
-	 * The index into the LOG_LEVELS array for the default log level.
-	 */
-	private static final int DEFAULT_LOG_LEVEL_INDEX = 3;
-	
-	
-	private List<TaskConfiguration> taskConfigList;
-	private int quietValue;
-	private int verboseValue;
-	private List<String> plugins;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public CommandLineParser() {
-		taskConfigList = new ArrayList<TaskConfiguration>();
-		
-		quietValue = 0;
-		verboseValue = 0;
-		plugins = new ArrayList<String>();
-	}
-	
-	
-	/**
-	 * Parses the command line arguments.
-	 * 
-	 * @param programArgs
-	 *            The arguments.
-	 */
-	public void parse(String [] programArgs) {
-		List<GlobalOptionConfiguration> globalOptions;
-		
-		// Create the global options list.
-		globalOptions = new ArrayList<GlobalOptionConfiguration>();
-		
-		// Process the command line arguments to build all nodes in the pipeline.
-		for (int i = 0; i < programArgs.length;) {
-			String arg;
-			
-			arg = programArgs[i];
-			
-			if (arg.indexOf(TASK_ARGUMENT_PREFIX) == 0) {
-				i = parseTask(programArgs, i);
-			} else if (arg.indexOf(GLOBAL_ARGUMENT_PREFIX) == 0) {
-				i = parseGlobalOption(globalOptions, programArgs, i);
-			} else {
-				throw new OsmosisRuntimeException("Expected argument " + (i + 1) + " to be an option or task name.");
-			}
-		}
-		
-		// Process the global options.
-		for (GlobalOptionConfiguration globalOption : globalOptions) {
-			if (isArgumentForOption(OPTION_QUIET_SHORT, OPTION_QUIET_LONG, globalOption.name)) {
-				quietValue = parseOptionIntegerWithDefault(globalOption, 0) + 1;
-			} else if (isArgumentForOption(OPTION_VERBOSE_SHORT, OPTION_VERBOSE_LONG, globalOption.name)) {
-				verboseValue = parseOptionIntegerWithDefault(globalOption, 0) + 1;
-			} else if (isArgumentForOption(OPTION_PLUGIN_SHORT, OPTION_PLUGIN_LONG, globalOption.name)) {
-				plugins.add(parseOptionString(globalOption));
-			} else {
-				throw new OsmosisRuntimeException(
-						"Argument " + (globalOption.offset + 1) + " specifies an unrecognised option.");
-			}
-		}
-	}
-	
-	
-	/**
-	 * Checks if the current command line argument is for the specified option.
-	 * 
-	 * @param shortOptionName
-	 *            The short name of the option to check for.
-	 * @param longOptionName
-	 *            The long name of the option to check for.
-	 * @param argument
-	 *            The command line argument without the option prefix.
-	 * @return True if the argument is for the specified option.
-	 */
-	private boolean isArgumentForOption(String shortOptionName, String longOptionName, String argument) {
-		return shortOptionName.equals(argument) || longOptionName.equals(argument);
-	}
-	
-	
-	/**
-	 * Determines if an argument is a parameter to the current option/task or
-	 * the start of another option/task.
-	 * 
-	 * @param argument
-	 *            The argument.
-	 * @return True if this is an option parameter.
-	 */
-	private boolean isOptionParameter(String argument) {
-		if (argument.length() >= GLOBAL_ARGUMENT_PREFIX.length()) {
-			if (argument.substring(0, GLOBAL_ARGUMENT_PREFIX.length()).equals(GLOBAL_ARGUMENT_PREFIX)) {
-				return false;
-			}
-		}
-		
-		if (argument.length() >= TASK_ARGUMENT_PREFIX.length()) {
-			if (argument.substring(0, TASK_ARGUMENT_PREFIX.length()).equals(TASK_ARGUMENT_PREFIX)) {
-				return false;
-			}
-		}
-		
-		return true;
-	}
-	
-	
-	/**
-	 * Parses a command line option into an integer. If none is specified, zero
-	 * will be returned.
-	 * 
-	 * @param globalOption
-	 *            The global option to be parsed.
-	 * @return The integer value.
-	 */
-	private int parseOptionIntegerWithDefault(GlobalOptionConfiguration globalOption, int defaultValue) {
-		int result;
-		
-		// If no parameters are available, we use the default value.
-		if (globalOption.parameters.size() <= 0) {
-			return defaultValue;
-		}
-		
-		// An integer option may only have one parameter.
-		if (globalOption.parameters.size() > 1) {
-			throw new OsmosisRuntimeException(
-					"Expected argument " + (globalOption.offset + 1) + " to have no more than one parameter.");
-		}
-		
-		// Parse the option.
-		try {
-			result = Integer.parseInt(globalOption.parameters.get(0));
-			
-		} catch (NumberFormatException e) {
-			throw new OsmosisRuntimeException(
-					"Expected argument " + (globalOption.offset + 2) + " to contain an integer value.");
-		}
-		
-		return result;
-	}
-	
-	
-	/**
-	 * Parses a command line option into an string.
-	 * 
-	 * @param globalOption
-	 *            The global option to be parsed.
-	 */
-	private String parseOptionString(GlobalOptionConfiguration globalOption) {
-		// A string option must have one parameter.
-		if (globalOption.parameters.size() != 1) {
-			throw new OsmosisRuntimeException(
-					"Expected argument " + (globalOption.offset + 1) + " to have one parameter.");
-		}
-		
-		return globalOption.parameters.get(0);
-	}
-	
-	
-	/**
-	 * Parses the details of a single option.
-	 * 
-	 * @param globalOptions
-	 *            The list of global options being parsed, the new option will
-	 *            be stored into this object.
-	 * @param programArgs
-	 *            The command line arguments passed to this application.
-	 * @param offset
-	 *            The current offset through the command line arguments.
-	 * @return The new offset through the command line arguments.
-	 */
-	private int parseGlobalOption(List<GlobalOptionConfiguration> globalOptions, String [] programArgs, int offset) {
-		int i;
-		String argument;
-		GlobalOptionConfiguration globalOption;
-		
-		i = offset;
-		argument = programArgs[i++].substring(1);
-		
-		globalOption = new GlobalOptionConfiguration();
-		globalOption.name = argument;
-		globalOption.offset = offset;
-		
-		// Loop until the next option or task is reached and add the arguments as parameters to the option.
-		while ((i < programArgs.length) && isOptionParameter(programArgs[i])) {
-			globalOption.parameters.add(programArgs[i++]);
-		}
-		
-		// Add the fully populated global option object to the list.
-		globalOptions.add(globalOption);
-		
-		return i;
-	}
-	
-	
-	/**
-	 * Parses the details of a single task and creates a task information object.
-	 * 
-	 * @param programArgs
-	 *            The command line arguments passed to this application.
-	 * @param offset
-	 *            The current offset through the command line arguments.
-	 * @return The new offset through the command line arguments.
-	 */
-	private int parseTask(String [] programArgs, int offset) {
-		int i;
-		String taskType;
-		Map<String, String> taskArgs;
-		Map<String, String> pipeArgs;
-		String taskId;
-		int defaultArgIndex;
-		String defaultArg;
-		
-		i = offset;
-		
-		// Extract the task type from the current argument.
-		taskType = programArgs[i++].substring(TASK_ARGUMENT_PREFIX.length());
-		
-		// Build up a list of task and pipe arguments.
-		taskArgs = new HashMap<String, String>();
-		pipeArgs = new HashMap<String, String>();
-		defaultArg = null;
-		defaultArgIndex = -1;
-		while (i < programArgs.length) {
-			String arg;
-			int equalsIndex;
-			String argName;
-			String argValue;
-			
-			arg = programArgs[i];
-			
-			if (arg.indexOf(TASK_ARGUMENT_PREFIX) == 0) {
-				break;
-			}
-			
-			equalsIndex = arg.indexOf("=");
-			
-			// If an equals sign exists this is a named argument, otherwise it is a default argument.
-			if (equalsIndex >= 0) {
-				// Check if the name component of the argument exists.
-				if (equalsIndex == 0) {
-					throw new OsmosisRuntimeException(
-							"Argument " + (i + 1) + " doesn't contain a name before the '=' (ie. name=value).");
-				}
-				
-				// Check if the value component of the argument exists.
-				if (equalsIndex >= (arg.length() - 1)) {
-					throw new OsmosisRuntimeException(
-							"Argument " + (i + 1) + " doesn't contain a value after the '=' (ie. name=value).");
-				}
-				
-				// Split the argument into name and value.
-				argName = arg.substring(0, equalsIndex);
-				argValue = arg.substring(equalsIndex + 1);
-				
-				// Add pipeline arguments to pipeArgs, all other arguments to taskArgs.
-				// A pipeline arg is inPipe, inPipe.x, outPipe or outPipe.x.
-				if (
-						PipelineConstants.IN_PIPE_ARGUMENT_PREFIX.equals(argName)
-						|| argName.indexOf(PipelineConstants.IN_PIPE_ARGUMENT_PREFIX + ".") == 0
-						|| PipelineConstants.OUT_PIPE_ARGUMENT_PREFIX.equals(argName)
-						|| argName.indexOf(PipelineConstants.OUT_PIPE_ARGUMENT_PREFIX + ".") == 0) {
-					pipeArgs.put(argName, argValue);
-				} else {
-					taskArgs.put(argName, argValue);
-				}
-				
-			} else {
-				if (defaultArgIndex >= 0) {
-					throw new OsmosisRuntimeException(
-							"Only one default (un-named) argument can exist per task.  Arguments "
-							+ (i + 1) + " and " + (defaultArgIndex + 1) + " have no name.");
-				}
-				
-				defaultArg = arg;
-				defaultArgIndex = i;
-			}
-			
-			i++;
-		}
-		
-		// Build a unique task id.
-		taskId = (taskConfigList.size() + 1) + "-" + taskType;
-		
-		// Create a new task information object and add it to the list.
-		taskConfigList.add(
-			new TaskConfiguration(taskId, taskType, pipeArgs, taskArgs, defaultArg)
-		);
-		
-		return i;
-	}
-	
-	
-	/**
-	 * The list of task information objects.
-	 * 
-	 * @return The taskInfoList.
-	 */
-	public List<TaskConfiguration> getTaskInfoList() {
-		return taskConfigList;
-	}
-	
-	
-	/**
-	 * The level of logging required.
-	 * 
-	 * @return The log level to be used.
-	 */
-	public Level getLogLevel() {
-		int logLevelIndex;
-		
-		logLevelIndex = DEFAULT_LOG_LEVEL_INDEX + verboseValue - quietValue;
-		
-		if (logLevelIndex < 0) {
-			logLevelIndex = 0;
-		}
-		if (logLevelIndex >= LOG_LEVELS.length) {
-			logLevelIndex = LOG_LEVELS.length - 1;
-		}
-		
-		return LOG_LEVELS[logLevelIndex];
-	}
-	
-	
-	/**
-	 * Returns the plugins to be loaded.
-	 * 
-	 * @return The list of plugin class names.
-	 */
-	public List<String> getPlugins() {
-		return plugins;
-	}
-	
-	
-	/**
-	 * A data storage class holding information relating to a global option
-	 * during parsing.
-	 */
-	private class GlobalOptionConfiguration {
-		/**
-		 * The name of the option.
-		 */
-		public String name;
-		/**
-		 * The parameters for the option.
-		 */
-		public List<String> parameters;
-		/**
-		 * The command line argument offset of this global option.
-		 */
-		public int offset;
-		
-		
-		/**
-		 * Creates a new instance.
-		 */
-		public GlobalOptionConfiguration() {
-			parameters = new ArrayList<String>();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java b/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java
deleted file mode 100644
index ed7553b..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.container.v0_6;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-
-
-/**
- * Entity container implementation for relations.
- * 
- * @author Brett Henderson
- */
-public class RelationContainer extends EntityContainer {
-	private static final long serialVersionUID = 1L;
-	
-	
-	private Relation relation;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param relation
-	 *            The relation to wrap.
-	 */
-	public RelationContainer(Relation relation) {
-		this.relation = relation;
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sr
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public RelationContainer(StoreReader sr, StoreClassRegister scr) {
-		relation = new Relation(sr, scr);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void store(StoreWriter sw, StoreClassRegister scr) {
-		relation.store(sw, scr);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(EntityProcessor processor) {
-		processor.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Relation getEntity() {
-		return relation;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public RelationContainer getWriteableInstance() {
-		if (relation.isReadOnly()) {
-			return new RelationContainer(relation.getWriteableInstance());
-		} else {
-			return this;
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java b/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java
deleted file mode 100644
index a54c167..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.container.v0_6;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-
-
-/**
- * Entity container implementation for ways.
- * 
- * @author Brett Henderson
- */
-public class WayContainer extends EntityContainer {
-	private static final long serialVersionUID = 1L;
-	
-	
-	private Way way;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param way
-	 *            The way to wrap.
-	 */
-	public WayContainer(Way way) {
-		this.way = way;
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sr
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public WayContainer(StoreReader sr, StoreClassRegister scr) {
-		way = new Way(sr, scr);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void store(StoreWriter sw, StoreClassRegister scr) {
-		way.store(sw, scr);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(EntityProcessor processor) {
-		processor.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Way getEntity() {
-		return way;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public WayContainer getWriteableInstance() {
-		if (way.isReadOnly()) {
-			return new WayContainer(way.getWriteableInstance());
-		} else {
-			return this;
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java b/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java
deleted file mode 100644
index 0286701..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.database;
-
-
-/**
- * Defines common constants shared between database tasks.
- * 
- * @author Brett Henderson
- */
-public final class DatabaseConstants {
-	
-	/**
-	 * This class cannot be instantiated.
-	 */
-	private DatabaseConstants() {
-	}
-	
-
-    /**
-     * The task argument for specifying an database authorisation properties file.
-     */
-    public static final String TASK_ARG_AUTH_FILE = "authFile";
-
-    /**
-     * The task argument for specifying the host for a database connection.
-     */
-    public static final String TASK_ARG_HOST = "host";
-
-    /**
-     * The task argument for specifying the database instance for a database connection.
-     */
-    public static final String TASK_ARG_DATABASE = "database";
-
-    /**
-     * The task argument for specifying the user for a database connection.
-     */
-    public static final String TASK_ARG_USER = "user";
-    
-    /**
-     * The task argument for specifying the database type to be used.
-     */
-    public static final String TASK_ARG_DB_TYPE = "dbType";
-
-    /**
-     * The task argument for specifying the password for a database connection.
-     */
-    public static final String TASK_ARG_PASSWORD = "password";
-
-    /**
-     * The task argument for specifying whether schema version validation should be performed.
-     */
-    public static final String TASK_ARG_VALIDATE_SCHEMA_VERSION = "validateSchemaVersion";
-
-    /**
-     * The task argument for specifying what should occur if an invalid schema version is
-     * encountered.
-     */
-    public static final String TASK_ARG_ALLOW_INCORRECT_SCHEMA_VERSION = "allowIncorrectSchemaVersion";
-
-    /**
-     * The task argument for forcing a utf-8 database connection.
-     */
-    public static final String TASK_ARG_FORCE_UTF8 = "forceUtf8";
-
-    /**
-     * The task argument for enabling profiling on the database connection.
-     */
-    public static final String TASK_ARG_PROFILE_SQL = "profileSql";
-
-    /**
-     * The default host for a database connection.
-     */
-    public static final String TASK_DEFAULT_HOST = "localhost";
-
-    /**
-     * The default database for a database connection.
-     */
-    public static final String TASK_DEFAULT_DATABASE = "osm";
-
-    /**
-     * The default user for a database connection.
-     */
-    public static final String TASK_DEFAULT_USER = "osm";
-
-    /**
-     * The default password for a database connection.
-     */
-    public static final DatabaseType TASK_DEFAULT_DB_TYPE = DatabaseType.POSTGRESQL;
-
-    /**
-     * The default password for a database connection.
-     */
-    public static final String TASK_DEFAULT_PASSWORD = "";
-
-    /**
-     * The default value for whether schema version validation should be performed.
-     */
-    public static final boolean TASK_DEFAULT_VALIDATE_SCHEMA_VERSION = true;
-
-    /**
-     * The default value for whether the program should allow an incorrect schema version.
-     */
-    public static final boolean TASK_ALLOW_INCORRECT_SCHEMA_VERSION = false;
-
-    /**
-     * The default value for forcing a utf-8 connection.
-     */
-    public static final boolean TASK_DEFAULT_FORCE_UTF8 = false;
-
-    /**
-     * The default value for enabling profile on a database connection.
-     */
-    public static final boolean TASK_DEFAULT_PROFILE_SQL = false;
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java b/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java
deleted file mode 100644
index 80f97d6..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java
+++ /dev/null
@@ -1,503 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.domain.v0_6;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedList;
-
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-
-/**
- * A data class representing an OSM data bound element.
- * 
- * @author Karl Newman
- */
-public class Bound extends Entity implements Comparable<Bound> {
-	
-	private static final double MIN_LATITUDE = -90.0;
-	private static final double MAX_LATITUDE = 90.0;
-	private static final double MIN_LONGITUDE = -180.0;
-	private static final double MAX_LONGITUDE = 180.0;
-	
-	private double right;
-	private double left;
-	private double top;
-	private double bottom;
-	private String origin;
-	
-	
-	/**
-	 * Creates a new instance which covers the entire planet.
-	 * 
-	 * @param origin
-	 *            The origin (source) of the data, typically a URI
-	 * 
-	 */
-	public Bound(String origin) {
-		this(MAX_LONGITUDE, MIN_LONGITUDE, MAX_LATITUDE, MIN_LATITUDE, origin);
-	}
-
-
-	/**
-	 * Creates a new instance with the specified boundaries.
-	 * 
-	 * @param right
-	 *            The longitude coordinate of the right (East) edge of the bound
-	 * @param left
-	 *            The longitude coordinate of the left (West) edge of the bound
-	 * @param top
-	 *            The latitude coordinate of the top (North) edge of the bound
-	 * @param bottom
-	 *            The latitude coordinate of the bottom (South) edge of the bound
-	 * @param origin
-	 *            The origin (source) of the data, typically a URI
-	 */
-	public Bound(double right, double left, double top, double bottom, String origin) {
-		super(new CommonEntityData(0, 0, new Date(), OsmUser.NONE, 0)); // minimal underlying entity
-		
-		// Check if any coordinates are out of bounds
-		if (Double.compare(right, MAX_LONGITUDE + 1.0d) > 0
-		        || Double.compare(right, MIN_LONGITUDE - 1.0d) < 0
-		        || Double.compare(left, MAX_LONGITUDE + 1.0d) > 0
-		        || Double.compare(left, MIN_LONGITUDE - 1.0d) < 0
-		        || Double.compare(top, MAX_LATITUDE + 1.0d) > 0
-		        || Double.compare(top, MIN_LATITUDE - 1.0d) < 0
-		        || Double.compare(bottom, MAX_LATITUDE + 1.0d) > 0
-		        || Double.compare(bottom, MIN_LATITUDE - 1.0d) < 0) {
-			throw new IllegalArgumentException("Bound coordinates outside of valid range");
-		}
-		if (Double.compare(top, bottom) < 0) {
-			throw new IllegalArgumentException("Bound top < bottom");
-		}
-		if (origin == null) {
-			throw new IllegalArgumentException("Bound origin is null");
-		}
-		this.right = right;
-		this.left = left;
-		this.top = top;
-		this.bottom = bottom;
-		this.origin = origin;
-	}
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sr
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers within the store.
-	 */
-	public Bound(StoreReader sr, StoreClassRegister scr) {
-		super(sr, scr);
-
-		this.right = sr.readDouble();
-		this.left = sr.readDouble();
-		this.top = sr.readDouble();
-		this.bottom = sr.readDouble();
-		this.origin = sr.readString();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void store(StoreWriter sw, StoreClassRegister scr) {
-		super.store(sw, scr);
-
-		sw.writeDouble(right);
-		sw.writeDouble(left);
-		sw.writeDouble(top);
-		sw.writeDouble(bottom);
-		sw.writeString(origin);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityType getType() {
-		return EntityType.Bound;
-	}
-
-
-	/**
-	 * @return The right (East) bound longitude
-	 */
-	public double getRight() {
-		return right;
-	}
-
-
-	/**
-	 * @return The left (West) bound longitude
-	 */
-	public double getLeft() {
-		return left;
-	}
-
-
-	/**
-	 * @return The top (North) bound latitude
-	 */
-	public double getTop() {
-		return top;
-	}
-
-
-	/**
-	 * @return The bottom (South) bound latitude
-	 */
-	public double getBottom() {
-		return bottom;
-	}
-
-
-	/**
-	 * @return the origin
-	 */
-	public String getOrigin() {
-		return origin;
-	}
-
-
-	/**
-	 * Calculate the intersected area of this with the specified bound.
-	 * 
-	 * @param intersectingBound
-	 *            Bound element with which to calculate the intersection
-	 * @return Bound Resultant intersection of the two bound object
-	 */
-	public Bound intersect(Bound intersectingBound) {
-		String newOrigin;
-		double newRight = 0.0, newLeft = 0.0, newTop, newBottom;
-
-		boolean intersect180, this180; // flags to indicate bound cross antimeridian
-
-		if (intersectingBound == null) {
-			return null; // no intersection
-		}
-		// first check the vertical intersection
-		newTop = Math.min(this.getTop(), intersectingBound.getTop());
-		newBottom = Math.max(this.getBottom(), intersectingBound.getBottom());
-		if (Double.compare(newBottom, newTop) >= 0) { // no north-south intersecting region
-			return null;
-		}
-
-		intersect180 = (Double.compare(intersectingBound.getLeft(), intersectingBound.getRight()) > 0);
-		this180 = (Double.compare(this.getLeft(), this.getRight()) > 0);
-
-		if ((intersect180 && this180) || !(intersect180 || this180)) {
-			// if both or neither cross the antimeridian, use the simple case
-			newRight = Math.min(this.getRight(), intersectingBound.getRight());
-			newLeft = Math.max(this.getLeft(), intersectingBound.getLeft());
-			if (!(intersect180 || this180) && (Double.compare(newLeft, newRight) >= 0)) {
-				/*
-				 * This is only applicable for the case where neither cross the antimeridian,
-				 * because if both cross, they must intersect.
-				 */
-				return null; // no intersecting area
-			}
-		} else {
-			Bound b1, b2; // stand-ins for this and intersectingBound
-
-			if (intersect180 && !this180) {
-				// passed parameter Bound crosses the antimeridian, this Bound doesn't
-				b1 = this;
-				b2 = intersectingBound;
-			} else {
-				// this Bound crosses the antimeridian, passed parameter Bound doesn't
-				b1 = intersectingBound;
-				b2 = this;
-			}
-			if (Double.compare(b1.getRight(), b2.getLeft()) > 0
-			        && Double.compare(b1.getLeft(), b2.getRight()) < 0) {
-				// intersects on both sides of the antimeridian--just pick the smaller of the
-				// two
-				Double diff1 = b1.getRight() - b1.getLeft();
-				Double diff2 = b2.getRight() - MIN_LONGITUDE + MAX_LONGITUDE - b2.getLeft();
-				if (Double.compare(diff1, diff2) <= 0) {
-					newRight = b1.getRight();
-					newLeft = b1.getLeft();
-				} else {
-					newRight = b2.getRight();
-					newLeft = b2.getLeft();
-				}
-			} else if (Double.compare(b1.getRight(), b2.getLeft()) > 0) {
-				// intersects on the East side of the antimeridian
-				newRight = b1.getRight();
-				newLeft = b2.getLeft();
-			} else if (Double.compare(b1.getLeft(), b2.getRight()) < 0) {
-				// intersects on the West side of the antimeridian
-				newRight = b2.getRight();
-				newLeft = b1.getLeft();
-			}
-		}
-		if (Double.compare(newRight, newLeft) == 0) {
-			return null;
-		}
-		
-		// Keep the origin string from this if it's not blank, otherwise use the origin string from
-		// the intersecting Bound
-		if (origin != "") {
-			newOrigin = origin;
-		} else {
-			newOrigin = intersectingBound.origin;
-		}
-		
-		return new Bound(newRight, newLeft, newTop, newBottom, newOrigin);
-	}
-
-
-	/**
-	 * Calculate the union area of this with the specified bound. Not a strict mathematical union,
-	 * but the smallest rectangular area which includes both bound. Thus, result may include areas
-	 * not contained in the original bound.
-	 * 
-	 * @param unionBound
-	 *            Bound element with which to calculate the union
-	 * @return Bound Resultant union of the two bound objects
-	 */
-	public Bound union(Bound unionBound) {
-		double newRight = 0.0, newLeft = 0.0, newTop, newBottom;
-		String newOrigin;
-
-		if (unionBound == null) {
-			return this; // nothing to compute a union with
-		}
-
-		// First compute the vertical union
-		newTop = Math.max(this.getTop(), unionBound.getTop());
-		newBottom = Math.min(this.getBottom(), unionBound.getBottom());
-		if (Double.compare(newBottom, newTop) >= 0) { // no north-south intersecting region
-			return null;
-		}
-		// Next check the (likely) common case where one of the bound covers the planet
-		if ((Double.compare(this.getLeft(), MIN_LONGITUDE) == 0 && Double.compare(
-		        this.getRight(),
-		        MAX_LONGITUDE) == 0)
-		        || (Double.compare(unionBound.getLeft(), MIN_LONGITUDE) == 0 && Double.compare(
-		                unionBound.getRight(),
-		                MAX_LONGITUDE) == 0)) {
-			newRight = MAX_LONGITUDE;
-			newLeft = MIN_LONGITUDE;
-		} else {
-			boolean union180, this180; // flags to indicate bound cross antimeridian
-			double size1, size2; // resulting union sizes for comparison
-
-			union180 = (Double.compare(unionBound.getLeft(), unionBound.getRight()) > 0);
-			this180 = (Double.compare(this.getLeft(), this.getRight()) > 0);
-
-			if (union180 && this180) {
-				// if both cross the antimeridian, then the union will cross, too.
-				newRight = Math.max(this.getRight(), unionBound.getRight());
-				newLeft = Math.min(this.getLeft(), unionBound.getLeft());
-			} else if (!(union180 || this180)) {
-				// neither cross the antimeridian, but the union might
-
-				// first calculate the size of a simple union which doesn't cross the antimeridian
-				size1 = Math.max(this.getRight(), unionBound.getRight())
-				        - Math.min(this.getLeft(), unionBound.getLeft());
-				// then calculate the size of the resulting union which does cross the antimeridian
-				size2 = (Math.min(this.getRight(), unionBound.getRight()) - MIN_LONGITUDE)
-				        + (MAX_LONGITUDE - Math.max(this.getLeft(), unionBound.getLeft()));
-
-				// now pick the smaller of the two
-				if (Double.compare(size1, size2) <= 0) {
-					newRight = Math.max(this.getRight(), unionBound.getRight());
-					newLeft = Math.min(this.getLeft(), unionBound.getLeft());
-				} else {
-					newRight = Math.min(this.getRight(), unionBound.getRight());
-					newLeft = Math.max(this.getLeft(), unionBound.getLeft());
-				}
-			} else {
-				// One of the Bound crosses the antimeridian, the other doesn't
-				Bound b1, b2;
-				if (union180 && !this180) {
-					// passed parameter Bound crosses the antimeridian, this Bound doesn't
-					b1 = unionBound;
-					b2 = this;
-				} else {
-					// this Bound crosses the antimeridian, passed parameter Bound doesn't
-					b1 = this;
-					b2 = unionBound;
-				}
-
-				// check for the case where the two Bound overlap on both edges such that the union
-				// covers the planet.
-				if (Double.compare(b1.getRight(), b2.getLeft()) >= 0
-				        && Double.compare(b1.getLeft(), b2.getRight()) <= 0) {
-					newLeft = MIN_LONGITUDE;
-					newRight = MAX_LONGITUDE;
-				} else {
-					// first calculate the size of a union with the simple bound added to the left
-					size1 = (Math.max(b1.getRight(), b2.getRight()) - MIN_LONGITUDE)
-					        + (MAX_LONGITUDE - b1.getLeft());
-					// first calculate the size of a union with the simple bound added to the right
-					size2 = (b1.getRight() - MIN_LONGITUDE)
-					        + (MAX_LONGITUDE - Math.min(b1.getLeft(), b2.getLeft()));
-
-					// now pick the smaller of the two
-					if (Double.compare(size1, size2) <= 0) {
-						newRight = Math.max(b1.getRight(), b2.getRight());
-						newLeft = b1.getLeft();
-					} else {
-						newRight = b1.getRight();
-						newLeft = Math.min(b1.getLeft(), b2.getLeft());
-					}
-				}
-			}
-		}
-
-		if (Double.compare(newRight, newLeft) == 0) {
-			return null;
-		}
-		
-		// Keep the origin string from this if it's not blank, otherwise use the origin string from
-		// the union Bound
-		if (this.getOrigin() != "") {
-			newOrigin = getOrigin();
-		} else {
-			newOrigin = unionBound.getOrigin();
-		}
-		
-		return new Bound(newRight, newLeft, newTop, newBottom, newOrigin);
-	}
-
-
-	/**
-	 * Retrieve a collection of Bound objects which collectively comprise the entirety of this
-	 * Bound but individually do not cross the antimeridian and thus can be used in simple area
-	 * operations. The degenerate case will return this Bound.
-	 * 
-	 * @return Iterable collection of Bound elements
-	 */
-	public Iterable<Bound> toSimpleBound() {
-		Collection<Bound> c = new LinkedList<Bound>();
-		if (Double.compare(this.getLeft(), this.getRight()) < 0) {
-			// simple case, just return this
-			c.add(this);
-		} else {
-			// split the bound into two parts--one on either side of the antimeridian
-			c.add(new Bound(
-			        MAX_LONGITUDE,
-			        this.getLeft(),
-			        this.getTop(),
-			        this.getBottom(),
-			        this.getOrigin()));
-			c.add(new Bound(
-			        this.getRight(),
-			        MIN_LONGITUDE,
-			        this.getTop(),
-			        this.getBottom(),
-			        this.getOrigin()));
-		}
-		return Collections.unmodifiableCollection(c);
-	}
-
-
-	/**
-	 * Compares this bound to the specified bound. The bound comparison is based on a comparison
-	 * of area, latitude, and longitude in that order.
-	 * 
-	 * @param comparisonBound
-	 *            The bound to compare to.
-	 * @return 0 if equal, < 0 if this sorts before comparison (this is "smaller"), and > 0 if this
-	 *         sorts before comparison (this is "bigger")
-	 */
-	public int compareTo(Bound comparisonBound) {
-		double areaT = 0.0, areaC = 0.0;
-		int result;
-
-		/*
-		 * This is a very simple "area" calculation just using the coordinate values, not accounting
-		 * for any projections.
-		 */
-		for (Bound b : this.toSimpleBound()) {
-			areaT += (b.getRight() - b.getLeft()) * (b.getTop() - b.getBottom());
-		}
-		for (Bound b : comparisonBound.toSimpleBound()) {
-			areaC += (b.getRight() - b.getLeft()) * (b.getTop() - b.getBottom());
-		}
-
-		// Use Double.compare (instead of < and >) to catch unique border cases
-		result = Double.compare(areaT, areaC);
-		if (result != 0) {
-			return result;
-		}
-
-		result = Double.compare(this.getTop(), comparisonBound.getTop());
-		if (result != 0) {
-			return result;
-		}
-
-		result = Double.compare(this.getBottom(), comparisonBound.getBottom());
-		if (result != 0) {
-			return result;
-		}
-
-		result = Double.compare(this.getLeft(), comparisonBound.getLeft());
-		if (result != 0) {
-			return result;
-		}
-
-		result = Double.compare(this.getRight(), comparisonBound.getRight());
-		if (result != 0) {
-			return result;
-		}
-
-		result = this.getOrigin().compareTo(comparisonBound.getOrigin());
-		return result;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(Object o) {
-		if (o instanceof Bound) {
-			return compareTo((Bound) o) == 0;
-		} else {
-			return false;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		/*
-		 * As per the hashCode definition, this doesn't have to be unique it
-		 * just has to return the same value for any two objects that compare
-		 * equal. Using both id and version will provide a good distribution of
-		 * values but is simple to calculate.
-		 */
-		return (int) getId() + getVersion();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Bound getWriteableInstance() {
-		return this;
-	}
-
-    /** 
-     * ${@inheritDoc}.
-     */
-    @Override
-    public String toString() {
-        return "Bound(top=" + getTop() + ", bottom=" + getBottom() + ", left=" + getLeft() + ", right=" + getRight()
-				+ ")";
-    }
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java b/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java
deleted file mode 100644
index dc64afb..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java
+++ /dev/null
@@ -1,386 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.domain.v0_6;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
-import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-
-
-/**
- * A data class representing a single OSM relation.
- * 
- * @author Brett Henderson
- */
-public class Relation extends Entity implements Comparable<Relation> {
-	private List<RelationMember> members;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestamp
-	 *            The last updated timestamp.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @deprecated As of 0.40, replaced by Relation(entityData).
-	 */
-	public Relation(long id, int version, Date timestamp, OsmUser user, long changesetId) {
-		// Chain to the more-specific constructor
-		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestampContainer
-	 *            The container holding the timestamp in an alternative
-	 *            timestamp representation.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @deprecated As of 0.40, replaced by Relation(entityData).
-	 */
-	public Relation(long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId) {
-		super(id, version, timestampContainer, user, changesetId);
-		
-		this.members = new ArrayList<RelationMember>();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param entityData
-	 *            The common entity data.
-	 */
-	public Relation(CommonEntityData entityData) {
-		super(entityData);
-		
-		this.members = new ArrayList<RelationMember>();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestamp
-	 *            The last updated timestamp.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @param tags
-	 *            The tags to apply to the object.
-	 * @param members
-	 *            The members to apply to the object.
-	 * @deprecated As of 0.40, replaced by Relation(entityData, members).
-	 */
-	public Relation(
-			long id, int version, Date timestamp, OsmUser user, long changesetId, Collection<Tag> tags,
-			List<RelationMember> members) {
-		// Chain to the more-specific constructor
-		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId, tags, members);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestampContainer
-	 *            The container holding the timestamp in an alternative
-	 *            timestamp representation.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @param tags
-	 *            The tags to apply to the object.
-	 * @param members
-	 *            The members to apply to the object.
-	 * @deprecated As of 0.40, replaced by Relation(entityData, members).
-	 */
-	public Relation(
-			long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId,
-			Collection<Tag> tags, List<RelationMember> members) {
-		super(id, version, timestampContainer, user, changesetId, tags);
-		
-		this.members = new ArrayList<RelationMember>(members);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param entityData
-	 *            The common entity data.
-	 * @param members
-	 *            The members to apply to the object.
-	 */
-	public Relation(
-			CommonEntityData entityData, List<RelationMember> members) {
-		super(entityData);
-		
-		this.members = new ArrayList<RelationMember>(members);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param originalRelation
-	 *            The relation to clone from.
-	 */
-	private Relation(Relation originalRelation) {
-		super(originalRelation);
-		
-		this.members = new ArrayList<RelationMember>(members);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sr
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public Relation(StoreReader sr, StoreClassRegister scr) {
-		super(sr, scr);
-		
-		int featureCount;
-		
-		featureCount = sr.readInteger();
-		
-		members = new ArrayList<RelationMember>();
-		for (int i = 0; i < featureCount; i++) {
-			members.add(new RelationMember(sr, scr));
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void store(StoreWriter sw, StoreClassRegister scr) {
-		super.store(sw, scr);
-		
-		sw.writeInteger(members.size());
-		for (RelationMember relationMember : members) {
-			relationMember.store(sw, scr);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityType getType() {
-		return EntityType.Relation;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(Object o) {
-		if (o instanceof Relation) {
-			return compareTo((Relation) o) == 0;
-		} else {
-			return false;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		/*
-		 * As per the hashCode definition, this doesn't have to be unique it
-		 * just has to return the same value for any two objects that compare
-		 * equal. Using both id and version will provide a good distribution of
-		 * values but is simple to calculate.
-		 */
-		return (int) getId() + getVersion();
-	}
-	
-	
-	/**
-	 * Compares this member list to the specified member list. The bigger list
-	 * is considered bigger, if that is equal then each relation member is
-	 * compared.
-	 * 
-	 * @param comparisonMemberList
-	 *            The member list to compare to.
-	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
-	 *         "bigger".
-	 */
-	protected int compareMemberList(Collection<RelationMember> comparisonMemberList) {
-		Iterator<RelationMember> i;
-		Iterator<RelationMember> j;
-		
-		// The list with the most entities is considered bigger.
-		if (members.size() != comparisonMemberList.size()) {
-			return members.size() - comparisonMemberList.size();
-		}
-		
-		// Check the individual node references.
-		i = members.iterator();
-		j = comparisonMemberList.iterator();
-		while (i.hasNext()) {
-			int result = i.next().compareTo(j.next());
-			
-			if (result != 0) {
-				return result;
-			}
-		}
-		
-		// There are no differences.
-		return 0;
-	}
-
-
-	/**
-	 * Compares this relation to the specified relation. The relation comparison
-	 * is based on a comparison of id, version, timestamp, and tags in that order.
-	 * 
-	 * @param comparisonRelation
-	 *            The relation to compare to.
-	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
-	 *         "bigger".
-	 */
-	public int compareTo(Relation comparisonRelation) {
-		int memberListResult;
-		
-		if (this.getId() < comparisonRelation.getId()) {
-			return -1;
-		}
-		if (this.getId() > comparisonRelation.getId()) {
-			return 1;
-		}
-
-		if (this.getVersion() < comparisonRelation.getVersion()) {
-			return -1;
-		}
-		if (this.getVersion() > comparisonRelation.getVersion()) {
-			return 1;
-		}
-
-		if (this.getTimestamp() == null && comparisonRelation.getTimestamp() != null) {
-			return -1;
-		}
-		if (this.getTimestamp() != null && comparisonRelation.getTimestamp() == null) {
-			return 1;
-		}
-		if (this.getTimestamp() != null && comparisonRelation.getTimestamp() != null) {
-			int result;
-			
-			result = this.getTimestamp().compareTo(comparisonRelation.getTimestamp());
-			
-			if (result != 0) {
-				return result;
-			}
-		}
-		
-		memberListResult = compareMemberList(
-			comparisonRelation.members
-		);
-		
-		if (memberListResult != 0) {
-			return memberListResult;
-		}
-		
-		return compareTags(comparisonRelation.getTags());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void makeReadOnly() {
-		if (!isReadOnly()) {
-			members = Collections.unmodifiableList(members);
-		}
-		
-		super.makeReadOnly();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Relation getWriteableInstance() {
-		if (isReadOnly()) {
-			return new Relation(this);
-		} else {
-			return this;
-		}
-	}
-	
-	
-	/**
-	 * Returns the attached list of relation members. The returned list is
-	 * read-only.
-	 * 
-	 * @return The member list.
-	 */
-	public List<RelationMember> getMembers() {
-		return members;
-	}
-
-    /** 
-     * ${@inheritDoc}.
-     */
-    @Override
-    public String toString() {
-        String type = null;
-        Collection<Tag> tags = getTags();
-        for (Tag tag : tags) {
-            if (tag.getKey() != null && tag.getKey().equalsIgnoreCase("type")) {
-                type = tag.getValue();
-                break;
-            }
-        }
-        if (type != null) {
-            return "Relation(id=" + getId() + ", #tags=" +  getTags().size() + ", type='" + type + "')";
-        }
-        return "Relation(id=" + getId() + ", #tags=" +  getTags().size() + ")";
-    }
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java b/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java
deleted file mode 100644
index 8ffb911..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java
+++ /dev/null
@@ -1,395 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.domain.v0_6;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
-import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-import org.openstreetmap.osmosis.core.util.IntAsChar;
-
-
-/**
- * A data class representing a single OSM way.
- * 
- * @author Brett Henderson
- */
-public class Way extends Entity implements Comparable<Way> {
-	
-	private List<WayNode> wayNodes;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestamp
-	 *            The last updated timestamp.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @deprecated As of 0.40, replaced by Way(entityData).
-	 */
-	public Way(long id, int version, Date timestamp, OsmUser user, long changesetId) {
-		// Chain to the more specific constructor
-		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestampContainer
-	 *            The container holding the timestamp in an alternative
-	 *            timestamp representation.
-	 * @param user
-	 *            The name of the user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @deprecated As of 0.40, replaced by Way(entityData).
-	 */
-	public Way(long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId) {
-		super(id, version, timestampContainer, user, changesetId);
-		
-		this.wayNodes = new ArrayList<WayNode>();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param entityData
-	 *            The common entity data.
-	 */
-	public Way(CommonEntityData entityData) {
-		super(entityData);
-		
-		this.wayNodes = new ArrayList<WayNode>();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestamp
-	 *            The last updated timestamp.
-	 * @param user
-	 *            The user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @param tags
-	 *            The tags to apply to the object.
-	 * @param wayNodes
-	 *            The way nodes to apply to the object
-	 * @deprecated As of 0.40, replaced by Way(entityData, wayNodes).
-	 */
-	public Way(long id, int version, Date timestamp, OsmUser user, long changesetId, Collection<Tag> tags,
-			List<WayNode> wayNodes) {
-		// Chain to the more specific constructor
-		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId, tags, wayNodes);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param id
-	 *            The unique identifier.
-	 * @param version
-	 *            The version of the entity.
-	 * @param timestampContainer
-	 *            The container holding the timestamp in an alternative
-	 *            timestamp representation.
-	 * @param user
-	 *            The name of the user that last modified this entity.
-	 * @param changesetId
-	 *            The id of the changeset that this version of the entity was created by.
-	 * @param tags
-	 *            The tags to apply to the object.
-	 * @param wayNodes
-	 *            The way nodes to apply to the object
-	 * @deprecated As of 0.40, replaced by Way(entityData, wayNodes).
-	 */
-	public Way(
-			long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId,
-			Collection<Tag> tags, List<WayNode> wayNodes) {
-		super(id, version, timestampContainer, user, changesetId, tags);
-		
-		this.wayNodes = new ArrayList<WayNode>(wayNodes);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param entityData
-	 *            The common entity data.
-	 * @param wayNodes
-	 *            The way nodes to apply to the object
-	 */
-	public Way(
-			CommonEntityData entityData, List<WayNode> wayNodes) {
-		super(entityData);
-		
-		this.wayNodes = new ArrayList<WayNode>(wayNodes);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param originalWay
-	 *            The way to clone from.
-	 */
-	private Way(Way originalWay) {
-		super(originalWay);
-		
-		this.wayNodes = new ArrayList<WayNode>(wayNodes);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sr
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public Way(StoreReader sr, StoreClassRegister scr) {
-		super(sr, scr);
-		
-		int featureCount;
-		
-		featureCount = sr.readCharacter();
-		
-		wayNodes = new ArrayList<WayNode>();
-		for (int i = 0; i < featureCount; i++) {
-			wayNodes.add(new WayNode(sr, scr));
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void store(StoreWriter sw, StoreClassRegister scr) {
-		super.store(sw, scr);
-		
-		sw.writeCharacter(IntAsChar.intToChar(wayNodes.size()));
-		for (WayNode wayNode : wayNodes) {
-			wayNode.store(sw, scr);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityType getType() {
-		return EntityType.Way;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(Object o) {
-		if (o instanceof Way) {
-			return compareTo((Way) o) == 0;
-		} else {
-			return false;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		/*
-		 * As per the hashCode definition, this doesn't have to be unique it
-		 * just has to return the same value for any two objects that compare
-		 * equal. Using both id and version will provide a good distribution of
-		 * values but is simple to calculate.
-		 */
-		return (int) getId() + getVersion();
-	}
-
-
-	/**
-	 * Compares this node list to the specified node list. The comparison is
-	 * based on a direct comparison of the node ids.
-	 * 
-	 * @param comparisonWayNodes
-	 *            The node list to compare to.
-	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
-	 *         "bigger".
-	 */
-	protected int compareWayNodes(List<WayNode> comparisonWayNodes) {
-		Iterator<WayNode> i;
-		Iterator<WayNode> j;
-		
-		// The list with the most entities is considered bigger.
-		if (wayNodes.size() != comparisonWayNodes.size()) {
-			return wayNodes.size() - comparisonWayNodes.size();
-		}
-		
-		// Check the individual way nodes.
-		i = wayNodes.iterator();
-		j = comparisonWayNodes.iterator();
-		while (i.hasNext()) {
-			int result = i.next().compareTo(j.next());
-			
-			if (result != 0) {
-				return result;
-			}
-		}
-		
-		// There are no differences.
-		return 0;
-	}
-
-
-	/**
-	 * Compares this way to the specified way. The way comparison is based on a
-	 * comparison of id, version, timestamp, wayNodeList and tags in that order.
-	 * 
-	 * @param comparisonWay
-	 *            The way to compare to.
-	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
-	 *         "bigger".
-	 */
-	public int compareTo(Way comparisonWay) {
-		int wayNodeListResult;
-		
-		if (this.getId() < comparisonWay.getId()) {
-			return -1;
-		}
-		if (this.getId() > comparisonWay.getId()) {
-			return 1;
-		}
-		
-		if (this.getVersion() < comparisonWay.getVersion()) {
-			return -1;
-		}
-		if (this.getVersion() > comparisonWay.getVersion()) {
-			return 1;
-		}
-		
-		if (this.getTimestamp() == null && comparisonWay.getTimestamp() != null) {
-			return -1;
-		}
-		if (this.getTimestamp() != null && comparisonWay.getTimestamp() == null) {
-			return 1;
-		}
-		if (this.getTimestamp() != null && comparisonWay.getTimestamp() != null) {
-			int result;
-			
-			result = this.getTimestamp().compareTo(comparisonWay.getTimestamp());
-			
-			if (result != 0) {
-				return result;
-			}
-		}
-		
-		wayNodeListResult = compareWayNodes(
-			comparisonWay.getWayNodes()
-		);
-		
-		if (wayNodeListResult != 0) {
-			return wayNodeListResult;
-		}
-		
-		return compareTags(comparisonWay.getTags());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void makeReadOnly() {
-		if (!isReadOnly()) {
-			wayNodes = Collections.unmodifiableList(wayNodes);
-		}
-		
-		super.makeReadOnly();
-	}
-
-
-	/**
-	 * Returns the attached list of way nodes. The returned list is read-only.
-	 * 
-	 * @return The wayNodeList.
-	 */
-	public List<WayNode> getWayNodes() {
-		return wayNodes;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Way getWriteableInstance() {
-		if (isReadOnly()) {
-			return new Way(this);
-		} else {
-			return this;
-		}
-	}
-
-
-    /**
-     * Is this way closed? (A way is closed if the first node id equals the last node id.)
-     *
-     * @return True or false
-     */
-    public boolean isClosed() {
-        return wayNodes.get(0).getNodeId() == wayNodes.get(wayNodes.size() - 1).getNodeId();
-    }
-
-    /** 
-     * ${@inheritDoc}.
-     */
-    @Override
-    public String toString() {
-        String name = null;
-        Collection<Tag> tags = getTags();
-        for (Tag tag : tags) {
-            if (tag.getKey() != null && tag.getKey().equalsIgnoreCase("name")) {
-                name = tag.getValue();
-                break;
-            }
-        }
-        if (name != null) {
-            return "Way(id=" + getId() + ", #tags=" +  getTags().size() + ", name='" + name + "')";
-        }
-        return "Way(id=" + getId() + ", #tags=" +  getTags().size() + ")";
-    }
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java b/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java
deleted file mode 100644
index e10e2cb..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.lifecycle;
-
-
-/**
- * Classes that hold heavyweight resources that can't wait for garbage
- * collection should implement this interface. It provides a release method that
- * should be called by all clients when the class is no longer required. This
- * release method is guaranteed not to throw exceptions and should always be
- * called within a finally clause.
- * 
- * @author Brett Henderson
- */
-public interface Releasable {
-	/**
-	 * Performs resource cleanup tasks such as closing files, or database
-	 * connections. This must be called after all processing is complete and may
-	 * be called multiple times. Implementations must call release on any nested
-	 * Releasable objects. It should be called within a finally block to ensure
-	 * it is called in exception scenarios.
-	 */
-	void release();
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java b/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java
deleted file mode 100644
index 108ec41..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.misc.v0_6;
-
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
-
-
-/**
- * An OSM data source that produces an empty change stream.
- * 
- * @author Brett Henderson
- */
-public class EmptyChangeReader implements RunnableChangeSource {
-	private ChangeSink changeSink;
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		try {
-			changeSink.complete();
-		} finally {
-			changeSink.release();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java b/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java
deleted file mode 100644
index 178a1e7..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.misc.v0_6;
-
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data source that produces an empty entity stream.
- * 
- * @author Brett Henderson
- */
-public class EmptyReader implements RunnableSource {
-	private Sink sink;
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		try {
-			sink.complete();
-		} finally {
-			sink.release();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java b/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java
deleted file mode 100644
index 614b2c7..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.misc.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-
-
-/**
- * An OSM change sink that discards all data sent to it. This is primarily
- * intended for benchmarking purposes.
- * 
- * @author Brett Henderson
- */
-public class NullChangeWriter implements ChangeSink {
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer change) {
-		// Discard the data.
-	}
-	
-	
-	/**
-	 * Flushes all changes to file.
-	 */
-	public void complete() {
-		// Nothing to do.
-	}
-	
-	
-	/**
-	 * Cleans up any open file handles.
-	 */
-	public void release() {
-		// Nothing to do.
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java b/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java
deleted file mode 100644
index b378122..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.misc.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink that discards all data sent to it. This is primarily
- * intended for benchmarking purposes.
- * 
- * @author Brett Henderson
- */
-public class NullWriter implements Sink {
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Discard the data.
-	}
-	
-	
-	/**
-	 * Flushes all changes to file.
-	 */
-	public void complete() {
-		// Nothing to do.
-	}
-	
-	
-	/**
-	 * Cleans up any open file handles.
-	 */
-	public void release() {
-		// Nothing to do.
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java b/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java
deleted file mode 100644
index 66ebeae..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.progress.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.progress.v0_6.impl.ProgressTracker;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * Logs progress information using jdk logging at info level at regular intervals.
- * 
- * @author Brett Henderson
- */
-public class ChangeProgressLogger implements ChangeSinkChangeSource {
-	
-	private static final Logger LOG = Logger.getLogger(ChangeProgressLogger.class.getName());
-	
-	private ChangeSink changeSink;
-	private ProgressTracker progressTracker;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param interval
-	 *            The interval between logging progress reports in milliseconds.
-	 */
-	public ChangeProgressLogger(int interval) {
-		progressTracker = new ProgressTracker(interval);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		Entity entity;
-		ChangeAction action;
-		
-		entity = changeContainer.getEntityContainer().getEntity();
-		action = changeContainer.getAction();
-		
-		if (progressTracker.updateRequired()) {
-			LOG.info(
-					"Processing " + entity.getType() + " " + entity.getId() + " with action " + action + ", "
-					+ progressTracker.getObjectsPerSecond() + " objects/second.");
-		}
-		
-		changeSink.process(changeContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		LOG.info("Processing completion steps.");
-		
-		changeSink.complete();
-		
-		LOG.info("Processing complete.");
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeSink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java b/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java
deleted file mode 100644
index 9172d18..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.progress.v0_6;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkChangeSourceManager;
-
-
-/**
- * The task manager factory for a change progress logger.
- * 
- * @author Brett Henderson
- */
-public class ChangeProgressLoggerFactory extends TaskManagerFactory {
-	private static final String ARG_LOG_INTERVAL = "interval";
-	private static final int DEFAULT_LOG_INTERVAL = 5;
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		ChangeProgressLogger task;
-		int interval;
-		
-		// Get the task arguments.
-		interval = getIntegerArgument(taskConfig, ARG_LOG_INTERVAL, DEFAULT_LOG_INTERVAL);
-		
-		// Build the task object.
-		task = new ChangeProgressLogger(interval * 1000);
-		
-		return new ChangeSinkChangeSourceManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java b/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java
deleted file mode 100644
index 055a6ec..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.progress.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.progress.v0_6.impl.ProgressTracker;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Logs progress information using jdk logging at info level at regular intervals.
- * 
- * @author Brett Henderson
- */
-public class EntityProgressLogger implements SinkSource {
-	
-	private static final Logger LOG = Logger.getLogger(EntityProgressLogger.class.getName());
-	
-	private Sink sink;
-	private ProgressTracker progressTracker;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param interval
-	 *            The interval between logging progress reports in milliseconds.
-	 */
-	public EntityProgressLogger(int interval) {
-		progressTracker = new ProgressTracker(interval);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		Entity entity;
-		
-		entity = entityContainer.getEntity();
-		
-		if (progressTracker.updateRequired()) {
-			LOG.info(
-					"Processing " + entity.getType() + " " + entity.getId() + ", "
-					+ progressTracker.getObjectsPerSecond() + " objects/second.");
-		}
-		
-		sink.process(entityContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		LOG.info("Processing completion steps.");
-		
-		sink.complete();
-		
-		LOG.info("Processing complete.");
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java b/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java
deleted file mode 100644
index 0b679e6..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.progress.v0_6;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
-
-
-/**
- * The task manager factory for an entity progress logger.
- * 
- * @author Brett Henderson
- */
-public class EntityProgressLoggerFactory extends TaskManagerFactory {
-	private static final String ARG_LOG_INTERVAL = "interval";
-	private static final int DEFAULT_LOG_INTERVAL = 5;
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		EntityProgressLogger task;
-		int interval;
-		
-		// Get the task arguments.
-		interval = getIntegerArgument(taskConfig, ARG_LOG_INTERVAL, DEFAULT_LOG_INTERVAL);
-		
-		// Build the task object.
-		task = new EntityProgressLogger(interval * 1000);
-		
-		return new SinkSourceManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java b/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java
deleted file mode 100644
index 0a17ebe..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.progress.v0_6.impl;
-
-/**
- * Maintains state about execution progress. It calculates when the next update
- * is due, and provides statistics on execution.
- * 
- * @author Brett Henderson
- */
-public class ProgressTracker {
-	
-	private int interval;
-	private boolean initialized;
-	private long lastUpdateTimestamp;
-	private long objectCount;
-	private double objectsPerSecond;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param interval
-	 *            The interval between logging progress reports in milliseconds.
-	 */
-	public ProgressTracker(int interval) {
-		this.interval = interval;
-		
-		initialized = false;
-	}
-	
-	
-	/**
-	 * Indicates if an update is due. This should be called once per object that
-	 * is processed.
-	 * 
-	 * @return True if an update is due.
-	 */
-	public boolean updateRequired() {
-		if (!initialized) {
-			lastUpdateTimestamp = System.currentTimeMillis();
-			objectCount = 0;
-			objectsPerSecond = 0;
-			
-			initialized = true;
-			
-			return false;
-			
-		} else {
-			long currentTimestamp;
-			long duration;
-			
-			// Calculate the time since the last update.
-			currentTimestamp = System.currentTimeMillis();
-			duration = currentTimestamp - lastUpdateTimestamp;
-			
-			// Increment the processed object count.
-			objectCount++;
-			
-			if (duration > interval || duration < 0) {
-				lastUpdateTimestamp = currentTimestamp;
-				
-				// Calculate the number of objects processed per second.
-				objectsPerSecond = (double) objectCount * 1000 / duration;
-				
-				// Reset the object count.
-				objectCount = 0;
-				
-				return true;
-				
-			} else {
-				return false;
-			}
-		}
-	}
-	
-	
-	/**
-	 * Provides the number of objects processed per second. This only becomes
-	 * valid after updateRequired returns true for the first time.
-	 * 
-	 * @return The number of objects processed per second in the last timing
-	 *         interval.
-	 */
-	public double getObjectsPerSecond() {
-		return objectsPerSecond;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java b/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java
deleted file mode 100644
index 4dd05d1..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java
+++ /dev/null
@@ -1,316 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.report.v0_6;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink that analyses the data sent to it and provides a simple
- * report.
- * 
- * @author Brett Henderson
- */
-public class EntityReporter implements Sink {
-	
-	private static final int COLUMN_WIDTH_USER_NAME = 50;
-	private static final int COLUMN_WIDTH_NODE_COUNT = 7;
-	private static final int COLUMN_WIDTH_WAY_COUNT = 7;
-	private static final int COLUMN_WIDTH_RELATION_COUNT = 7;
-	
-	private Logger log = Logger.getLogger(EntityReporter.class.getName());
-	
-	private File file;
-	private FileWriter fileWriter;
-	private Map<String, UserStatistics> userMap;
-	private UserStatistics anonymousUser;
-	private UserStatistics totalUser;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to write.
-	 */
-	public EntityReporter(File file) {
-		this.file = file;
-		
-		userMap = new HashMap<String, UserStatistics>();
-		anonymousUser = new UserStatistics("anonymous");
-		totalUser = new UserStatistics("Total");
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		String userName;
-		final UserStatistics user;
-		
-		// Obtain the user statistics object.
-		userName = entityContainer.getEntity().getUser().getName();
-		if (userName != null && userName.length() > 0) {
-			if (userMap.containsKey(userName)) {
-				user = userMap.get(userName);
-			} else {
-				user = new UserStatistics(userName);
-				userMap.put(userName, user);
-			}
-		} else {
-			user = anonymousUser;
-		}
-		
-		// Increment the relevant user statistic.
-		entityContainer.process(
-			new EntityProcessor() {
-				private UserStatistics processorUser = user;
-				
-				public void process(BoundContainer bound) {
-					// Do nothing.
-				}
-				
-				public void process(NodeContainer node) {
-					processorUser.incrementNodeCount();
-					totalUser.incrementNodeCount();
-				}
-				
-				public void process(WayContainer way) {
-					processorUser.incrementWayCount();
-					totalUser.incrementWayCount();
-				}
-				
-				public void process(RelationContainer relation) {
-					processorUser.incrementRelationCount();
-					totalUser.incrementRelationCount();
-				}
-			}
-		);
-	}
-	
-	
-	/**
-	 * Writes a single value and pads it out to the correct column width.
-	 * 
-	 * @param writer
-	 *            The report destination.
-	 * @param data
-	 *            The data to be written.
-	 * @param columnWidth
-	 *            The width of the column.
-	 */
-	private void writeColumnValue(BufferedWriter writer, String data, int columnWidth) throws IOException {
-		int padLength;
-		
-		// Calculate the required data padding. The total column width is the
-		// specified column width plus one space.
-		padLength = columnWidth - data.length() + 1;
-		if (padLength < 1) {
-			padLength = 1;
-		}
-		
-		writer.write(data);
-		for (int i = 0; i < padLength; i++) {
-			writer.write(' ');
-		}
-	}
-	
-	
-	/**
-	 * Writes a single line summary of the user statistics.
-	 * 
-	 * @param writer
-	 *            The report destination.
-	 * @param userStatistics
-	 *            The user to report on.
-	 */
-	private void writeUserLine(BufferedWriter writer, UserStatistics userStatistics) throws IOException {
-		writeColumnValue(writer, userStatistics.getUserName(), COLUMN_WIDTH_USER_NAME);
-		writeColumnValue(writer, Integer.toString(userStatistics.getNodeCount()), COLUMN_WIDTH_NODE_COUNT);
-		writeColumnValue(writer, Integer.toString(userStatistics.getWayCount()), COLUMN_WIDTH_WAY_COUNT);
-		writeColumnValue(writer, Integer.toString(userStatistics.getRelationCount()), COLUMN_WIDTH_RELATION_COUNT);
-		writer.newLine();
-	}
-	
-	
-	/**
-	 * Add user information to the report file.
-	 * 
-	 * @param writer
-	 *            The report destination.
-	 */
-	private void writeUserReport(BufferedWriter writer) throws IOException {
-		List<UserStatistics> userList;
-		
-		// Sort the user statistics by user id.
-		userList = new ArrayList<UserStatistics>(userMap.values());
-		Collections.sort(
-			userList,
-			new Comparator<UserStatistics>() {
-				public int compare(UserStatistics o1, UserStatistics o2) {
-					return o1.getUserName().compareTo(o2.getUserName());
-				}
-			}
-		);
-		
-		writer.write("********** User Report **********");
-		writer.newLine();
-		writeColumnValue(writer, "USER NAME", COLUMN_WIDTH_USER_NAME);
-		writeColumnValue(writer, "NODES", COLUMN_WIDTH_NODE_COUNT);
-		writeColumnValue(writer, "WAYS", COLUMN_WIDTH_WAY_COUNT);
-		writeColumnValue(writer, "RELNS", COLUMN_WIDTH_RELATION_COUNT);
-		writer.newLine();
-		writeUserLine(writer, anonymousUser);
-		for (UserStatistics userStatistics : userList) {
-			writeUserLine(writer, userStatistics);
-		}
-		writer.newLine();
-		writeUserLine(writer, totalUser);
-	}
-	
-	
-	/**
-	 * Flushes all changes to file.
-	 */
-	public void complete() {
-		try {
-			BufferedWriter writer;
-			
-			fileWriter = new FileWriter(file);
-			writer = new BufferedWriter(fileWriter);
-			
-			// Produce a report on the user statistics.
-			writeUserReport(writer);
-			
-			writer.close();
-			fileWriter = null;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to write report to file " + file + ".");
-		}
-	}
-	
-	
-	/**
-	 * Cleans up any open file handles.
-	 */
-	public void release() {
-		if (fileWriter != null) {
-			try {
-				fileWriter.close();
-			} catch (IOException e) {
-				log.log(Level.SEVERE, "Unable to close file writer for file " + file + ".", e);
-			} finally {
-				fileWriter = null;
-			}
-		}
-	}
-	
-	
-	/**
-	 * A class holding the summary information for a single user.
-	 */
-	private static class UserStatistics {
-		
-		private String userName;
-		private int nodeCount;
-		private int wayCount;
-		private int relationCount;
-		
-		
-		/**
-		 * Creates a new instance.
-		 * 
-		 * @param userName
-		 *            The name of the user that this statistics record relates
-		 *            to.
-		 */
-		public UserStatistics(String userName) {
-			this.userName = userName;
-		}
-		
-		
-		/**
-		 * Increments the node count by one.
-		 */
-		public void incrementNodeCount() {
-			nodeCount++;
-		}
-		
-		
-		/**
-		 * Increments the way count by one.
-		 */
-		public void incrementWayCount() {
-			wayCount++;
-		}
-		
-		
-		/**
-		 * Increments the relation count by one.
-		 */
-		public void incrementRelationCount() {
-			relationCount++;
-		}
-		
-		
-		/**
-		 * Returns the name of the user for which this object contains data.
-		 * 
-		 * @return The user name.
-		 */
-		public String getUserName() {
-			return userName;
-		}
-		
-		
-		/**
-		 * Returns the node count.
-		 * 
-		 * @return The node count.
-		 */
-		public int getNodeCount() {
-			return nodeCount;
-		}
-		
-		
-		/**
-		 * Returns the way count.
-		 * 
-		 * @return The way count.
-		 */
-		public int getWayCount() {
-			return wayCount;
-		}
-		
-		
-		/**
-		 * Returns the relation count.
-		 * 
-		 * @return The relation count.
-		 */
-		public int getRelationCount() {
-			return relationCount;
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java b/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java
deleted file mode 100644
index 4cc2110..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java
+++ /dev/null
@@ -1,241 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.report.v0_6;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.filter.common.BitSetIdTracker;
-import org.openstreetmap.osmosis.core.filter.common.IdTracker;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * A sink that verifies the referential integrity of all data passing through
- * it.
- * 
- * @author Brett Henderson
- */
-public class IntegrityReporter implements Sink, EntityProcessor {
-	
-	private static final Logger LOG = Logger.getLogger(IntegrityReporter.class.getName());
-	
-	private File file;
-	private boolean initialized;
-	private BufferedWriter writer;
-	private IdTracker nodeBitSet;
-	private IdTracker wayBitSet;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to write.
-	 */
-	public IntegrityReporter(File file) {
-		this.file = file;
-		
-		initialized = false;
-		nodeBitSet = new BitSetIdTracker();
-		wayBitSet = new BitSetIdTracker();
-	}
-	
-	
-	/**
-	 * Writes data to the output file.
-	 * 
-	 * @param data
-	 *            The data to be written.
-	 */
-	private void write(String data) {
-		try {
-			writer.write(data);
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to write data.", e);
-		}
-	}
-	
-	
-	/**
-	 * Writes a new line in the output file.
-	 */
-	private void writeNewLine() {
-		try {
-			writer.newLine();
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to write data.", e);
-		}
-	}
-	
-	
-	/**
-	 * Initialises the output file for writing. This must be called by
-	 * sub-classes before any writing is performed. This method may be called
-	 * multiple times without adverse affect allowing sub-classes to invoke it
-	 * every time they perform processing.
-	 */
-	protected void initialize() {
-		if (!initialized) {
-			OutputStream outStream = null;
-			
-			try {
-				outStream = new FileOutputStream(file);
-				
-				writer = new BufferedWriter(new OutputStreamWriter(outStream, "UTF-8"));
-				
-				outStream = null;
-				
-			} catch (IOException e) {
-				throw new OsmosisRuntimeException("Unable to open file " + file + " for writing.", e);
-			} finally {
-				if (outStream != null) {
-					try {
-						outStream.close();
-					} catch (Exception e) {
-						LOG.log(Level.SEVERE, "Unable to close output stream for file " + file + ".", e);
-					}
-					outStream = null;
-				}
-			}
-			
-			initialized = true;
-			
-			write("Entity Type, Entity Id, Referred Type, Referred Id");
-			writeNewLine();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-    public void process(BoundContainer bound) {
-	    // Do nothing.
-    }
-
-
-    /**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer node) {
-		nodeBitSet.set(node.getEntity().getId());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		Way way;
-		
-		way = wayContainer.getEntity();
-		
-		wayBitSet.set(way.getId());
-		
-		for (WayNode wayNode : way.getWayNodes()) {
-			if (!nodeBitSet.get(wayNode.getNodeId())) {
-				initialize();
-				
-				write("Way," + way.getId() + ",Node," + wayNode.getNodeId());
-				writeNewLine();
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		Relation relation;
-		
-		relation = relationContainer.getEntity();
-		
-		for (RelationMember relationMember : relation.getMembers()) {
-			EntityType memberType;
-			
-			memberType = relationMember.getMemberType();
-			
-			if (EntityType.Node.equals(memberType)) {
-				if (!nodeBitSet.get(relationMember.getMemberId())) {
-					initialize();
-					
-					write("Relation," + relation.getId() + ",Node," + relationMember.getMemberId());
-					writeNewLine();
-				}
-			} else if (EntityType.Way.equals(memberType)) {
-				if (!wayBitSet.get(relationMember.getMemberId())) {
-					initialize();
-					
-					write("Relation," + relation.getId() + ",Way," + relationMember.getMemberId());
-					writeNewLine();
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes all changes to file.
-	 */
-	public void complete() {
-		try {
-			if (writer != null) {
-				writer.close();
-			}
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to complete writing to the file " + file + ".", e);
-		} finally {
-			initialized = false;
-			writer = null;
-		}
-	}
-	
-	
-	/**
-	 * Cleans up any open file handles.
-	 */
-	public void release() {
-		try {
-			try {
-				if (writer != null) {
-					writer.close();
-				}
-			} catch (IOException e) {
-				LOG.log(Level.SEVERE, "Unable to close writer.", e);
-			}
-		} finally {
-			initialized = false;
-			writer = null;
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java
deleted file mode 100644
index 888687d..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java
+++ /dev/null
@@ -1,287 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.common;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.lifecycle.Releasable;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.ChunkedObjectStore;
-import org.openstreetmap.osmosis.core.store.ObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.PersistentIterator;
-import org.openstreetmap.osmosis.core.store.Storeable;
-
-
-/**
- * Allows a large number of objects to be sorted by writing them all to disk
- * then sorting using a merge sort algorithm.
- * 
- * @param <T>
- *            The object type to be sorted.
- * @author Brett Henderson
- */
-public class FileBasedSort<T extends Storeable> implements Releasable {
-	/**
-	 * The maximum number of entities to perform memory-based sorting on,
-	 * amounts larger than this will be split into chunks of this size, the
-	 * chunks sorted in memory before writing to file, and all the results
-	 * merged using the merge sort algorithm.
-	 */
-	private static final int MAX_MEMORY_SORT_COUNT = 16384;
-	
-	/**
-	 * The maximum number of sources to merge together at a single level of the
-	 * merge sort hierarchy. Must be 2 or higher. A standard merge sort is 2.
-	 */
-	private static final int MAX_MERGE_SOURCE_COUNT = 2;
-	
-	
-	/**
-	 * The number of levels in the merge sort hierarchy to perform in memory
-	 * before persisting to a file. By persisting to file at regular hierarchy
-	 * levels, the number of file handles is minimised. File handle count is
-	 * likely to be an issue before memory usage due to the small number of
-	 * in-flight objects at any point in time.
-	 * <p>
-	 * The number of file handles will be MAX_MERGE_SOURCE_COUNT raised to the
-	 * power of MAX_MEMORY_SORT_DEPTH.
-	 */
-	private static final int MAX_MEMORY_SORT_DEPTH = 8;
-	
-
-	private ObjectSerializationFactory serializationFactory;
-	private Comparator<T> comparator;
-	private ChunkedObjectStore<T> chunkedEntityStore;
-	private List<T> addBuffer;
-	private boolean useCompression;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param serializationFactory
-	 *            The factory defining the object serialisation implementation.
-	 * @param comparator
-	 *            The comparator to be used for sorting the results.
-	 * @param useCompression
-	 *            If true, the storage files will be compressed.
-	 */
-	public FileBasedSort(
-			ObjectSerializationFactory serializationFactory, Comparator<T> comparator, boolean useCompression) {
-		this.serializationFactory = serializationFactory;
-		this.comparator = comparator;
-		this.useCompression = useCompression;
-		
-		chunkedEntityStore = new ChunkedObjectStore<T>(serializationFactory, "emta", "idx", useCompression);
-		addBuffer = new ArrayList<T>(MAX_MEMORY_SORT_COUNT);
-	}
-	
-	
-	/**
-	 * Sorts the data currently in the add buffer, writes it to the object
-	 * store, and clears the buffer.
-	 */
-	private void flushAddBuffer() {
-		if (addBuffer.size() >= 0) {
-			// Sort the chunk prior to writing.
-			Collections.sort(addBuffer, comparator);
-			
-			// Write all entities in the buffer to entity storage.
-			for (T entity : addBuffer) {
-				chunkedEntityStore.add(entity);
-			}
-			
-			addBuffer.clear();
-			
-			// Close the chunk in the underlying data store so that it can be
-			// read separately.
-			chunkedEntityStore.closeChunk();
-		}
-	}
-	
-	
-	/**
-	 * Adds a new object to be sorted.
-	 * 
-	 * @param value
-	 *            The data object.
-	 */
-	public void add(T value) {
-		// Add the new data entity to the add buffer.
-		addBuffer.add(value);
-		
-		// If the add buffer is full, it must be sorted and written to entity
-		// storage.
-		if (addBuffer.size() >= MAX_MEMORY_SORT_COUNT) {
-			flushAddBuffer();
-		}
-	}
-	
-	
-	/**
-	 * This is a wrapper method around the iterate method with the same argument
-	 * list that persists the sort results prior to returning. This forces all
-	 * sorting by nested recursive method calls to be performed allowing all
-	 * associated memory can be freed.
-	 * 
-	 * @param nestLevel
-	 *            The current recursive nesting level of the merge sort
-	 *            operation.
-	 * @param beginChunkIndex
-	 *            The initial chunk to begin sorting from.
-	 * @param chunkCount
-	 *            The number of chunks to sort.
-	 * @return An iterator providing access to the sort result.
-	 */
-	private ReleasableIterator<T> iteratePersisted(int nestLevel, long beginChunkIndex, long chunkCount) {
-		ReleasableIterator<T> persistentIterator;
-		
-		// Create a persistent iterator based on the requested underlying chunk
-		// iterator.
-		persistentIterator = new PersistentIterator<T>(
-			serializationFactory,
-			iterate(nestLevel, beginChunkIndex, chunkCount),
-			"emtb",
-			useCompression
-		);
-		
-		// Prime the persistent iterator so that all underlying iterator data is
-		// written to file.
-		try {
-			ReleasableIterator<T> result;
-			
-			result = persistentIterator;
-			
-			// This will cause all data to be read from the underlying iterator
-			// into the persistent store.
-			persistentIterator.hasNext();
-			
-			persistentIterator = null;
-			
-			return result;
-			
-		} finally {
-			// This will release the persistent iterator and its underlying
-			// source iterator if the persistence operations failed.
-			if (persistentIterator != null) {
-				persistentIterator.release();
-			}
-		}
-	}
-	
-	
-	/**
-	 * Sorts the specified sub-section of the overall storage contents. This
-	 * result list is not backed by a file and should be persisted prior to
-	 * being incorporated into a higher level merge operation.
-	 * 
-	 * @param nestLevel
-	 *            The current recursive nesting level of the merge sort
-	 *            operation.
-	 * @param beginChunkIndex
-	 *            The initial chunk to begin sorting from.
-	 * @param chunkCount
-	 *            The number of chunks to sort.
-	 * @return An iterator providing access to the sort result.
-	 */
-	private ReleasableIterator<T> iterate(int nestLevel, long beginChunkIndex, long chunkCount) {
-		List<ReleasableIterator<T>> sources;
-		
-		sources = new ArrayList<ReleasableIterator<T>>();
-		
-		try {
-			MergingIterator<T> mergingIterator;
-			
-			// If we are down to a small number of entities, we retrieve each source from file.
-			// Otherwise we recurse and split the number of entities down into smaller chunks.
-			if (chunkCount <= MAX_MERGE_SOURCE_COUNT) {
-				for (int i = 0; i < chunkCount; i++) {
-					sources.add(
-						chunkedEntityStore.iterate(beginChunkIndex + i)
-					);
-				}
-				
-			} else {
-				long maxChunkIndex;
-				long subChunkCount;
-				
-				// The current chunk count must be divided by
-				// MAX_FILE_SORT_COUNT and we must recurse for each
-				// of those sub chunk counts.
-				subChunkCount = chunkCount / MAX_MERGE_SOURCE_COUNT;
-				
-				// If the sub chunk count is now less than MAX_FILE_SORT_COUNT,
-				// increase it to that value to minimise the number of files
-				// created.
-				if (subChunkCount < MAX_MERGE_SOURCE_COUNT) {
-					subChunkCount = MAX_MERGE_SOURCE_COUNT;
-				}
-				
-				// We can never pass beyond the chunk boundaries specified for
-				// this function.
-				maxChunkIndex = beginChunkIndex + chunkCount;
-				
-				for (
-						long subFirstChunk = beginChunkIndex;
-						subFirstChunk < maxChunkIndex;
-						subFirstChunk += subChunkCount) {
-					
-					// The chunk count passed to the nested function should not
-					// make the nested function exceed this function's boundaries.
-					if (subFirstChunk + subChunkCount > maxChunkIndex) {
-						subChunkCount = maxChunkIndex - subFirstChunk;
-					}
-					
-					// Either call the persistent or standard version of the
-					// recursive iterate based on whether this nesting level
-					// requires persistence.
-					if (((nestLevel + 1) % MAX_MEMORY_SORT_DEPTH) == 0) {
-						sources.add(
-							iteratePersisted(nestLevel + 1, subFirstChunk, subChunkCount)
-						);
-					} else {
-						sources.add(
-							iterate(nestLevel + 1, subFirstChunk, subChunkCount)
-						);
-					}
-				}
-			}
-			
-			// Create a merging iterator to merge all of the sources.
-			mergingIterator = new MergingIterator<T>(sources, comparator);
-			
-			// The merging iterator owns the sources now, so we clear our copy
-			// of them to prevent them being released on method exit.
-			sources.clear();
-			
-			return mergingIterator;
-			
-		} finally {
-			for (ReleasableIterator<T> source : sources) {
-				source.release();
-			}
-		}
-	}
-	
-	
-	/**
-	 * Sorts and returns the contents of the sorter.
-	 * 
-	 * @return An iterator providing access to the sorted entities.
-	 */
-	public ReleasableIterator<T> iterate() {
-		flushAddBuffer();
-		
-		return iterate(0, 0, chunkedEntityStore.getChunkCount());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		chunkedEntityStore.release();
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java
deleted file mode 100644
index da961a8..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.common;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-
-
-/**
- * This iterator examines a list of sorted input sources and merges them into a
- * single sorted list.
- * 
- * @param <DataType>
- *            The object type to be sorted.
- * @author Brett Henderson
- */
-public class MergingIterator<DataType> implements ReleasableIterator<DataType> {
-	private List<ReleasableIterator<DataType>> sources;
-	private Comparator<DataType> comparator;
-	private List<DataType> sourceData;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sources
-	 *            The list of data sources.
-	 * @param comparator
-	 *            The comparator to be used for sorting.
-	 */
-	public MergingIterator(List<ReleasableIterator<DataType>> sources, Comparator<DataType> comparator) {
-		this.sources = new ArrayList<ReleasableIterator<DataType>>(sources);
-		this.comparator = comparator;
-	}
-	
-	
-	/**
-	 * Primes the sorting collections.
-	 */
-	private void initialize() {
-		if (sourceData == null) {
-			// Get the first entity from each source.  Delete any empty sources.
-			sourceData = new ArrayList<DataType>(sources.size());
-			for (int sourceIndex = 0; sourceIndex < sources.size();) {
-				ReleasableIterator<DataType> source;
-				
-				source = sources.get(sourceIndex);
-				
-				if (source.hasNext()) {
-					sourceData.add(source.next());
-					sourceIndex++;
-				} else {
-					sources.remove(sourceIndex);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public boolean hasNext() {
-		initialize();
-		
-		return sourceData.size() > 0;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public DataType next() {
-		DataType dataMinimum;
-		int indexMinimum;
-		ReleasableIterator<DataType> source;
-		
-		initialize();
-		
-		if (!hasNext()) {
-			throw new NoSuchElementException();
-		}
-		
-		dataMinimum = sourceData.get(0);
-		indexMinimum = 0;
-		
-		// Find the minimum entity.
-		for (int indexCurrent = 1; indexCurrent < sources.size(); indexCurrent++) {
-			DataType dataCurrent = sourceData.get(indexCurrent);
-			
-			// Check if the current data entity is less than the existing minimum.
-			if (comparator.compare(dataMinimum, dataCurrent) > 0) {
-				dataMinimum = dataCurrent;
-				indexMinimum = indexCurrent;
-			}
-		}
-		
-		// Get the next entity from the source if available.
-		// Otherwise remove the source and its current data.
-		source = sources.get(indexMinimum);
-		if (source.hasNext()) {
-			sourceData.set(indexMinimum, source.next());
-		} else {
-			sources.remove(indexMinimum).release();
-			sourceData.remove(indexMinimum);
-		}
-		
-		return dataMinimum;
-	}
-	
-	
-	/**
-	 * Not supported. An UnsupportedOperationException is always thrown.
-	 */
-	public void remove() {
-		throw new UnsupportedOperationException();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		for (ReleasableIterator<DataType> source : sources) {
-			source.release();
-		}
-	}
-
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java
deleted file mode 100644
index b45507b..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.Comparator;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * A change stream filter that sorts changes. The sort order is specified by
- * comparator provided during instantiation.
- * 
- * @author Brett Henderson
- */
-public class ChangeSorter implements ChangeSinkChangeSource {
-	private FileBasedSort<ChangeContainer> fileBasedSort;
-	private ChangeSink changeSink;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param comparator
-	 *            The comparator to use for sorting.
-	 */
-	public ChangeSorter(Comparator<ChangeContainer> comparator) {
-		fileBasedSort =
-			new FileBasedSort<ChangeContainer>(
-					new SingleClassObjectSerializationFactory(ChangeContainer.class), comparator, true);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer change) {
-		fileBasedSort.add(change);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		ReleasableIterator<ChangeContainer> iterator = null;
-		
-		try {
-			iterator = fileBasedSort.iterate();
-			
-			while (iterator.hasNext()) {
-				changeSink.process(iterator.next());
-			}
-			
-			changeSink.complete();
-		} finally {
-			if (iterator != null) {
-				iterator.release();
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		fileBasedSort.release();
-		changeSink.release();
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java
deleted file mode 100644
index fb8ad48..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * A data stream filter that sorts tags on changes. This is useful for testing
- * to allow two sets of data to be compared for equality.
- * 
- * @author Brett Henderson
- */
-public class ChangeTagSorter implements ChangeSinkChangeSource {
-	private ChangeSink changeSink;
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		EntityContainer readOnlyContainer;
-		EntityContainer writeableContainer;
-		Entity entity;
-		Collection<Tag> sortedTags;
-		
-		readOnlyContainer = changeContainer.getEntityContainer();
-		writeableContainer = readOnlyContainer.getWriteableInstance();
-		
-		entity = writeableContainer.getEntity();
-		sortedTags = sortTags(entity.getTags());
-		entity.getTags().clear();
-		entity.getTags().addAll(sortedTags);
-		
-		changeSink.process(new ChangeContainer(writeableContainer, changeContainer.getAction()));
-	}
-
-
-	/**
-	 * Sorts the specified tag list.
-	 * 
-	 * @param tagList
-	 *            The tag list to be sorted.
-	 * @return A new list containing the sorted tags.
-	 */
-	private List<Tag> sortTags(Collection<Tag> tagList) {
-		List<Tag> sortedTagList;
-		
-		sortedTagList = new ArrayList<Tag>(tagList);
-		Collections.sort(sortedTagList);
-		
-		return sortedTagList;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		changeSink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeSink.release();
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java
deleted file mode 100644
index 24d0af4..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.Comparator;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
-import org.openstreetmap.osmosis.core.store.GenericObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A data stream filter that sorts entities. The sort order is specified by
- * comparator provided during instantiation.
- * 
- * @author Brett Henderson
- */
-public class EntitySorter implements SinkSource {
-	private FileBasedSort<EntityContainer> fileBasedSort;
-	private Sink sink;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param comparator
-	 *            The comparator to use for sorting.
-	 */
-	public EntitySorter(Comparator<EntityContainer> comparator) {
-		fileBasedSort = new FileBasedSort<EntityContainer>(new GenericObjectSerializationFactory(), comparator, true);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		fileBasedSort.add(entityContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		ReleasableIterator<EntityContainer> iterator = null;
-		
-		try {
-			iterator = fileBasedSort.iterate();
-			
-			while (iterator.hasNext()) {
-				sink.process(iterator.next());
-			}
-			
-			sink.complete();
-		} finally {
-			if (iterator != null) {
-				iterator.release();
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		fileBasedSort.release();
-		sink.release();
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java
deleted file mode 100644
index ad574e6..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.Comparator;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * Validates that change data in a pipeline is sorted by entity type then id thus only allowing
- * delta style changes (ie. not full history). It accepts input data from a Source and passes all
- * data to a downstream Sink.
- * 
- * @author Brett Henderson
- */
-public class SortedDeltaChangePipeValidator implements ChangeSinkChangeSource {
-	private ChangeSink changeSink;
-	private Comparator<ChangeContainer> comparator;
-	private ChangeContainer previousChangeContainer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SortedDeltaChangePipeValidator() {
-		comparator = new ChangeAsEntityComparator(new EntityContainerComparator(new EntityByTypeThenIdComparator()));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		changeSink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		// If this is not the first entity in the pipeline, make sure this
-		// entity is greater than the previous.
-		if (previousChangeContainer != null) {
-			if (comparator.compare(previousChangeContainer, changeContainer) >= 0) {
-				throw new OsmosisRuntimeException(
-					"Pipeline entities are not sorted or contain multiple versions of a single entity"
-					+ ", previous entity type=" + previousChangeContainer.getEntityContainer().getEntity().getType()
-					+ ", id=" + previousChangeContainer.getEntityContainer().getEntity().getId()
-					+ ", version=" + previousChangeContainer.getEntityContainer().getEntity().getVersion()
-					+ " current entity type=" + changeContainer.getEntityContainer().getEntity().getType()
-					+ ", id=" + changeContainer.getEntityContainer().getEntity().getId() 
-					+ ", version=" + changeContainer.getEntityContainer().getEntity().getVersion() + "."
-				);
-			}
-		}
-		
-		changeSink.process(changeContainer);
-		
-		previousChangeContainer = changeContainer;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeSink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java
deleted file mode 100644
index 03351dc..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Validates that entity data in a pipeline is sorted by entity type then id. It
- * allows duplicates. It accepts input data from a Source and passes all data to
- * a downstream Sink.
- * 
- * @author Brett Henderson
- */
-public class SortedDuplicateEntityPipeValidator implements SinkSource {
-	private Sink sink;
-	private EntityContainerComparator comparator;
-	private EntityContainer previousEntityContainer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SortedDuplicateEntityPipeValidator() {
-		comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// If this is not the first entity in the pipeline, make sure this
-		// entity is greater than the previous.
-		if (previousEntityContainer != null) {
-			if (comparator.compare(previousEntityContainer,	entityContainer) > 0) {
-				throw new OsmosisRuntimeException(
-					"Pipeline entities are not sorted, previous entity type="
-					+ previousEntityContainer.getEntity().getType() + ", id="
-					+ previousEntityContainer.getEntity().getId() + ", version="
-					+ previousEntityContainer.getEntity().getVersion() + " current entity type="
-					+ entityContainer.getEntity().getType() + ", id=" 
-                    + entityContainer.getEntity().getId() + ", version="
-                    + entityContainer.getEntity().getVersion() + "."
-				);
-			}
-		}
-		
-		sink.process(entityContainer);
-		
-		previousEntityContainer = entityContainer;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java
deleted file mode 100644
index 7ed212b..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Validates that entity data in a pipeline is sorted by entity type then id. It
- * accepts input data from a Source and passes all data to a downstream Sink.
- * 
- * @author Brett Henderson
- */
-public class SortedEntityPipeValidator implements SinkSource {
-	private Sink sink;
-	private EntityContainerComparator comparator;
-	private EntityContainer previousEntityContainer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SortedEntityPipeValidator() {
-		comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// If this is not the first entity in the pipeline, make sure this
-		// entity is greater than the previous.
-		if (previousEntityContainer != null) {
-			if (comparator.compare(previousEntityContainer,	entityContainer) >= 0) {
-				throw new OsmosisRuntimeException(
-					"Pipeline entities are not sorted, previous entity type="
-					+ previousEntityContainer.getEntity().getType() + ", id="
-					+ previousEntityContainer.getEntity().getId() + ", version="
-					+ previousEntityContainer.getEntity().getVersion() + " current entity type="
-					+ entityContainer.getEntity().getType() + ", id=" 
-                    + entityContainer.getEntity().getId() + ", version="
-                    + entityContainer.getEntity().getVersion() + "."
-				);
-			}
-		}
-		
-		sink.process(entityContainer);
-		
-		previousEntityContainer = entityContainer;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java
deleted file mode 100644
index 0bf7cd7..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.Comparator;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * Validates that change data in a pipeline is sorted by entity type, then id, then version allowing
- * full history changes. It accepts input data from a Source and passes all data to a downstream
- * Sink.
- * 
- * @author Brett Henderson
- */
-public class SortedHistoryChangePipeValidator implements ChangeSinkChangeSource {
-	private ChangeSink changeSink;
-	private Comparator<ChangeContainer> comparator;
-	private ChangeContainer previousChangeContainer;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SortedHistoryChangePipeValidator() {
-		comparator = new ChangeAsEntityComparator(new EntityContainerComparator(
-				new EntityByTypeThenIdThenVersionComparator()));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		changeSink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		// If this is not the first entity in the pipeline, make sure this
-		// entity is greater than the previous.
-		if (previousChangeContainer != null) {
-			if (comparator.compare(previousChangeContainer, changeContainer) >= 0) {
-				throw new OsmosisRuntimeException(
-					"Pipeline entities are not sorted, previous entity type="
-					+ previousChangeContainer.getEntityContainer().getEntity().getType()
-					+ ", id=" + previousChangeContainer.getEntityContainer().getEntity().getId()
-					+ ", version=" + previousChangeContainer.getEntityContainer().getEntity().getVersion()
-					+ " current entity type=" + changeContainer.getEntityContainer().getEntity().getType()
-					+ ", id=" + changeContainer.getEntityContainer().getEntity().getId() 
-					+ ", version=" + changeContainer.getEntityContainer().getEntity().getVersion() + "."
-				);
-			}
-		}
-		
-		changeSink.process(changeContainer);
-		
-		previousChangeContainer = changeContainer;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeSink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java b/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java
deleted file mode 100644
index 9743a69..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.sort.v0_6;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A data stream filter that sorts tags on entities. This is useful for testing
- * to allow two sets of data to be compared for equality.
- * 
- * @author Brett Henderson
- */
-public class TagSorter implements SinkSource {
-	private Sink sink;
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		EntityContainer writeableContainer;
-		Entity entity;
-		Collection<Tag> sortedTags;
-		
-		writeableContainer = entityContainer.getWriteableInstance();
-		
-		entity = writeableContainer.getEntity();
-		sortedTags = sortTags(entity.getTags());
-		entity.getTags().clear();
-		entity.getTags().addAll(sortedTags);
-		
-		sink.process(writeableContainer);
-	}
-
-
-	/**
-	 * Sorts the specified tag list.
-	 * 
-	 * @param tagList
-	 *            The tag list to be sorted.
-	 * @return A new list containing the sorted tags.
-	 */
-	private List<Tag> sortTags(Collection<Tag> tagList) {
-		List<Tag> sortedTagList;
-		
-		sortedTagList = new ArrayList<Tag>(tagList);
-		Collections.sort(sortedTagList);
-		
-		return sortedTagList;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java b/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java
deleted file mode 100644
index e8c2767..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java
+++ /dev/null
@@ -1,280 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.store;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Queue;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * This class provides a mechanism for a thread to pass data to another thread.
- * Both threads will block until the other is ready. It supports a single
- * writing thread, and a single reading thread. Multiple reading or writing
- * threads are NOT supported.
- * 
- * @param <T>
- *            The type of data held in the postbox.
- */
-public class DataPostbox<T> {
-	private int bufferCapacity;
-	private int chunkSize;
-	private Lock lock;
-	private Condition dataWaitCondition;
-	private Collection<T> centralQueue;
-	private Collection<T> inboundQueue;
-	private Queue<T> outboundQueue;
-	private boolean released;
-	private boolean complete;
-	private boolean outputOkay;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param capacity
-	 *            The maximum number of objects to hold in the postbox before
-	 *            blocking.
-	 */
-	public DataPostbox(int capacity) {
-		if (capacity <= 0) {
-			throw new OsmosisRuntimeException(
-				"A capacity of " + capacity
-				+ " is invalid, must be greater than 0."
-			);
-		}
-		
-		this.bufferCapacity = capacity;
-		
-		// Use a chunk size one quarter of total buffer size. This is a magic
-		// number but performance isn't highly sensitive to this parameter.
-		chunkSize = bufferCapacity / 4;
-		if (chunkSize <= 0) {
-			chunkSize = 1;
-		}
-		lock = new ReentrantLock();
-		dataWaitCondition = lock.newCondition();
-		centralQueue = new ArrayList<T>();
-		inboundQueue = new ArrayList<T>();
-		outboundQueue = new ArrayDeque<T>();
-		released = false;
-		complete = false;
-		outputOkay = true;
-	}
-	
-	
-	/**
-	 * This is called by the input thread to validate that no errors have
-	 * occurred on the output thread.
-	 */
-	private void checkForOutputErrors() {
-		// Check for reading thread error.
-		if (!outputOkay) {
-			throw new OsmosisRuntimeException("An output error has occurred, aborting.");
-		}
-	}
-	
-	
-	/**
-	 * This is called by the output thread to validate that no errors have
-	 * occurred on the input thread.
-	 */
-	private void checkForInputErrors() {
-		// Check for writing thread error.
-		if (released && (!complete)) {
-			throw new OsmosisRuntimeException("An input error has occurred, aborting.");
-		}
-	}
-	
-	
-	/**
-	 * Either thread can call this method when they wish to wait until an update
-	 * has been performed by the other thread.
-	 */
-	private void waitForUpdate() {
-		try {
-			dataWaitCondition.await();
-			
-		} catch (InterruptedException e) {
-			throw new OsmosisRuntimeException("Thread was interrupted.", e);
-		}
-	}
-	
-	
-	/**
-	 * Either thread can call this method when they wish to signal the other
-	 * thread that an update has occurred.
-	 */
-	private void signalUpdate() {
-		dataWaitCondition.signal();
-	}
-	
-	
-	/**
-	 * Adds a group of objects to the central queue ready for consumption by the
-	 * receiver.
-	 * 
-	 * @param o
-	 *            The objects to be added.
-	 */
-	private void populateCentralQueue() {
-		lock.lock();
-		
-		try {
-			checkForOutputErrors();
-			
-			// Wait until the currently posted data is cleared.
-			while (centralQueue.size() >= bufferCapacity) {
-				waitForUpdate();
-				checkForOutputErrors();
-			}
-			
-			// Post the new data.
-			centralQueue.addAll(inboundQueue);
-			inboundQueue.clear();
-			signalUpdate();
-			
-		} finally {
-			lock.unlock();
-		}
-	}
-	
-	
-	/**
-	 * Empties the contents of the central queue into the outbound queue.
-	 */
-	private void consumeCentralQueue() {
-		lock.lock();
-		
-		try {
-			checkForInputErrors();
-			
-			// Wait until data is available.
-			while (!((centralQueue.size() > 0) || complete)) {
-				waitForUpdate();
-				checkForInputErrors();
-			}
-			
-			outboundQueue.addAll(centralQueue);
-			centralQueue.clear();
-			
-			signalUpdate();
-			
-		} finally {
-			lock.unlock();
-		}
-	}
-	
-	
-	/**
-	 * Adds a new object to the postbox.
-	 * 
-	 * @param o
-	 *            The object to be added.
-	 */
-	public void put(T o) {
-		inboundQueue.add(o);
-		
-		if (inboundQueue.size() >= chunkSize) {
-			populateCentralQueue();
-		}
-	}
-	
-	
-	/**
-	 * Marks input is complete.
-	 */
-	public void complete() {
-		lock.lock();
-		
-		try {
-			populateCentralQueue();
-			
-			complete = true;
-			signalUpdate();
-			
-		} finally {
-			lock.unlock();
-		}
-	}
-	
-	
-	/**
-	 * Must be called at the end of input processing regardless of whether
-	 * errors have occurred.
-	 */
-	public void release() {
-		lock.lock();
-		
-		try {
-			released = true;
-			signalUpdate();
-			
-		} finally {
-			lock.unlock();
-		}
-	}
-	
-	
-	/**
-	 * Indicates if data is available for output. This will block until either
-	 * data is available, input processing has completed, or an input error
-	 * occurs.
-	 * 
-	 * @return True if data is available.
-	 */
-	public boolean hasNext() {
-		int queueSize;
-		
-		queueSize = outboundQueue.size();
-		
-		if (queueSize <= 0) {
-			consumeCentralQueue();
-			queueSize = outboundQueue.size();
-		}
-		
-		return queueSize > 0;
-	}
-	
-	
-	/**
-	 * Returns the next available object from the postbox. This should be
-	 * preceeded by a call to hasNext.
-	 * 
-	 * @return The next available object.
-	 */
-	public T getNext() {
-		if (hasNext()) {
-			T result;
-			
-			result = outboundQueue.remove();
-			
-			return result;
-			
-		} else {
-			throw new OsmosisRuntimeException("No data is available, should call hasNext first.");
-		}
-	}
-	
-	
-	/**
-	 * Allows an output thread to signal that it has failed, this will cause
-	 * exceptions to be thrown if more data is sent by input input threads.
-	 */
-	public void setOutputError() {
-		lock.lock();
-		
-		try {
-			outputOkay = false;
-			signalUpdate();
-			
-		} finally {
-			lock.unlock();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java b/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java
deleted file mode 100644
index 5ff41ca..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.store;
-
-import java.util.Date;
-
-import org.openstreetmap.osmosis.core.buffer.v0_6.EntityBuffer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.misc.v0_6.NullWriter;
-import org.openstreetmap.osmosis.core.progress.v0_6.EntityProgressLogger;
-
-/**
- * Very simple class for applying load to the DataPostbox class and measuring
- * performance.
- * 
- * @author Brett Henderson
- */
-public final class DataPostboxLoadInjector implements Runnable {
-
-	private EntityBuffer buffer;
-	private EntityProgressLogger progressLogger;
-	private NullWriter nullWriter;
-
-	/**
-	 * Launches the application.
-	 * 
-	 * @param args
-	 *            The program arguments.
-	 */
-	public static void main(String[] args) {
-		new DataPostboxLoadInjector().run();
-	}
-
-	
-	private DataPostboxLoadInjector() {
-		buffer = new EntityBuffer(10000);
-		progressLogger = new EntityProgressLogger(5000);
-		buffer.setSink(progressLogger);
-		nullWriter = new NullWriter();
-		progressLogger.setSink(nullWriter);
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void run() {
-		Thread t1;
-		Thread t2;
-
-		t1 = new Thread(new Writer(), "input");
-		t2 = new Thread(buffer, "output");
-
-		t1.start();
-		t2.start();
-
-		try {
-			t1.join();
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-
-		try {
-			t2.join();
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		}
-	}
-
-	private class Writer implements Runnable {
-		private Node node;
-		private NodeContainer nodeContainer;
-
-		public Writer() {
-			node = new Node(new CommonEntityData(1, 2, new Date(), OsmUser.NONE, 3), 10, 10);
-			nodeContainer = new NodeContainer(node);
-		}
-
-		public void run() {
-			while (true) {
-				buffer.process(nodeContainer);
-			}
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java b/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java
deleted file mode 100644
index 373a0b4..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.task.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.lifecycle.Completable;
-import org.openstreetmap.osmosis.core.task.common.Task;
-
-
-/**
- * Defines the interface for all tasks consuming OSM changes to data.
- * 
- * @author Brett Henderson
- */
-public interface ChangeSink extends Task, Completable {
-	
-	/**
-	 * Process the change.
-	 * 
-	 * @param change
-	 *            The change to be processed.
-	 */
-	void process(ChangeContainer change);
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java b/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java
deleted file mode 100644
index 950ce9a..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.task.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.lifecycle.Completable;
-import org.openstreetmap.osmosis.core.task.common.Task;
-
-
-/**
- * Defines the interface for tasks consuming OSM data types.
- * 
- * @author Brett Henderson
- */
-public interface Sink extends Task, Completable {
-	
-	/**
-	 * Process the entity.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	void process(EntityContainer entityContainer);
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java b/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java
deleted file mode 100644
index 284c6dd..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.tee.v0_6;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkMultiChangeSource;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSource;
-
-
-/**
- * Sends input change data to two output destinations.
- * 
- * @author Brett Henderson
- */
-public class ChangeTee implements ChangeSinkMultiChangeSource {
-	
-	private List<ProxyChangeSinkChangeSource> sinkList;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param outputCount
-	 *            The number of output destinations to write to.
-	 */
-	public ChangeTee(int outputCount) {
-		sinkList = new ArrayList<ProxyChangeSinkChangeSource>();
-		
-		for (int i = 0; i < outputCount; i++) {
-			sinkList.add(new ProxyChangeSinkChangeSource());
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public ChangeSource getChangeSource(int index) {
-		if (index < 0 || index >= sinkList.size()) {
-			throw new OsmosisRuntimeException("Source index " + index
-					+ " is in the range 0 to " + (sinkList.size() - 1) + ".");
-		}
-		
-		return sinkList.get(index);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public int getChangeSourceCount() {
-		return sinkList.size();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer change) {
-		for (ProxyChangeSinkChangeSource sink : sinkList) {
-			// We're passing the data to multiple downstream tasks therefore should make the entity
-			// read-only to prevent multiple threads impacting each other.
-			change.getEntityContainer().getEntity().makeReadOnly();
-			
-			sink.process(change);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		for (ProxyChangeSinkChangeSource sink : sinkList) {
-			sink.complete();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		for (ProxyChangeSinkChangeSource sink : sinkList) {
-			sink.release();
-		}
-	}
-	
-	
-	/**
-	 * Instances of this class are returned via the parent class getSource method.
-	 * 
-	 * @author Brett Henderson
-	 */
-	private static class ProxyChangeSinkChangeSource implements ChangeSinkChangeSource {
-		private ChangeSink changeSink;
-		
-		
-		/**
-		 * Creates a new instance.
-		 */
-		public ProxyChangeSinkChangeSource() {
-			// Nothing to do.
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void setChangeSink(ChangeSink changeSink) {
-			this.changeSink = changeSink;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(ChangeContainer change) {
-			changeSink.process(change);
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void complete() {
-			changeSink.complete();
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void release() {
-			changeSink.release();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java b/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java
deleted file mode 100644
index ad013fa..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.tee.v0_6;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkMultiSource;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Source;
-
-
-/**
- * Sends input data to two output destinations.
- * 
- * @author Brett Henderson
- */
-public class EntityTee implements SinkMultiSource {
-	
-	private List<ProxySinkSource> sinkList;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param outputCount
-	 *            The number of output destinations to write to.
-	 */
-	public EntityTee(int outputCount) {
-		sinkList = new ArrayList<ProxySinkSource>();
-		
-		for (int i = 0; i < outputCount; i++) {
-			sinkList.add(new ProxySinkSource());
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public Source getSource(int index) {
-		if (index < 0 || index >= sinkList.size()) {
-			throw new OsmosisRuntimeException("Source index " + index
-					+ " is in the range 0 to " + (sinkList.size() - 1) + ".");
-		}
-		
-		return sinkList.get(index);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public int getSourceCount() {
-		return sinkList.size();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		for (ProxySinkSource sink : sinkList) {
-			// We're passing the data to multiple downstream tasks therefore should make the entity
-			// read-only to prevent multiple threads impacting each other.
-			entityContainer.getEntity().makeReadOnly();
-			
-			sink.process(entityContainer);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		for (ProxySinkSource sink : sinkList) {
-			sink.complete();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		for (ProxySinkSource sink : sinkList) {
-			sink.release();
-		}
-	}
-	
-	
-	/**
-	 * Instances of this class are returned via the parent class getSource method.
-	 * 
-	 * @author Brett Henderson
-	 */
-	private static class ProxySinkSource implements SinkSource {
-		private Sink sink;
-		
-		
-		/**
-		 * Creates a new instance.
-		 */
-		public ProxySinkSource() {
-			// Nothing to do.
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void setSink(Sink sink) {
-			this.sink = sink;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(EntityContainer entityContainer) {
-			sink.process(entityContainer);
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void complete() {
-			sink.complete();
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void release() {
-			sink.release();
-		}		
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java b/core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java
deleted file mode 100644
index ac7e601..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.util;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.lifecycle.Releasable;
-
-
-/**
- * This class provides a mechanism to use simple files as locks to prevent
- * multiple threads or processes from updating common files.
- * 
- * @author Brett Henderson
- */
-public class FileBasedLock implements Releasable {
-	
-	private static final Logger LOG = Logger.getLogger(FileBasedLock.class.getName());
-	
-	private File lockFile;
-	private FileOutputStream outputStream;
-	private FileChannel fileChannel;
-	private FileLock fileLock;
-	private boolean initialized;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param lockFile
-	 *            The file to use for locking.
-	 */
-	public FileBasedLock(File lockFile) {
-		this.lockFile = lockFile;
-		
-		initialized = false;
-	}
-	
-	
-	/**
-	 * Creates the file resources used internally for implementing the lock.
-	 */
-	private void initialize() {
-		if (!initialized) {
-			try {
-				outputStream = new FileOutputStream(lockFile);
-			} catch (IOException e) {
-				throw new OsmosisRuntimeException("Unable to open lock file " + lockFile + ".");
-			}
-			
-			fileChannel = outputStream.getChannel();
-			
-			initialized = true;
-		}
-	}
-	
-	
-	/**
-	 * Obtain an exclusive lock. This will fail if another thread or process
-	 * already has a lock.
-	 */
-	public void lock() {
-		initialize();
-		
-		if (fileLock != null) {
-			throw new OsmosisRuntimeException("A lock has already been obtained on file " + lockFile + ".");
-		}
-		
-		try {
-			fileLock = fileChannel.tryLock();
-			
-			if (fileLock == null) {
-				throw new OsmosisRuntimeException("A exclusive lock already exists on file " + lockFile + ".");
-			}
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException(
-					"An error occurred while trying to obtain an exclusive lock on file " + lockFile + ".");
-		}
-	}
-	
-	
-	/**
-	 * Release the lock.
-	 */
-	public void unlock() {
-		initialize();
-		
-		try {
-			fileLock.release();
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to release lock on file " + lockFile + ".");
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		if (outputStream != null) {
-			try {
-				outputStream.close();
-			} catch (Exception e) {
-				LOG.warning("Unable to close lock stream on file " + lockFile + ".");
-			} finally {
-				outputStream = null;
-				fileChannel = null;
-				fileLock = null;
-				initialized = false;
-			}
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java b/core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java
deleted file mode 100644
index 9dc3ebb..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java
+++ /dev/null
@@ -1,180 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.util;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * This map uses a HashMap internally, but only instantiates it after values are
- * added. This is intended for cases where most instances of the map will remain
- * empty and avoids the instantiation overhead of creating the full map.
- * 
- * @author Brett Henderson
- * 
- * @param <K>
- *            the type of keys maintained by this map
- * @param <V>
- *            the type of mapped values
- */
-public class LazyHashMap<K, V> implements Map<K, V> {
-
-	private Map<K, V> internalMap;
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void clear() {
-		if (internalMap != null) {
-			internalMap.clear();
-		}
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean containsKey(Object key) {
-		if (internalMap != null) {
-			return internalMap.containsKey(key);
-		} else {
-			return false;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean containsValue(Object value) {
-		if (internalMap != null) {
-			return internalMap.containsValue(value);
-		} else {
-			return false;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Set<Map.Entry<K, V>> entrySet() {
-		if (internalMap != null) {
-			return internalMap.entrySet();
-		} else {
-			return Collections.<K, V>emptyMap().entrySet();
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public V get(Object key) {
-		if (internalMap != null) {
-			return internalMap.get(key);
-		} else {
-			return null;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean isEmpty() {
-		if (internalMap != null) {
-			return internalMap.isEmpty();
-		} else {
-			return true;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Set<K> keySet() {
-		if (internalMap != null) {
-			return internalMap.keySet();
-		} else {
-			return Collections.<K, V>emptyMap().keySet();
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public V put(K key, V value) {
-		if (internalMap == null) {
-			internalMap = new HashMap<K, V>();
-		}
-		
-		return internalMap.put(key, value);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void putAll(Map<? extends K, ? extends V> m) {
-		if (internalMap == null) {
-			internalMap = new HashMap<K, V>();
-		}
-		
-		internalMap.putAll(m);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public V remove(Object key) {
-		if (internalMap != null) {
-			return internalMap.remove(key);
-		} else {
-			return null;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int size() {
-		if (internalMap != null) {
-			return internalMap.size();
-		} else {
-			return 0;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Collection<V> values() {
-		if (internalMap != null) {
-			return internalMap.values();
-		} else {
-			return Collections.<K, V>emptyMap().values();
-		}
-	}
-}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java b/core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java
deleted file mode 100644
index 7cbb7b3..0000000
--- a/core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java
+++ /dev/null
@@ -1,128 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.util;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * Allows Properties objects to be loaded and stored to file.
- */
-public class PropertiesPersister {
-	
-	private static final Logger LOG = Logger.getLogger(PropertiesPersister.class.getName());
-	
-	
-	private AtomicFileCreator atomicFileCreator;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param propertiesFile
-	 *            The location of the file containing the persisted data.
-	 */
-	public PropertiesPersister(File propertiesFile) {
-		atomicFileCreator = new AtomicFileCreator(propertiesFile);
-	}
-	
-	
-	/**
-	 * Loads the properties from the file.
-	 * 
-	 * @return The properties.
-	 */
-	public Properties load() {
-
-		FileInputStream fileInputStream = null;
-		
-		try {
-			Reader reader;
-			Properties properties;
-			
-			fileInputStream = new FileInputStream(atomicFileCreator.getFile());
-			reader = new InputStreamReader(new BufferedInputStream(fileInputStream), Charset.forName("UTF-8"));
-			
-			properties = new Properties();
-			properties.load(reader);
-			
-			fileInputStream.close();
-			fileInputStream = null;
-			
-			return properties;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read the properties from file " + atomicFileCreator.getFile()
-					+ ".", e);
-		} finally {
-			if (fileInputStream != null) {
-				try {
-					fileInputStream.close();
-				} catch (Exception e) {
-					LOG.log(Level.WARNING, "Unable to close properties file " + atomicFileCreator.getFile() + ".", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Stores the properties to the file overwriting any existing file contents.
-	 * 
-	 * @param properties
-	 *            The properties.
-	 */
-	public void store(Properties properties) {
-		FileOutputStream fileOutputStream = null;
-		
-		try {
-			Writer writer;
-			
-			fileOutputStream = new FileOutputStream(atomicFileCreator.getTmpFile());
-			writer = new OutputStreamWriter(new BufferedOutputStream(fileOutputStream));
-			
-			properties.store(writer, null);
-			
-			writer.close();
-			
-			atomicFileCreator.renameTmpFileToCurrent();
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException(
-					"Unable to write the properties to temporary file " + atomicFileCreator.getTmpFile() + ".", e);
-		} finally {
-			if (fileOutputStream != null) {
-				try {
-					fileOutputStream.close();
-				} catch (Exception e) {
-					LOG.log(Level.WARNING, "Unable to close temporary state file " + atomicFileCreator.getTmpFile()
-							+ ".", e);
-				}
-			}
-		}
-	}
-
-
-	/**
-	 * Checks if the properties file exists.
-	 * 
-	 * @return True if a file exists, false otherwise.
-	 */
-	public boolean exists() {
-		return atomicFileCreator.exists();
-	}
-}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java b/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java
deleted file mode 100644
index 8f3d121..0000000
--- a/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.bound.v0_6;
-
-import java.util.Date;
-import java.util.Iterator;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-
-/**
- * Unit tests for the bound computer.
- * 
- * @author Igor Podolskiy
- */
-public class BoundComputerTest {
-
-	/**
-	 * Tests the bound computation if no nodes are upstream.
-	 */
-	@Test
-	public void computeBoundNoNodes() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundComputer bc = new BoundComputer("NewBound");
-		bc.setSink(inspector);
-		bc.complete();
-		bc.release();
-
-		Assert.assertNull(inspector.getLastEntityContainer());
-	}
-
-
-	/**
-	 * Tests the bound computation if no nodes but a bound entity is upstream.
-	 */
-	@Test
-	public void computeBoundNoNodesWithBound() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundComputer bc = new BoundComputer("NewBound");
-		bc.setSink(inspector);
-		bc.process(new BoundContainer(new Bound("Test")));
-		bc.complete();
-		bc.release();
-
-		Assert.assertNull(inspector.getLastEntityContainer());
-	}
-
-
-	/**
-	 * Tests the bound computation when no bound entity is upstream.
-	 */
-	@Test
-	public void computeBoundNoUpstreamBound() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundComputer bc = new BoundComputer("NewBound");
-		bc.setSink(inspector);
-		bc.process(new NodeContainer(new Node(new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		bc.process(new NodeContainer(new Node(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 1), 2, 2)));
-		bc.complete();
-		bc.release();
-
-		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
-		Assert.assertEquals(new Bound(2, 1, 2, 1, "NewBound"), ec.getEntity());
-	}
-
-
-	/**
-	 * Tests the bound computation when there is bound entity is upstream.
-	 */
-	@Test
-	public void computeBoundWithUpstreamBound() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundComputer bc = new BoundComputer("NewBound");
-		bc.setSink(inspector);
-		bc.process(new NodeContainer(new Node(new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		bc.process(new NodeContainer(new Node(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 1), 2, 2)));
-		bc.complete();
-		bc.release();
-
-		Iterator<EntityContainer> iterator = inspector.getProcessedEntities().iterator();
-		EntityContainer ec = iterator.next();
-		Assert.assertEquals(new Bound(2, 1, 2, 1, "NewBound"), ec.getEntity());
-
-		// Ensure there is no second bound.
-		ec = iterator.next();
-		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
-	}
-
-}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java b/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java
deleted file mode 100644
index 67afdda..0000000
--- a/core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.bound.v0_6;
-
-import java.util.Date;
-import java.util.Iterator;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-/**
- * Test for the bound setter task.
- *
- * @author Igor Podolskiy
- */
-public class BoundSetterTest {
-
-	/**
-	 * Tests the bound removal.
-	 */
-	@Test
-	public void removeExistingBoundTest() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundSetter setter = new BoundSetter(null);
-		setter.setSink(inspector);
-		setter.process(new BoundContainer(new Bound("Test")));
-		setter.process(new NodeContainer(new Node(
-				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		setter.complete();
-		setter.release();
-		
-		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
-		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
-	}
-	
-	/**
-	 * Tests the bound removal when there is no bound upstream.
-	 */
-	@Test
-	public void removeNoBoundTest() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		BoundSetter setter = new BoundSetter(null);
-		setter.setSink(inspector);
-		setter.process(new NodeContainer(new Node(
-				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		setter.complete();
-		setter.release();
-		
-		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
-		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
-	}
-	
-	/**
-	 * Tests the bound setting.
-	 */
-	@Test
-	public void overwriteBoundTest() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		Bound newBound = new Bound(2, 1, 4, 3, "NewBound");
-		BoundSetter setter = new BoundSetter(newBound);
-		setter.setSink(inspector);
-		setter.process(new BoundContainer(new Bound("Test")));
-		setter.process(new NodeContainer(new Node(
-				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		setter.complete();
-		setter.release();
-		
-		Iterator<EntityContainer> iterator = inspector.getProcessedEntities().iterator();
-		EntityContainer ec = iterator.next();
-		Assert.assertEquals(EntityType.Bound, ec.getEntity().getType());
-		Bound bound = (Bound) ec.getEntity();
-		Assert.assertEquals(bound, newBound);
-		
-		// Ensure there is no second bound
-		ec = iterator.next();
-		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
-	}
-	
-	/**
-	 * Tests the bound setting when there is no bound upstream.
-	 */
-	@Test
-	public void setNewBoundTest() {
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		Bound newBound = new Bound(2, 1, 4, 3, "NewBound");
-		BoundSetter setter = new BoundSetter(newBound);
-		setter.setSink(inspector);
-		setter.process(new NodeContainer(new Node(
-				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
-		setter.complete();
-		setter.release();
-		
-		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
-		Assert.assertEquals(EntityType.Bound, ec.getEntity().getType());
-		Bound bound = (Bound) ec.getEntity();
-		Assert.assertEquals(bound, newBound);
-	}
-
-}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java b/core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java
deleted file mode 100644
index a46a881..0000000
--- a/core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.cli;
-
-import java.util.Arrays;
-import java.util.logging.Level;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * Tests the CommandLineParser class.
- * 
- * @author Brett Henderson
- */
-public class CommandLineParserTest {
-	/**
-	 * Validates the quiet option.
-	 */
-	@Test
-	public void testQuietOption() {
-		CommandLineParser commandLineParser;
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {});
-		Assert.assertEquals("Incorrect default log level.", Level.INFO, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-q"});
-		Assert.assertEquals("Incorrect quiet log level.", Level.WARNING, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-q", "1"});
-		Assert.assertEquals("Incorrect very quiet log level.", Level.SEVERE, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-q", "2"});
-		Assert.assertEquals("Incorrect very very quiet log level.", Level.OFF, commandLineParser.getLogLevel());
-	}
-	
-	
-	/**
-	 * Validates the verbose option.
-	 */
-	@Test
-	public void testVerboseOption() {
-		CommandLineParser commandLineParser;
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {});
-		Assert.assertEquals("Incorrect default log level.", Level.INFO, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v"});
-		Assert.assertEquals("Incorrect verbose log level.", Level.FINE, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "1"});
-		Assert.assertEquals("Incorrect very verbose log level.", Level.FINER, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "2"});
-		Assert.assertEquals("Incorrect very very verbose log level.", Level.FINEST, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "3"});
-		Assert.assertEquals(
-				"Incorrect very very very verbose log level.",
-				Level.FINEST, commandLineParser.getLogLevel());
-	}
-	
-	
-	/**
-	 * Validates the quiet and verbose options in combination.
-	 */
-	@Test
-	public void testQuietAndVerboseOption() {
-		CommandLineParser commandLineParser;
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {});
-		Assert.assertEquals("Incorrect default log level.", Level.INFO, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "-q"});
-		Assert.assertEquals("Incorrect default log level.", Level.INFO, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "1", "-q", "1"});
-		Assert.assertEquals("Incorrect default log level.", Level.INFO, commandLineParser.getLogLevel());
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-v", "1", "-q", "2"});
-		Assert.assertEquals("Incorrect quiet log level.", Level.WARNING, commandLineParser.getLogLevel());
-	}
-	
-	
-	/**
-	 * Validates the quiet and verbose options in combination.
-	 */
-	@Test
-	public void testPluginOption() {
-		CommandLineParser commandLineParser;
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-p", "plugin1", "-p", "plugin2"});
-		Assert.assertEquals(
-				"Incorrect plugin list.",
-				Arrays.asList("plugin1", "plugin2"),
-				commandLineParser.getPlugins());
-	}
-	
-	
-	/**
-	 * Validates failure when an unknown option is specified.
-	 */
-	@Test (expected = OsmosisRuntimeException.class)
-	public void testUnknownOption() {
-		CommandLineParser commandLineParser;
-		
-		commandLineParser = new CommandLineParser();
-		commandLineParser.parse(new String [] {"-a"});
-	}
-}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java b/core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java
deleted file mode 100644
index 7a006d5..0000000
--- a/core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java
+++ /dev/null
@@ -1,597 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.core.domain.v0_6;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
-
-
-/**
- * Tests the Bound entity class.
- * 
- * @author Karl Newman
- */
-public class BoundTest {
-
-	/**
-	 * Test the constructor with right > 180.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor1() {
-		new Bound(181.0000000000001, -20, 20, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with right < -180.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor2() {
-		new Bound(-181.0000000000001, -20, 20, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with left > 180.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor3() {
-		new Bound(20, 181.0000000000001, 20, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with left < -180.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor4() {
-		new Bound(20, -181.0000000000001, 20, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with top > 90.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor5() {
-		new Bound(20, -20, 91.0000000000001, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with top < -90.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor6() {
-		new Bound(20, -20, -91.0000000000001, -20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with bottom > 90.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor7() {
-		new Bound(20, -20, 20, 91.0000000000001, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with bottom < -90.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor8() {
-		new Bound(20, -20, 20, -91.0000000000001, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with top < bottom.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor9() {
-		new Bound(20, -20, -20, 20, "not null");
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test the constructor with null origin string.
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public final void testConstructor10() {
-		new Bound(20, -20, 20, -20, null);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a valid constructor with only the origin string provided (covers full planet).
-	 */
-	@Test
-	public final void testConstructor11() {
-		Bound b = new Bound("not null");
-		assertTrue(Double.compare(b.getRight(), 180) == 0
-		        && Double.compare(b.getLeft(), -180) == 0
-		        && Double.compare(b.getTop(), 90) == 0
-		        && Double.compare(b.getBottom(), -90) == 0
-		        && b.getOrigin().equals("not null"));
-	}
-
-
-	/**
-	 * Test a valid constructor with all values provided.
-	 */
-	@Test
-	public final void testConstructor12() {
-		Bound b = new Bound(20, -20, 21, -21, "not null");
-		assertTrue(Double.compare(b.getRight(), 20) == 0
-		        && Double.compare(b.getLeft(), -20) == 0
-		        && Double.compare(b.getTop(), 21) == 0
-		        && Double.compare(b.getBottom(), -21) == 0
-		        && b.getOrigin().equals("not null"));
-	}
-
-
-	/**
-	 * Test a simple intersection.
-	 */
-	@Test
-	public final void testIntersect1() {
-		final double right1 = 20.0;
-		final double left1 = 10.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 30.0;
-		final double left2 = 15.0;
-		final double top2 = 45.0;
-		final double bottom2 = 20.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "this");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
-		Bound b = b1.intersect(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("this"));
-		// Test it with arguments swapped
-		b = b2.intersect(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-	}
-
-
-	/**
-	 * Test an intersect with no top-bottom overlapping areas.
-	 */
-	@Test
-	public final void testIntersect2() {
-		final double right1 = 20.0;
-		final double left1 = 10.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 30.0;
-		final double left2 = 15.0;
-		final double top2 = 25.0;
-		final double bottom2 = 20.0;
-		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
-		        right2,
-		        left2,
-		        top2,
-		        bottom2,
-		        ""));
-		assertNull(b);
-	}
-
-
-	/**
-	 * Test an intersect with no left-right overlapping areas.
-	 */
-	@Test
-	public final void testIntersect3() {
-		final double right1 = 20.0;
-		final double left1 = 10.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 30.0;
-		final double left2 = 21.0;
-		final double top2 = 45.0;
-		final double bottom2 = 20.0;
-		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
-		        right2,
-		        left2,
-		        top2,
-		        bottom2,
-		        ""));
-		assertNull(b);
-	}
-
-
-	/**
-	 * Test an intersect with 1 Bound crossing the antimeridian.
-	 */
-	@Test
-	public final void testIntersect4() {
-		final double right1 = 20.0;
-		final double left1 = 60.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 30.0;
-		final double left2 = 15.0;
-		final double top2 = 45.0;
-		final double bottom2 = 20.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
-		Bound b = b1.intersect(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-		// Test it with arguments swapped
-		b = b2.intersect(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-	}
-
-
-	/**
-	 * Test an intersection where one Bound crosses the antimeridian (but doesn't cover the planet)
-	 * and both ends overlap with the intersecting Bound. A strict intersection would result in two
-	 * Bound areas, so just expect the smaller (longitudinally) of the Bound as the result.
-	 */
-	@Test
-	public final void testIntersect5() {
-		final double right1 = 150.0;
-		final double left1 = 170.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 175.0;
-		final double left2 = 145.0;
-		final double top2 = 45.0;
-		final double bottom2 = 25.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.intersect(b2);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-		// Test it with arguments swapped
-		b = b2.intersect(b1);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-	}
-
-
-	/**
-	 * Test an intersect with both Bound crossing the antimeridian.
-	 */
-	@Test
-	public final void testIntersect6() {
-		final double right1 = 20.0;
-		final double left1 = 60.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 30.0;
-		final double left2 = 50.0;
-		final double top2 = 45.0;
-		final double bottom2 = 35.0;
-		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
-		        right2,
-		        left2,
-		        top2,
-		        bottom2,
-		        ""));
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom2) == 0);
-	}
-
-
-	/**
-	 * Test a simple union on opposite sides of the planet with exactly the same distance between
-	 * them on both sides. The smallest resulting union could wrap around the planet either way, so
-	 * expect a simple Bound which does not cross the antimeridian.
-	 */
-	@Test
-	public final void testUnion1() {
-		final double right1 = 90.0;
-		final double left1 = 80.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = -90.0;
-		final double left2 = -100.0;
-		final double top2 = 45.0;
-		final double bottom2 = 35.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "this");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("this"));
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-	}
-
-
-	/**
-	 * Test a union where one Bound is entirely contained by another.
-	 */
-	@Test
-	public final void testUnion2() {
-		final double right1 = 20.0;
-		final double left1 = 10.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 15.0;
-		final double left2 = 12.0;
-		final double top2 = 35.0;
-		final double bottom2 = 32.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0
-		        && b.getOrigin().equals("that"));
-	}
-
-
-	/**
-	 * Test a union of two simple Bound where the resulting Bound crosses the antimeridian.
-	 */
-	@Test
-	public final void testUnion3() {
-		final double right1 = 91.0;
-		final double left1 = 80.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = -90.0;
-		final double left2 = -100.0;
-		final double top2 = 45.0;
-		final double bottom2 = 35.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-	}
-
-
-	/**
-	 * Test a union where one Bound crosses the antimeridian but there is still a gap such that the
-	 * union does not cover the planet.
-	 */
-	@Test
-	public final void testUnion4() {
-		final double right1 = 10.0;
-		final double left1 = 20.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 15.0;
-		final double left2 = 12.0;
-		final double top2 = 35.0;
-		final double bottom2 = 32.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right2) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-	}
-
-
-	/**
-	 * Test a union where both Bound cross the antimeridian but do not cover the planet.
-	 */
-	@Test
-	public final void testUnion5() {
-		final double right1 = -170.0;
-		final double left1 = 175.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = -175.0;
-		final double left2 = 170.0;
-		final double top2 = 35.0;
-		final double bottom2 = 25.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom2) == 0);
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left2) == 0
-		        && Double.compare(b.getTop(), top1) == 0
-		        && Double.compare(b.getBottom(), bottom2) == 0);
-	}
-
-
-	/**
-	 * Test a union where one Bound covers the planet left-right.
-	 */
-	@Test
-	public final void testUnion6() {
-		final double right1 = 180.0;
-		final double left1 = -180.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 15.0;
-		final double left2 = 12.0;
-		final double top2 = 45.0;
-		final double bottom2 = 32.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), right1) == 0
-		        && Double.compare(b.getLeft(), left1) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom1) == 0);
-	}
-
-
-	/**
-	 * Test a union where the Bound overlap and the resulting union covers the planet left-right.
-	 */
-	@Test
-	public final void testUnion7() {
-		final double right1 = 150.0;
-		final double left1 = 170.0;
-		final double top1 = 40.0;
-		final double bottom1 = 30.0;
-		final double right2 = 175.0;
-		final double left2 = 145.0;
-		final double top2 = 45.0;
-		final double bottom2 = 25.0;
-		final double minLongitude = -180.0;
-		final double maxLongitude = 180.0;
-
-		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
-		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
-		Bound b = b1.union(b2);
-		assertTrue(Double.compare(b.getRight(), maxLongitude) == 0
-		        && Double.compare(b.getLeft(), minLongitude) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom2) == 0);
-		// Test it with arguments swapped
-		b = b2.union(b1);
-		assertTrue(Double.compare(b.getRight(), maxLongitude) == 0
-		        && Double.compare(b.getLeft(), minLongitude) == 0
-		        && Double.compare(b.getTop(), top2) == 0
-		        && Double.compare(b.getBottom(), bottom2) == 0);
-	}
-
-
-	/**
-	 * Test the case where the Bound is already "simple" (i.e., doesn't cross the antimeridian).
-	 */
-	@Test
-	public final void testToSimpleBound1() {
-		final double left = -179.0;
-		final double right = 179.0;
-		final double top = 1.0;
-		final double bottom = -1.0;
-		boolean expected1found = false;
-		int cnt = 0;
-		Bound expected1 = new Bound(right, left, top, bottom, "");
-
-		for (Bound b : new Bound(right, left, top, bottom, "").toSimpleBound()) {
-			cnt++;
-			if (Double.compare(b.getRight(), expected1.getRight()) == 0
-			        && Double.compare(b.getLeft(), expected1.getLeft()) == 0
-			        && Double.compare(b.getTop(), expected1.getTop()) == 0
-			        && Double.compare(b.getBottom(), expected1.getBottom()) == 0) {
-				expected1found = true;
-			}
-		}
-		assertTrue(cnt == 1);
-		assertTrue(expected1found);
-	}
-
-
-	/**
-	 * Test the case where the Bound is split into two simple Bound elements, one on either side
-	 * of the antimeridian.
-	 */
-	@Test
-	public final void testToSimpleBound2() {
-		final double left = 179.0;
-		final double right = -179.0;
-		final double top = 1.0;
-		final double bottom = -1.0;
-		final double minLongitude = -180.0;
-		final double maxLongitude = 180.0;
-		boolean expected1found = false, expected2found = false;
-		int cnt = 0;
-		Bound expected1 = new Bound(maxLongitude, left, top, bottom, "");
-		Bound expected2 = new Bound(right, minLongitude, top, bottom, "");
-
-		for (Bound b : new Bound(right, left, top, bottom, "").toSimpleBound()) {
-			cnt++;
-			if (Double.compare(b.getRight(), expected1.getRight()) == 0
-			        && Double.compare(b.getLeft(), expected1.getLeft()) == 0
-			        && Double.compare(b.getTop(), expected1.getTop()) == 0
-			        && Double.compare(b.getBottom(), expected1.getBottom()) == 0) {
-				expected1found = true;
-			}
-			if (Double.compare(b.getRight(), expected2.getRight()) == 0
-			        && Double.compare(b.getLeft(), expected2.getLeft()) == 0
-			        && Double.compare(b.getTop(), expected2.getTop()) == 0
-			        && Double.compare(b.getBottom(), expected2.getBottom()) == 0) {
-				expected2found = true;
-			}
-		}
-
-		assertTrue(cnt == 2);
-		assertTrue(expected1found);
-		assertTrue(expected2found);
-	}
-}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java b/core/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
deleted file mode 100644
index 737d24e..0000000
--- a/core/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.test.task.v0_6;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-/**
- * Mock object for inspecting the resulting entities after passing through a pipeline task.
- * 
- * @author Karl Newman
- */
-public class SinkEntityInspector implements Sink {
-
-	private List<EntityContainer> processedEntities;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SinkEntityInspector() {
-		processedEntities = new LinkedList<EntityContainer>();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Catch all passed entities and save them for later inspection.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		processedEntities.add(entityContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Shortcut method if you only care about the most recent EntityContainer.
-	 * 
-	 * @return the lastEntityContainer
-	 */
-	public EntityContainer getLastEntityContainer() {
-		if (processedEntities.isEmpty()) {
-			return null;
-		} else {
-			return processedEntities.get(processedEntities.size() - 1);
-		}
-	}
-
-
-	/**
-	 * Retrieve an Iterable of all the processed EntityContainers.
-	 * 
-	 * @return the processedEntities
-	 */
-	public Iterable<EntityContainer> getProcessedEntities() {
-		return Collections.unmodifiableList(processedEntities);
-	}
-
-}
diff --git a/dataset/.checkstyle b/dataset/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/dataset/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/dataset/.classpath b/dataset/.classpath
deleted file mode 100644
index 5028abb..0000000
--- a/dataset/.classpath
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/dataset/.gitignore b/dataset/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/dataset/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/dataset/.project b/dataset/.project
deleted file mode 100644
index 3a25d2f..0000000
--- a/dataset/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Dataset</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/dataset/build.xml b/dataset/build.xml
deleted file mode 100644
index 8f35857..0000000
--- a/dataset/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Dataset" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/dataset/ivy.xml b/dataset/ivy.xml
deleted file mode 100644
index b3a3c9e..0000000
--- a/dataset/ivy.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-dataset"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-    	
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java b/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java
deleted file mode 100644
index 7fc39e5..0000000
--- a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.dataset.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
-import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.task.v0_6.DatasetSinkSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * Provides a filter utilising a dataset to extract all entities that lie within
- * a specific geographical box identified by latitude and longitude coordinates.
- * 
- * @author Brett Henderson
- */
-public class DatasetBoundingBoxFilter implements DatasetSinkSource {
-	private Sink sink;
-	private double left;
-	private double right;
-	private double top;
-	private double bottom;
-	private boolean completeWays;
-	private DatasetContext datasetReader;
-	
-	
-	/**
-	 * Creates a new instance with the specified geographical coordinates. When
-	 * filtering, nodes right on the left and bottom edges of the box will be
-	 * included, nodes on the top and right edges will be excluded.
-	 * 
-	 * @param left
-	 *            The longitude marking the left edge of the bounding box.
-	 * @param right
-	 *            The longitude marking the right edge of the bounding box.
-	 * @param top
-	 *            The latitude marking the top edge of the bounding box.
-	 * @param bottom
-	 *            The latitude marking the bottom edge of the bounding box.
-	 * @param completeWays
-	 *            Include all nodes for ways which have some portion inside the
-	 *            filtered area.
-	 */
-	public DatasetBoundingBoxFilter(double left, double right, double top, double bottom, boolean completeWays) {
-		this.left = left;
-		this.right = right;
-		this.top = top;
-		this.bottom = bottom;
-		this.completeWays = completeWays;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(Dataset dataset) {
-		ReleasableIterator<EntityContainer> bboxData;
-		
-		if (datasetReader != null) {
-			throw new OsmosisRuntimeException("process may only be invoked once.");
-		}
-		
-		datasetReader = dataset.createReader();
-		
-		// Pass all data within the bounding box to the sink.
-		bboxData = datasetReader.iterateBoundingBox(left, right, top, bottom, completeWays);
-		try {
-			while (bboxData.hasNext()) {
-				sink.process(bboxData.next());
-			}
-			
-			sink.complete();
-			
-		} finally {
-			bboxData.release();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		if (datasetReader != null) {
-			datasetReader.release();
-		}
-	}
-}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java b/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java
deleted file mode 100644
index 061e097..0000000
--- a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.dataset.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
-import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.task.v0_6.DatasetSinkSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * Reads all data from a dataset.
- * 
- * @author Brett Henderson
- */
-public class DumpDataset implements DatasetSinkSource {
-	private Sink sink;
-	private DatasetContext datasetReader;
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(Dataset dataset) {
-		ReleasableIterator<EntityContainer> bboxData;
-		
-		if (datasetReader != null) {
-			throw new OsmosisRuntimeException("process may only be invoked once.");
-		}
-		
-		datasetReader = dataset.createReader();
-		
-		// Pass all data within the dataset to the sink.
-		bboxData = datasetReader.iterate();
-		try {
-			while (bboxData.hasNext()) {
-				sink.process(bboxData.next());
-			}
-			
-			sink.complete();
-			
-		} finally {
-			bboxData.release();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		if (datasetReader != null) {
-			datasetReader.release();
-		}
-	}
-}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java b/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java
deleted file mode 100644
index bb68ec2..0000000
--- a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.dataset.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.dataset.v0_6.impl.DatasetStore;
-import org.openstreetmap.osmosis.dataset.v0_6.impl.DatasetStoreFileManager;
-import org.openstreetmap.osmosis.dataset.v0_6.impl.PermanentFileDatasetStoreFileManager;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * Receives input data as a stream and builds a dataset containing all of the
- * data.
- * 
- * @author Brett Henderson
- */
-public class WriteDataset implements Sink {
-	
-	private DatasetStoreFileManager fileManager;
-	private DatasetStore store;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param directory
-	 *            The directory to store all data files in.
-	 * @param enableWayTileIndex
-	 *            If true a tile index is created for ways, otherwise a node-way
-	 *            index is used.
-	 */
-	public WriteDataset(File directory, boolean enableWayTileIndex) {
-		fileManager = new PermanentFileDatasetStoreFileManager(directory);
-		store = new DatasetStore(fileManager, enableWayTileIndex);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		store.process(entityContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		store.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		// We must release the store last because downstream tasks must be able
-		// to release store readers first.
-		store.release();
-		
-		// We must release the file manager after the store to ensure all open
-		// files are closed.
-		fileManager.release();
-	}
-}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java b/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java
deleted file mode 100644
index 5f4bca1..0000000
--- a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java
+++ /dev/null
@@ -1,438 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.dataset.v0_6.impl;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
-import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
-import org.openstreetmap.osmosis.core.store.ComparableComparator;
-import org.openstreetmap.osmosis.core.store.IndexStore;
-import org.openstreetmap.osmosis.core.store.IndexStoreReader;
-import org.openstreetmap.osmosis.core.store.IntegerLongIndexElement;
-import org.openstreetmap.osmosis.core.store.LongLongIndexElement;
-import org.openstreetmap.osmosis.core.store.NoSuchIndexElementException;
-import org.openstreetmap.osmosis.core.store.RandomAccessObjectStore;
-import org.openstreetmap.osmosis.core.store.RandomAccessObjectStoreReader;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.UnsignedIntegerComparator;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.util.TileCalculator;
-
-
-/**
- * Provides a file based storage mechanism for implementing a dataset.
- * 
- * @author Brett Henderson
- */
-public class DatasetStore implements Sink, EntityProcessor, Dataset {
-	
-	private static final Logger LOG = Logger.getLogger(DatasetStore.class.getName());
-	
-	
-	private SortedEntityPipeValidator sortedPipeValidator;
-	private TileCalculator tileCalculator;
-	private UnsignedIntegerComparator uintComparator;
-	
-	private boolean enableWayTileIndex;
-	
-	private CompletableContainer storeContainer;
-	private RandomAccessObjectStore<Node> nodeObjectStore;
-	private IndexStore<Long, LongLongIndexElement> nodeObjectOffsetIndexWriter;
-	private IndexStore<Integer, IntegerLongIndexElement> nodeTileIndexWriter;
-	private RandomAccessObjectStore<Way> wayObjectStore;
-	private IndexStore<Long, LongLongIndexElement> wayObjectOffsetIndexWriter;
-	private WayTileAreaIndex wayTileIndexWriter;
-	private IndexStore<Long, LongLongIndexElement> nodeWayIndexWriter;
-	private RandomAccessObjectStore<Relation> relationObjectStore;
-	private IndexStore<Long, LongLongIndexElement> relationObjectOffsetIndexWriter;
-	private IndexStore<Long, LongLongIndexElement> nodeRelationIndexWriter;
-	private IndexStore<Long, LongLongIndexElement> wayRelationIndexWriter;
-	private IndexStore<Long, LongLongIndexElement> relationRelationIndexWriter;
-	
-	private RandomAccessObjectStoreReader<Node> nodeObjectReader;
-	private IndexStoreReader<Long, LongLongIndexElement> nodeObjectOffsetIndexReader;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param fileManager
-	 *            The manager providing access to store files.
-	 * @param enableWayTileIndex
-	 *            If true a tile index is created for ways, otherwise a node-way
-	 *            index is used.
-	 */
-	public DatasetStore(DatasetStoreFileManager fileManager, boolean enableWayTileIndex) {
-		this.enableWayTileIndex = enableWayTileIndex;
-		
-		storeContainer = new CompletableContainer();
-		
-		// Validate all input data to ensure it is sorted.
-		sortedPipeValidator = new SortedEntityPipeValidator();
-		sortedPipeValidator.setSink(new Sink() {
-			@Override
-			public void complete() {
-				throw new UnsupportedOperationException();
-			}
-			
-			@Override
-			public void process(EntityContainer entityContainer) {
-				processImpl(entityContainer);
-			}
-			
-			@Override
-			public void release() {
-				throw new UnsupportedOperationException();
-			} });
-		
-		tileCalculator = new TileCalculator();
-		uintComparator = new UnsignedIntegerComparator();
-		
-		// Create node store and indexes.
-		nodeObjectStore = storeContainer.add(
-			new RandomAccessObjectStore<Node>(
-				new SingleClassObjectSerializationFactory(Node.class),
-				fileManager.getNodeObjectFile()
-			)
-		);
-		nodeObjectOffsetIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-			LongLongIndexElement.class,
-			new ComparableComparator<Long>(),
-			fileManager.getNodeObjectOffsetIndexFile()
-			)
-		);
-		nodeTileIndexWriter = storeContainer.add(
-			new IndexStore<Integer, IntegerLongIndexElement>(
-			IntegerLongIndexElement.class,
-			uintComparator,
-			fileManager.getNodeTileIndexFile()
-			)
-		);
-		
-		// Create way store and indexes.
-		wayObjectStore = storeContainer.add(
-			new RandomAccessObjectStore<Way>(
-				new SingleClassObjectSerializationFactory(Way.class),
-				fileManager.getWayObjectFile()
-			)
-		);
-		wayObjectOffsetIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getWayObjectOffsetIndexFile()
-			)
-		);
-		wayTileIndexWriter = storeContainer.add(new WayTileAreaIndex(fileManager));
-		nodeWayIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getNodeWayIndexFile()
-			)
-		);
-		
-		// Create relation store and indexes.
-		relationObjectStore = storeContainer.add(
-			new RandomAccessObjectStore<Relation>(
-				new SingleClassObjectSerializationFactory(Relation.class),
-				fileManager.getRelationObjectFile()
-			)
-		);
-		relationObjectOffsetIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getRelationObjectOffsetIndexFile()
-			)
-		);
-		nodeRelationIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getNodeRelationIndexFile()
-			)
-		);
-		wayRelationIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getWayRelationIndexFile()
-			)
-		);
-		relationRelationIndexWriter = storeContainer.add(
-			new IndexStore<Long, LongLongIndexElement>(
-				LongLongIndexElement.class,
-				new ComparableComparator<Long>(),
-				fileManager.getRelationRelationIndexFile()
-			)
-		);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		sortedPipeValidator.process(entityContainer);
-	}
-	
-	
-	/**
-	 * The entity processing implementation. This must not be called directly,
-	 * it is called by the internal sorted pipe validator.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	protected void processImpl(EntityContainer entityContainer) {
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-    public void process(BoundContainer bound) {
-        // Do nothing.
-    }
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer nodeContainer) {
-		Node node;
-		long nodeId;
-		long objectOffset;
-		
-		node = nodeContainer.getEntity();
-		nodeId = node.getId();
-		
-		// Write the node to the object store and save the file offset in an
-		// index keyed by node id.
-		objectOffset = nodeObjectStore.add(node);
-		nodeObjectOffsetIndexWriter.write(
-			new LongLongIndexElement(nodeId, objectOffset)
-		);
-		
-		// Write the node id to an index keyed by tile.
-		nodeTileIndexWriter.write(
-			new IntegerLongIndexElement((int) tileCalculator.calculateTile(node.getLatitude(), node.getLongitude()),
-			nodeId)
-		);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		Way way;
-		long wayId;
-		long objectOffset;
-		int minimumTile;
-		int maximumTile;
-		boolean tilesFound;
-		
-		if (nodeObjectReader == null) {
-			nodeObjectStore.complete();
-			nodeObjectReader = nodeObjectStore.createReader();
-		}
-		if (nodeObjectOffsetIndexReader == null) {
-			nodeObjectOffsetIndexWriter.complete();
-			nodeObjectOffsetIndexReader = nodeObjectOffsetIndexWriter.createReader();
-		}
-		
-		way = wayContainer.getEntity();
-		wayId = way.getId();
-		
-		// Write the way to the object store and save the file offset in an
-		// index keyed by way id.
-		objectOffset = wayObjectStore.add(way);
-		wayObjectOffsetIndexWriter.write(
-			new LongLongIndexElement(wayId, objectOffset)
-		);
-		
-		if (enableWayTileIndex) {
-		// Calculate the minimum and maximum tile indexes for the way.
-		tilesFound = false;
-		minimumTile = 0;
-		maximumTile = 0;
-		for (WayNode wayNode : way.getWayNodes()) {
-			long nodeId;
-			Node node;
-			int tile;
-			
-			nodeId = wayNode.getNodeId();
-			
-			try {
-			node = nodeObjectReader.get(
-				nodeObjectOffsetIndexReader.get(nodeId).getValue()
-			);
-			
-			tile = (int) tileCalculator.calculateTile(node.getLatitude(), node.getLongitude());
-			
-			if (tilesFound) {
-				if (uintComparator.compare(tile, minimumTile) < 0) {
-					minimumTile = tile;
-				}
-				if (uintComparator.compare(maximumTile, tile) < 0) {
-					maximumTile = tile;
-				}
-				
-			} else {
-				minimumTile = tile;
-				maximumTile = tile;
-				
-				tilesFound = true;
-			}
-				
-			} catch (NoSuchIndexElementException e) {
-				// Ignore any referential integrity problems.
-				if (LOG.isLoggable(Level.FINER)) {
-					LOG.finest(
-						"Ignoring referential integrity problem where way " + wayId
-						+ " refers to non-existent node " + nodeId + "."
-					);
-		}
-			}
-		}
-		
-		// Write the way id to an index keyed by tile but only if tiles were
-		// actually found.
-		if (tilesFound) {
-		wayTileIndexWriter.write(wayId, minimumTile, maximumTile);
-			}
-			
-		} else {
-			for (WayNode wayNode : way.getWayNodes()) {
-				long nodeId;
-				
-				nodeId = wayNode.getNodeId();
-				
-				nodeWayIndexWriter.write(new LongLongIndexElement(nodeId, wayId));
-			}
-	}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		Relation relation;
-		long relationId;
-		long objectOffset;
-		
-		relation = relationContainer.getEntity();
-		relationId = relation.getId();
-		
-		// Write the relation to the object store and save the file offset in an
-		// index keyed by relation id.
-		objectOffset = relationObjectStore.add(relation);
-		relationObjectOffsetIndexWriter.write(
-			new LongLongIndexElement(relationId, objectOffset)
-		);
-		
-		// Write the relation id to indexes keyed by each of the relation members.
-		for (RelationMember member : relation.getMembers()) {
-			EntityType memberType;
-			
-			memberType = member.getMemberType();
-			
-			if (memberType.equals(EntityType.Node)) {
-				nodeRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
-			} else if (memberType.equals(EntityType.Way)) {
-				wayRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
-			} else if (memberType.equals(EntityType.Relation)) {
-				relationRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
-			} else {
-				throw new OsmosisRuntimeException("Member type " + memberType + " is not recognised.");
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		// Complete all the stores to ensure their data is fully persisted.
-		storeContainer.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public DatasetContext createReader() {
-		ReleasableContainer releasableContainer = new ReleasableContainer();
-		
-		try {
-			DatasetContext reader;
-			
-			reader = new DatasetStoreReader(
-					new NodeStorageContainer(
-							releasableContainer.add(nodeObjectStore.createReader()),
-							releasableContainer.add(nodeObjectOffsetIndexWriter.createReader()),
-							releasableContainer.add(nodeTileIndexWriter.createReader()),
-							releasableContainer.add(nodeWayIndexWriter.createReader()),
-							releasableContainer.add(nodeRelationIndexWriter.createReader())),
-					new WayStorageContainer(
-							releasableContainer.add(wayObjectStore.createReader()),
-							releasableContainer.add(wayObjectOffsetIndexWriter.createReader()),
-							releasableContainer.add(wayTileIndexWriter.createReader()),
-							releasableContainer.add(wayRelationIndexWriter.createReader())),
-					new RelationStorageContainer(
-							releasableContainer.add(relationObjectStore.createReader()),
-							releasableContainer.add(relationObjectOffsetIndexWriter.createReader()),
-							releasableContainer.add(relationRelationIndexWriter.createReader())),
-					enableWayTileIndex
-			);
-			
-			// Stop the release of all created objects.
-			releasableContainer.clear();
-			
-			return reader;
-			
-		} finally {
-			releasableContainer.release();
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		if (nodeObjectReader != null) {
-			nodeObjectReader.release();
-		}
-		if (nodeObjectOffsetIndexReader != null) {
-			nodeObjectOffsetIndexReader.release();
-		}
-		
-		storeContainer.release();
-	}
-}
diff --git a/extract/.checkstyle b/extract/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/extract/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/extract/.classpath b/extract/.classpath
deleted file mode 100644
index ba46c0c..0000000
--- a/extract/.classpath
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Apidb"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Replication"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/aopalliance-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-dbcp-1.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-pool-1.5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/mysql-connector-java-5.1.18.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-aop-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-asm-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-beans-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-context-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-core-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-expression-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-jdbc-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-tx-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/extract/.gitignore b/extract/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/extract/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/extract/.project b/extract/.project
deleted file mode 100644
index ccac64d..0000000
--- a/extract/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Extract</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/extract/build.xml b/extract/build.xml
deleted file mode 100644
index 3e14443..0000000
--- a/extract/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Extract" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/extract/ivy.xml b/extract/ivy.xml
deleted file mode 100644
index d58dc72..0000000
--- a/extract/ivy.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-extract"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-apidb" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-replication" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="compile->default" changing="true"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..a10bb19
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,46 @@
+# Enable the gradle build daemon.  The daemon is a gradle process that
+# remains running between builds.  This significantly speeds up build
+# times.
+org.gradle.daemon=true
+
+# 3rd Party Library Versions
+dependencyVersionClassworlds=2.4
+dependencyVersionCommonsCodec=1.7
+dependencyVersionCommonsCompress=1.4.1
+dependencyVersionCommonsDbcp=1.4
+dependencyVersionJpf=1.5
+dependencyVersionJunit=4.10
+dependencyVersionMySql=5.1.21
+dependencyVersionNetty=3.2.7.Final
+dependencyVersionOsmPbf=1.1.1-754a33af
+dependencyVersionPostGis=1.3.3
+dependencyVersionPostgreSql=9.1-901-1.jdbc4
+dependencyVersionProtobuf=2.4.1
+dependencyVersionSpring=3.1.2.RELEASE
+dependencyVersionWoodstoxCore=4.1.4
+dependencyVersionWoodstoxStax2=3.1.1
+# Remaining on 2.9.1 instead of 2.10.0 for now because the newer version
+# depends on org.w3c.dom.ElementTraversal which is not being transitively
+# included. This could be possibly be fixed by including a newer version
+# of xml-apis but this hasn't been verified.
+dependencyVersionXerces=2.9.1
+
+# Builds are signed if the osmosisSigningEnabled property is set to true.
+# To enable signing, it is recommended to leave this file untouched and to
+# create a gradle.properties in your <USER_HOME>/.gradle/ directory and override
+# the setting there.  It is also necessary to set the following properties:
+# * signing.keyId - Something like ABCDEFGH (see gpg --list-keys)
+# * signing.secretKeyRingFile - Something like /home/<username>/.gnupg/secring.gpg
+# * signing.password - The password to unlock the secret key.
+osmosisSigningEnabled=false
+
+# If uploading to the Sonatype repositories, a username/password must be
+# provided. To do this, create a gradle.properties in your <USER_HOME>/.gradle/
+# directory and override the below property values there.
+sonatypeUsername=DO NOT EDIT ME.  Read the above comments.
+sonatypePassword=DO NOT EDIT ME.  Read the above comments.
+
+# By default, all builds are SNAPSHOT builds.  To create a release build, this
+# property should be overridden to be RELEASE.  Note that this variable should
+# not be updated.
+osmosisBuildType=SNAPSHOT
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..42d9b0e
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..8141911
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Mar 24 21:08:40 EST 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.4-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+ at if "%DEBUG%" == "" @echo off
+ at rem ##########################################################################
+ at rem
+ at rem  Gradle startup script for Windows
+ at rem
+ at rem ##########################################################################
+
+ at rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+ at rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+ at rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+ at rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+ at rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+ at rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+ at rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+ at rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+ at rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/hstore-jdbc/.checkstyle b/hstore-jdbc/.checkstyle
deleted file mode 100644
index 75246d3..0000000
--- a/hstore-jdbc/.checkstyle
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <fileset name="all" enabled="true" check-config-name="Sun Checks" local="false">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/hstore-jdbc/.classpath b/hstore-jdbc/.classpath
deleted file mode 100644
index de78ad0..0000000
--- a/hstore-jdbc/.classpath
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/hstore-jdbc/.gitignore b/hstore-jdbc/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/hstore-jdbc/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/hstore-jdbc/.project b/hstore-jdbc/.project
deleted file mode 100644
index 10f008b..0000000
--- a/hstore-jdbc/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Hstore</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/hstore-jdbc/build.xml b/hstore-jdbc/build.xml
deleted file mode 100644
index 307d5cf..0000000
--- a/hstore-jdbc/build.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Hstore" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-	
-	<!-- Disable checkstyle because this is external code. -->
-	<target name="checkstyle"/>
-</project>
diff --git a/hstore-jdbc/ivy.xml b/hstore-jdbc/ivy.xml
deleted file mode 100644
index f8c1fd2..0000000
--- a/hstore-jdbc/ivy.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-hstore"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="postgresql" name="postgresql" rev="${dependency.version.postgresql}" conf="compile->default"/>
-        
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/ivy.xml b/ivy.xml
deleted file mode 100644
index 3b5c62d..0000000
--- a/ivy.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    </publications>
-    
-    <dependencies>
-    </dependencies>
-</ivy-module>
diff --git a/osmosis-apidb/.checkstyle b/osmosis-apidb/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-apidb/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-apidb/.gitignore b/osmosis-apidb/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-apidb/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-apidb/build.gradle b/osmosis-apidb/build.gradle
new file mode 100644
index 0000000..8aa5f64
--- /dev/null
+++ b/osmosis-apidb/build.gradle
@@ -0,0 +1,10 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-replication')
+    compile project(':osmosis-xml')
+    compile group: 'commons-dbcp', name: 'commons-dbcp', version: dependencyVersionCommonsDbcp
+    compile group: 'org.springframework', name: 'spring-jdbc', version: dependencyVersionSpring
+    runtime group: 'postgresql', name: 'postgresql', version: dependencyVersionPostgreSql
+    runtime group: 'mysql', name: 'mysql-connector-java', version: dependencyVersionMySql
+    testCompile project(':osmosis-testutil')
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/ApidbPluginLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/ApidbPluginLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/ApidbPluginLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/ApidbPluginLoader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/BaseTableReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/BaseTableReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/BaseTableReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/BaseTableReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DataSourceFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DataSourceFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DataSourceFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DataSourceFactory.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java
new file mode 100644
index 0000000..d382d80
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/DatabaseContext2.java
@@ -0,0 +1,385 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.common;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabaseType;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionTemplate;
+
+
+/**
+ * This class manages the lifecycle of JDBC objects to minimise the risk of connection leaks and to
+ * support a consistent approach to database access.
+ * 
+ * @author Brett Henderson
+ */
+public class DatabaseContext2 {
+
+    private static final Logger LOG = Logger.getLogger(DatabaseContext.class.getName());
+
+    private BasicDataSource dataSource;
+    private PlatformTransactionManager txnManager;
+    private TransactionTemplate txnTemplate;
+    private JdbcTemplate jdbcTemplate;
+    private DatabaseType dbType;
+    private IdentityValueLoader identityValueLoader;
+    
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param loginCredentials Contains all information required to connect to the database.
+     */
+    public DatabaseContext2(DatabaseLoginCredentials loginCredentials) {
+    	dataSource = DataSourceFactory.createDataSource(loginCredentials);
+    	txnManager = new DataSourceTransactionManager(dataSource);
+    	txnTemplate = new TransactionTemplate(txnManager);
+    	jdbcTemplate = new JdbcTemplate(dataSource);
+    	this.dbType = loginCredentials.getDbType();
+    	
+    	setStatementFetchSizeForStreaming();
+
+        switch (loginCredentials.getDbType()) {
+        case POSTGRESQL:
+            identityValueLoader = new PostgresqlIdentityValueLoader2(this);
+            break;
+        case MYSQL:
+            identityValueLoader = new MysqlIdentityValueLoader2(this);
+            break;
+        default:
+            throw new OsmosisRuntimeException("Unknown database type " + loginCredentials.getDbType() + ".");
+        }
+    }
+    
+    
+    /**
+     * Gets the jdbc template which provides access to database functions.
+     * 
+     * @return The jdbc template.
+     */
+    public JdbcTemplate getJdbcTemplate() {
+    	return jdbcTemplate;
+    }
+    
+    
+	/**
+	 * Invokes the provided callback code within a transaction.
+	 * 
+	 * @param txnCallback
+	 *            The logic to be invoked within a transaction.
+	 * @param <T>
+	 *            The return type of the transaction callback.
+	 * 
+	 * @return The result.
+	 */
+    public <T> Object executeWithinTransaction(TransactionCallback<T> txnCallback) {
+    	return txnTemplate.execute(txnCallback);
+    }
+
+
+	/**
+	 * Returns the database type currently in use. This should only be used when it is not possible
+	 * to write database agnostic statements.
+	 * 
+	 * @return The database type.
+	 */
+    public DatabaseType getDatabaseType() {
+    	return dbType;
+    }
+    
+    
+    private void setStatementFetchSizeForStreaming() {
+    	switch (dbType) {
+        case POSTGRESQL:
+        	jdbcTemplate.setFetchSize(10000);
+			break;
+        case MYSQL:
+        	jdbcTemplate.setFetchSize(Integer.MIN_VALUE);
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+    }
+	
+	
+    /**
+	 * Truncates the contents of the specified tables.
+	 * 
+	 * @param tables
+	 *            The tables to be truncated.
+	 */
+	public void truncateTables(List<String> tables) {
+		switch (dbType) {
+        case POSTGRESQL:
+        	StringBuilder statementBuilder = new StringBuilder();
+    		
+			for (String table : tables) {
+				if (statementBuilder.length() == 0) {
+					statementBuilder.append("TRUNCATE ");
+				} else {
+					statementBuilder.append(", ");
+				}
+				
+				statementBuilder.append(table);
+			}
+			
+			jdbcTemplate.update(statementBuilder.toString());
+			break;
+        case MYSQL:
+			for (String table : tables) {
+				jdbcTemplate.update("TRUNCATE " + table);
+			}
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+	}
+	
+	
+    /**
+	 * Disables the indexes of the specified tables.
+	 * 
+	 * @param tables
+	 *            The tables to disable indexes on.
+	 */
+	public void disableIndexes(List<String> tables) {
+		switch (dbType) {
+        case POSTGRESQL:
+			// There is no way to automatically disable all indexes for a table.
+			break;
+        case MYSQL:
+        	for (String table : tables) {
+        		jdbcTemplate.update("ALTER TABLE " + table + " DISABLE KEYS");
+			}
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+	}
+	
+	
+    /**
+	 * Enables the indexes of the specified tables.
+	 * 
+	 * @param tables
+	 *            The tables to enable indexes on.
+	 */
+	public void enableIndexes(List<String> tables) {
+		switch (dbType) {
+        case POSTGRESQL:
+			// There is no way to automatically disable all indexes for a table.
+			break;
+        case MYSQL:
+        	for (String table : tables) {
+        		jdbcTemplate.update("ALTER TABLE " + table + " ENABLE KEYS");
+			}
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+	}
+	
+	
+    /**
+	 * Locks the specified tables for exclusive access.
+	 * 
+	 * @param tables
+	 *            The tables to lock.
+	 */
+	public void lockTables(List<String> tables) {
+		switch (dbType) {
+        case POSTGRESQL:
+			// Locking tables is not supported.
+			break;
+        case MYSQL:
+        	StringBuilder statementBuilder = new StringBuilder();
+        	
+        	for (String table : tables) {
+        		if (statementBuilder.length() == 0) {
+        			statementBuilder.append("LOCK TABLES ");
+        		} else {
+        			statementBuilder.append(", ");
+        		}
+        		statementBuilder.append(table);
+        		statementBuilder.append(" WRITE");
+			}
+        	
+        	jdbcTemplate.update(statementBuilder.toString());
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+	}
+	
+	
+    /**
+	 * Unlocks the specified tables.
+	 * 
+	 * @param tables
+	 *            The tables to unlock.
+	 */
+	public void unlockTables(List<String> tables) {
+		switch (dbType) {
+        case POSTGRESQL:
+			// Locking tables is not supported.
+			break;
+        case MYSQL:
+        	jdbcTemplate.update("UNLOCK TABLES");
+			break;
+		default:
+			throw new OsmosisRuntimeException("Unknown database type " + dbType + ".");
+		}
+	}
+
+
+	/**
+	 * Gets the last inserted identity column value. This is a global value and may not work
+	 * correctly if the database uses triggers.
+	 * 
+	 * @return The last inserted identity column value.
+	 */
+	public long getLastInsertId() {
+		return identityValueLoader.getLastInsertId();
+	}
+
+
+	/**
+	 * Gets the last retrieved sequence value. This is specific to the current connection only.
+	 * 
+	 * @param sequenceName
+	 *            The name of the sequence.
+	 * @return The last inserted identity column value.
+	 */
+	public long getLastSequenceId(String sequenceName) {
+		return identityValueLoader.getLastSequenceId(sequenceName);
+	}
+	
+
+    /**
+     * Releases all database resources. This method is guaranteed not to throw transactions and
+     * should always be called in a finally block whenever this class is used.
+     */
+    public void release() {
+    	identityValueLoader.release();
+    	
+    	try {
+			dataSource.close();
+		} catch (SQLException e) {
+			LOG.log(Level.WARNING, "Unable to cleanup the database connection pool.", e);
+		}
+    }
+    
+
+    /**
+     * Indicates if the specified column exists in the database.
+     * 
+     * @param tableName The table to check for.
+     * @param columnName The column to check for.
+     * @return True if the column exists, false otherwise.
+     */
+    public boolean doesColumnExist(String tableName, String columnName) {
+        ResultSet resultSet = null;
+        boolean result;
+
+        try {
+        	Connection connection;
+        	
+            LOG.finest("Checking if column {" + columnName + "} in table {" + tableName + "} exists.");
+
+            // This connection may not be freed if an exception occurs. It's a small chance and the
+			// additional code to avoid it is cumbersome.
+            connection = DataSourceUtils.getConnection(dataSource);
+            
+            resultSet = connection.getMetaData().getColumns(null, null, tableName, columnName);
+            result = resultSet.next();
+            resultSet.close();
+            resultSet = null;
+            
+            DataSourceUtils.releaseConnection(connection, dataSource);
+
+            return result;
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to check for the existence of column " + tableName + "."
+                    + columnName + ".", e);
+        } finally {
+            if (resultSet != null) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    // We are already in an error condition so log and continue.
+                    LOG.log(Level.WARNING, "Unable to close column existence result set.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Indicates if the specified table exists in the database.
+     * 
+     * @param tableName The table to check for.
+     * @return True if the table exists, false otherwise.
+     */
+    public boolean doesTableExist(String tableName) {
+        ResultSet resultSet = null;
+        boolean result;
+
+        try {
+        	Connection connection;
+        	
+            LOG.finest("Checking if table {" + tableName + "} exists.");
+
+            // This connection may not be freed if an exception occurs. It's a small chance and the
+			// additional code to avoid it is cumbersome.
+            connection = DataSourceUtils.getConnection(dataSource);
+
+            resultSet = connection.getMetaData().getTables(null, null, tableName, new String[] {"TABLE"});
+            result = resultSet.next();
+            resultSet.close();
+            resultSet = null;
+            
+            DataSourceUtils.releaseConnection(connection, dataSource);
+
+            return result;
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to check for the existence of table " + tableName + ".", e);
+        } finally {
+            if (resultSet != null) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    // We are already in an error condition so log and continue.
+                    LOG.log(Level.WARNING, "Unable to close table existence result set.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Enforces cleanup of any remaining resources during garbage collection. This is a safeguard
+     * and should not be required if release is called appropriately.
+     * 
+     * @throws Throwable If a problem occurs during finalization.
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        release();
+
+        super.finalize();
+    }
+
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/IdentityValueLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/IdentityValueLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/IdentityValueLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/IdentityValueLoader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader2.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader2.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader2.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/MysqlIdentityValueLoader2.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java
new file mode 100644
index 0000000..db59319
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/common/PostgresqlIdentityValueLoader2.java
@@ -0,0 +1,57 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.common;
+
+
+/**
+ * Postgresql implementation of an identity value loader.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgresqlIdentityValueLoader2 implements IdentityValueLoader {
+	private static final String SQL_SELECT_LAST_INSERT_ID =
+		"SELECT lastval() AS lastInsertId";
+	private static final String SQL_SELECT_LAST_SEQUENCE_ID =
+		"SELECT currval(?) AS lastSequenceId";
+	
+	private DatabaseContext2 dbCtx;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for all database access.
+	 */
+	public PostgresqlIdentityValueLoader2(DatabaseContext2 dbCtx) {
+		this.dbCtx = dbCtx;
+	}
+	
+	
+	/**
+	 * Returns the id of the most recently inserted row on the current
+	 * connection.
+	 * 
+	 * @return The newly inserted id.
+	 */
+	public long getLastInsertId() {
+		return dbCtx.getJdbcTemplate().queryForLong(SQL_SELECT_LAST_INSERT_ID);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public long getLastSequenceId(String sequenceName) {
+		return dbCtx.getJdbcTemplate().queryForLong(SQL_SELECT_LAST_SEQUENCE_ID, sequenceName);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		// Do nothing.
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java
new file mode 100644
index 0000000..e0aa7af
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReader.java
@@ -0,0 +1,127 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.util.Collections;
+import java.util.Date;
+
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.DeltaToDiffReader;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+
+
+/**
+ * A change source reading from database history tables. This aims to be suitable for running at
+ * regular intervals with database overhead proportional to changeset size.
+ * 
+ * @author Brett Henderson
+ */
+public class ApidbChangeReader implements RunnableChangeSource {
+
+    private ChangeSink changeSink;
+    private DatabaseLoginCredentials loginCredentials;
+    private DatabasePreferences preferences;
+    private Date intervalBegin;
+    private Date intervalEnd;
+    private boolean fullHistory;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param intervalBegin
+	 *            Marks the beginning (inclusive) of the time interval to be checked.
+	 * @param intervalEnd
+	 *            Marks the end (exclusive) of the time interval to be checked.
+	 * @param fullHistory
+	 *            Specifies if full version history should be returned, or just a single change per
+	 *            entity for the interval.
+	 */
+    public ApidbChangeReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+            Date intervalBegin, Date intervalEnd, boolean fullHistory) {
+        this.loginCredentials = loginCredentials;
+        this.preferences = preferences;
+        this.intervalBegin = intervalBegin;
+        this.intervalEnd = intervalEnd;
+        this.fullHistory = fullHistory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setChangeSink(ChangeSink changeSink) {
+        this.changeSink = changeSink;
+    }
+    
+    
+    /**
+	 * Runs the task implementation. This is called by the run method within a transaction.
+	 * 
+	 * @param dbCtx
+	 *            Used to access the database.
+	 */
+    protected void runImpl(DatabaseContext2 dbCtx) {
+    	try {
+    		AllEntityDao entityDao;
+    		ReleasableIterator<ChangeContainer> reader;
+    		
+    		changeSink.initialize(Collections.<String, Object>emptyMap());
+    		
+	        new SchemaVersionValidator(loginCredentials, preferences)
+	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+	        
+	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
+	        
+	        reader = entityDao.getHistory(intervalBegin, intervalEnd);
+	        if (!fullHistory) {
+	        	reader = new DeltaToDiffReader(reader);
+	        }
+	        try {
+	        	while (reader.hasNext()) {
+	        		changeSink.process(reader.next());
+	        	}
+	        	
+	        } finally {
+	        	reader.release();
+	        }
+	
+	        changeSink.complete();
+	        
+    	} finally {
+    		changeSink.release();
+    	}
+    }
+    
+
+    /**
+     * Reads all data from the database and send it to the sink.
+     */
+    public void run() {
+        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
+    	
+        try {
+        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
+        		private DatabaseContext2 dbCtxInner = dbCtx;
+
+				@Override
+				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
+					runImpl(dbCtxInner);
+				} });
+
+        } finally {
+            dbCtx.release();
+        }
+    }
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReaderFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReaderFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReaderFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeReaderFactory.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java
new file mode 100644
index 0000000..f209a59
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriter.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.ActionChangeWriter;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.ChangeWriter;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+/**
+ * A change sink writing to database tables. This aims to be suitable for running at regular
+ * intervals with database overhead proportional to changeset size.
+ * 
+ * @author Brett Henderson
+ */
+public class ApidbChangeWriter implements ChangeSink {
+
+    private final ChangeWriter changeWriter;
+
+    private final Map<ChangeAction, ActionChangeWriter> actionWriterMap;
+
+    private final SchemaVersionValidator schemaVersionValidator;
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param loginCredentials Contains all information required to connect to the database.
+     * @param preferences Contains preferences configuring database behaviour.
+     * @param populateCurrentTables If true, the current tables will be populated as well as history
+     *        tables.
+     */
+    public ApidbChangeWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+            boolean populateCurrentTables) {
+        changeWriter = new ChangeWriter(loginCredentials, populateCurrentTables);
+        actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
+        actionWriterMap.put(ChangeAction.Create, new ActionChangeWriter(changeWriter, ChangeAction.Create));
+        actionWriterMap.put(ChangeAction.Modify, new ActionChangeWriter(changeWriter, ChangeAction.Modify));
+        actionWriterMap.put(ChangeAction.Delete, new ActionChangeWriter(changeWriter, ChangeAction.Delete));
+
+        schemaVersionValidator = new SchemaVersionValidator(loginCredentials, preferences);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(ChangeContainer change) {
+        ChangeAction action;
+
+        // Verify that the schema version is supported.
+        schemaVersionValidator.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+
+        action = change.getAction();
+
+        if (!actionWriterMap.containsKey(action)) {
+            throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
+        }
+
+        // Process the entity using the action writer appropriate for the change
+        // action.
+        change.getEntityContainer().process(actionWriterMap.get(action));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void complete() {
+        changeWriter.complete();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void release() {
+        changeWriter.release();
+    }
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriterFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriterFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriterFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbChangeWriterFactory.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java
new file mode 100644
index 0000000..9c95da5
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReader.java
@@ -0,0 +1,113 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+
+
+/**
+ * An OSM data source reading from a databases current tables. The entire contents of the database
+ * are read.
+ * 
+ * @author Brett Henderson
+ */
+public class ApidbCurrentReader implements RunnableSource {
+
+    private Sink sink;
+    private DatabaseLoginCredentials loginCredentials;
+    private DatabasePreferences preferences;
+
+
+    /**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 */
+    public ApidbCurrentReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
+        this.loginCredentials = loginCredentials;
+        this.preferences = preferences;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setSink(Sink sink) {
+        this.sink = sink;
+    }
+    
+    
+    /**
+	 * Runs the task implementation. This is called by the run method within a transaction.
+	 * 
+	 * @param dbCtx
+	 *            Used to access the database.
+	 */
+    protected void runImpl(DatabaseContext2 dbCtx) {
+    	try {
+    		AllEntityDao entityDao;
+    		ReleasableIterator<EntityContainer> reader;
+    		
+    		sink.initialize(Collections.<String, Object>emptyMap());
+    		
+	        new SchemaVersionValidator(loginCredentials, preferences)
+	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+	        
+	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
+	        
+	        sink.process(new BoundContainer(new Bound("Osmosis " + OsmosisConstants.VERSION)));
+	        reader = entityDao.getCurrent();
+	        try {
+	        	while (reader.hasNext()) {
+	        		sink.process(reader.next());
+	        	}
+	        	
+	        } finally {
+	        	reader.release();
+	        }
+	
+	        sink.complete();
+	        
+    	} finally {
+    		sink.release();
+    	}
+    }
+    
+
+    /**
+     * Reads all data from the database and send it to the sink.
+     */
+    public void run() {
+        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
+    	
+        try {
+        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
+        		private DatabaseContext2 dbCtxInner = dbCtx;
+
+				@Override
+				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
+					runImpl(dbCtxInner);
+				} });
+
+        } finally {
+            dbCtx.release();
+        }
+    }
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReaderFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReaderFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReaderFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbCurrentReaderFactory.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java
new file mode 100644
index 0000000..0d42d8b
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicator.java
@@ -0,0 +1,107 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.ReplicationSource;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.Replicator;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SystemTimeLoader;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.TimeDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.TransactionDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.TransactionManager;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+
+
+/**
+ * Performs replication from an API database into change files.
+ */
+public class ApidbFileReplicator implements RunnableChangeSource {
+	
+	private DatabaseLoginCredentials loginCredentials;
+	private DatabasePreferences preferences;
+	private int iterations;
+	private int minInterval;
+	private int maxInterval;
+	private ChangeSink changeSink;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param iterations
+	 *            The number of replication intervals to execute. 0 means
+	 *            infinite.
+	 * @param minInterval
+	 *            The minimum number of milliseconds between intervals.
+	 * @param maxInterval
+	 *            The maximum number of milliseconds between intervals if no new
+	 *            data is available. This isn't a hard limit because proces
+	 */
+    public ApidbFileReplicator(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+            int iterations, int minInterval, int maxInterval) {
+    	this.loginCredentials = loginCredentials;
+    	this.preferences = preferences;
+    	this.iterations = iterations;
+    	this.minInterval = minInterval;
+    	this.maxInterval = maxInterval;
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+    
+    
+    /**
+	 * Runs the task implementation. This is called by the run method within a transaction.
+	 * 
+	 * @param dbCtx
+	 *            Used to access the database.
+	 */
+    protected void runImpl(DatabaseContext2 dbCtx) {
+		Replicator replicator;
+		ReplicationSource source;
+		TransactionManager txnSnapshotLoader;
+		SystemTimeLoader systemTimeLoader;
+		
+		new SchemaVersionValidator(loginCredentials, preferences)
+				.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+		
+		source = new AllEntityDao(dbCtx.getJdbcTemplate());
+		txnSnapshotLoader = new TransactionDao(dbCtx);
+		systemTimeLoader = new TimeDao(dbCtx.getJdbcTemplate());
+		
+		replicator = new Replicator(source, changeSink, txnSnapshotLoader, systemTimeLoader, iterations, minInterval,
+				maxInterval);
+		
+		replicator.replicate();
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
+    	
+        try {
+        	runImpl(dbCtx);
+
+        } finally {
+            dbCtx.release();
+        }
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java
new file mode 100644
index 0000000..43d08fe
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorFactory.java
@@ -0,0 +1,48 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.RunnableChangeSourceManager;
+
+
+/**
+ * The task factory for a file-based database replicator.
+ */
+public class ApidbFileReplicatorFactory extends DatabaseTaskManagerFactory {
+	private static final String ARG_ITERATIONS = "iterations";
+	private static final String ARG_MIN_INTERVAL = "minInterval";
+	private static final String ARG_MAX_INTERVAL = "maxInterval";
+	private static final int DEFAULT_ITERATIONS = 1;
+	private static final int DEFAULT_MIN_INTERVAL = 0;
+	private static final int DEFAULT_MAX_INTERVAL = 0;
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		DatabaseLoginCredentials loginCredentials;
+		DatabasePreferences preferences;
+		int iterations;
+		int minInterval;
+		int maxInterval;
+		
+		// Get the task arguments.
+		loginCredentials = getDatabaseLoginCredentials(taskConfig);
+		preferences = getDatabasePreferences(taskConfig);
+		iterations = getIntegerArgument(taskConfig, ARG_ITERATIONS, DEFAULT_ITERATIONS);
+		minInterval = getIntegerArgument(taskConfig, ARG_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+		maxInterval = getIntegerArgument(taskConfig, ARG_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
+		
+		return new RunnableChangeSourceManager(
+			taskConfig.getId(),
+			new ApidbFileReplicator(loginCredentials, preferences, iterations, minInterval, maxInterval),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java
new file mode 100644
index 0000000..6b63b69
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReader.java
@@ -0,0 +1,117 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.util.Collections;
+import java.util.Date;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.AllEntityDao;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.EntitySnapshotReader;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+
+/**
+ * An OSM data source reading from a database. The entire contents of the database are read.
+ * 
+ * @author Brett Henderson
+ */
+public class ApidbReader implements RunnableSource {
+
+    private Sink sink;
+
+    private DatabaseLoginCredentials loginCredentials;
+    private DatabasePreferences preferences;
+    private Date snapshotInstant;
+
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param loginCredentials Contains all information required to connect to the database.
+     * @param preferences Contains preferences configuring database behaviour.
+     * @param snapshotInstant The state of the node table at this point in time will be dumped. This
+     *        ensures a consistent snapshot.
+     */
+    public ApidbReader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+            Date snapshotInstant) {
+        this.loginCredentials = loginCredentials;
+        this.preferences = preferences;
+        this.snapshotInstant = snapshotInstant;
+        
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setSink(Sink sink) {
+        this.sink = sink;
+    }
+    
+    
+    /**
+	 * Runs the task implementation. This is called by the run method within a transaction.
+	 * 
+	 * @param dbCtx
+	 *            Used to access the database.
+	 */
+    protected void runImpl(DatabaseContext2 dbCtx) {
+    	try {
+    		AllEntityDao entityDao;
+    		ReleasableIterator<EntityContainer> reader;
+    		
+    		sink.initialize(Collections.<String, Object>emptyMap());
+    		
+	        new SchemaVersionValidator(loginCredentials, preferences)
+	                .validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+	        
+	        entityDao = new AllEntityDao(dbCtx.getJdbcTemplate());
+
+	        sink.process(new BoundContainer(new Bound("Osmosis " + OsmosisConstants.VERSION)));
+	        reader = new EntitySnapshotReader(entityDao.getHistory(), snapshotInstant);
+	        try {
+	        	while (reader.hasNext()) {
+	        		sink.process(reader.next());
+	        	}
+	        	
+	        } finally {
+	        	reader.release();
+	        }
+	
+	        sink.complete();
+	        
+    	} finally {
+    		sink.release();
+    	}
+    }
+    
+
+    /**
+     * Reads all data from the database and send it to the sink.
+     */
+    public void run() {
+        final DatabaseContext2 dbCtx = new DatabaseContext2(loginCredentials);
+    	
+        try {
+        	dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
+        		private DatabaseContext2 dbCtxInner = dbCtx;
+
+				@Override
+				protected void doInTransactionWithoutResult(TransactionStatus arg0) {
+					runImpl(dbCtxInner);
+				} });
+
+        } finally {
+            dbCtx.release();
+        }
+    }
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReaderFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReaderFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReaderFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbReaderFactory.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncator.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncator.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncator.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncatorFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncatorFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncatorFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbTruncatorFactory.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java
new file mode 100644
index 0000000..e461299
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbVersionConstants.java
@@ -0,0 +1,34 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+
+/**
+ * Defines constants specific to the specific schema version.
+ * 
+ * @author Brett Henderson
+ */
+public final class ApidbVersionConstants {
+	
+	/**
+	 * This class cannot be instantiated.
+	 */
+	private ApidbVersionConstants() {
+	}
+	
+	/**
+	 * Defines the schema migrations expected to be in the database.
+	 */
+	public static final String[] SCHEMA_MIGRATIONS = {
+		"1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
+		"11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
+		"21", "22", "23", "24", "25", "26", "27", "28", "29", "30",
+		"31", "32", "33", "34", "35", "36", "37", "38", "39", "40",
+		"41", "42", "43", "44", "45", "46", "47", "48", "49", "50",
+		"51", "52", "20100513171259", "20100516124737",
+		"20100910084426", "20101114011429", "20110322001319", 
+		"20110925112722", "20111116184519", "20111212183945",
+		"20120208122334", "20120208194454", "20120123184321",
+		"20120219161649", "20120214210114", "20120328090602",
+		"20120404205604", "20120318201948", "20120808231205"
+	};
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java
new file mode 100644
index 0000000..2cae012
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriter.java
@@ -0,0 +1,1244 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.ChangesetManager;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.MemberTypeRenderer;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.SchemaVersionValidator;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.UserManager;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.database.DbFeature;
+import org.openstreetmap.osmosis.core.database.DbFeatureHistory;
+import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.util.FixedPrecisionCoordinateConvertor;
+import org.openstreetmap.osmosis.core.util.TileCalculator;
+
+
+/**
+ * An OSM data sink for storing all data to a database. This task is intended for writing to an
+ * empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class ApidbWriter implements Sink, EntityProcessor {
+
+    // These SQL strings are the prefix to statements that will be built based
+    // on how many rows of data are to be inserted at a time.
+	private static final String INSERT_SQL_NODE_COLUMNS =
+		"INSERT INTO nodes(node_id, timestamp, version, visible, changeset_id, latitude, longitude, tile)";
+	private static final String INSERT_SQL_NODE_PARAMS = "?, ?, ?, ?, ?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_NODE = 8;
+
+    private static final String INSERT_SQL_NODE_TAG_COLUMNS = "INSERT INTO node_tags (node_id, k, v, version)";
+    private static final String INSERT_SQL_NODE_TAG_PARAMS = "?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_NODE_TAG = 4;
+
+    private static final String INSERT_SQL_WAY_COLUMNS =
+    	"INSERT INTO ways (way_id, timestamp, version, visible, changeset_id)";
+    private static final String INSERT_SQL_WAY_PARAMS = "?, ?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_WAY = 5;
+
+    private static final String INSERT_SQL_WAY_TAG_COLUMNS = "INSERT INTO way_tags (way_id, k, v, version)";
+    private static final String INSERT_SQL_WAY_TAG_PARAMS = "?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_WAY_TAG = 4;
+
+    private static final String INSERT_SQL_WAY_NODE_COLUMNS =
+    	"INSERT INTO way_nodes (way_id, node_id, sequence_id, version)";
+    private static final String INSERT_SQL_WAY_NODE_PARAMS = "?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_WAY_NODE = 4;
+
+    private static final String INSERT_SQL_RELATION_COLUMNS =
+    	"INSERT INTO relations (relation_id, timestamp, version, visible, changeset_id)";
+    private static final String INSERT_SQL_RELATION_PARAMS =
+    	"?, ?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_RELATION = 5;
+
+    private static final String INSERT_SQL_RELATION_TAG_COLUMNS =
+        "INSERT INTO relation_tags (relation_id, k, v, version)";
+    private static final String INSERT_SQL_RELATION_TAG_PARAMS = "?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_RELATION_TAG = 4;
+
+    private static final String INSERT_SQL_RELATION_MEMBER_COLUMNS =
+    	"INSERT INTO relation_members (relation_id, member_type, member_id, sequence_id, member_role, version)";
+    private static final String INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL =
+    	"?, ?, ?, ?, ?, ?";
+    private static final String INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL =
+    	"?, ?::nwr_enum, ?, ?, ?, ?";
+	private static final int INSERT_PRM_COUNT_RELATION_MEMBER = 6;
+
+    // These tables will have indexes disabled during loading data.
+    private static final List<String> DISABLE_KEY_TABLES = Arrays.asList(new String[] {"nodes",
+            "node_tags", "ways", "way_tags",
+            "way_nodes", "relations",
+            "relation_tags", "relation_members"});
+
+    // These SQL statements will be invoked after loading history tables to
+    // populate the current tables.
+    private static final int LOAD_CURRENT_NODE_ROW_COUNT = 1000000;
+
+    private static final int LOAD_CURRENT_WAY_ROW_COUNT = 100000;
+
+    private static final int LOAD_CURRENT_RELATION_ROW_COUNT = 100000;
+
+    private static final String LOAD_CURRENT_NODES =
+    	"INSERT INTO current_nodes SELECT node_id, latitude, longitude, changeset_id, visible, timestamp, tile, version"
+            + " FROM nodes WHERE node_id >= ? AND node_id < ?";
+
+    private static final String LOAD_CURRENT_NODE_TAGS =
+    	"INSERT INTO current_node_tags SELECT node_id, k, v FROM node_tags WHERE node_id >= ? AND node_id < ?";
+
+    private static final String LOAD_CURRENT_WAYS =
+    	"INSERT INTO current_ways SELECT way_id, changeset_id, timestamp, visible, version FROM ways"
+            + " WHERE way_id >= ? AND way_id < ?";
+
+    private static final String LOAD_CURRENT_WAY_TAGS =
+    	"INSERT INTO current_way_tags SELECT way_id, k, v FROM way_tags"
+            + " WHERE way_id >= ? AND way_id < ?";
+
+    private static final String LOAD_CURRENT_WAY_NODES =
+    	"INSERT INTO current_way_nodes SELECT way_id, node_id, sequence_id FROM way_nodes"
+            + " WHERE way_id >= ? AND way_id < ?";
+
+    private static final String LOAD_CURRENT_RELATIONS =
+    	"INSERT INTO current_relations SELECT relation_id, changeset_id, timestamp, visible, version"
+            + " FROM relations WHERE relation_id >= ? AND relation_id < ?";
+
+    private static final String LOAD_CURRENT_RELATION_TAGS =
+    	"INSERT INTO current_relation_tags SELECT relation_id, k, v FROM relation_tags"
+            + " WHERE relation_id >= ? AND relation_id < ?";
+
+    private static final String LOAD_CURRENT_RELATION_MEMBERS =
+    	"INSERT INTO current_relation_members (relation_id, member_id, member_role, member_type, sequence_id)"
+    		+ " SELECT relation_id, member_id, member_role, member_type, sequence_id"
+            + " FROM relation_members WHERE relation_id >= ? AND relation_id < ?";
+
+    // These tables will be locked for exclusive access while loading data.
+	private static final List<String> LOCK_TABLES = Arrays.asList(new String[] {"nodes", "node_tags", "ways",
+			"way_tags", "way_nodes", "relations", "relation_tags", "relation_members", "current_nodes",
+			"current_node_tags", "current_ways", "current_way_tags", "current_way_nodes", "current_relations",
+			"current_relation_tags", "current_relation_members", "users", "changesets", "changeset_tags" });
+
+    // These constants define how many rows of each data type will be inserted
+    // with single insert statements.
+    private static final int INSERT_BULK_ROW_COUNT_NODE = 100;
+    private static final int INSERT_BULK_ROW_COUNT_NODE_TAG = 100;
+    private static final int INSERT_BULK_ROW_COUNT_WAY = 100;
+    private static final int INSERT_BULK_ROW_COUNT_WAY_TAG = 100;
+    private static final int INSERT_BULK_ROW_COUNT_WAY_NODE = 100;
+    private static final int INSERT_BULK_ROW_COUNT_RELATION = 100;
+    private static final int INSERT_BULK_ROW_COUNT_RELATION_TAG = 100;
+    private static final int INSERT_BULK_ROW_COUNT_RELATION_MEMBER = 100;
+
+    /**
+	 * Builds a multi-row SQL insert statement.
+	 * 
+	 * @param columnSql
+	 *            The basic query without value bind variables.
+	 * @param parametersSql
+	 *            The SQL parameters portion of the query.
+	 * @param rowCount
+	 *            The number of rows to insert in a single query.
+	 * @return The generated SQL statement.
+	 */
+    private static String buildSqlInsertStatement(String columnSql, String parametersSql, int rowCount) {
+        StringBuilder buffer;
+
+        buffer = new StringBuilder();
+
+        buffer.append(columnSql).append(" VALUES ");
+
+        for (int i = 0; i < rowCount; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            
+            buffer.append("(");
+            buffer.append(parametersSql);
+            buffer.append(")");
+        }
+
+        return buffer.toString();
+    }
+
+    
+    private String insertSqlSingleNode;
+    private String insertSqlBulkNode;
+    private String insertSqlSingleNodeTag;
+    private String insertSqlBulkNodeTag;
+    private String insertSqlSingleWay;
+    private String insertSqlBulkWay;
+    private String insertSqlSingleWayTag;
+    private String insertSqlBulkWayTag;
+    private String insertSqlSingleWayNode;
+    private String insertSqlBulkWayNode;
+    private String insertSqlSingleRelation;
+    private String insertSqlBulkRelation;
+    private String insertSqlSingleRelationTag;
+    private String insertSqlBulkRelationTag;
+    private String insertSqlSingleRelationMember;
+    private String insertSqlBulkRelationMember;
+    private final DatabaseContext dbCtx;
+    private final UserManager userManager;
+    private final ChangesetManager changesetManager;
+    private final SchemaVersionValidator schemaVersionValidator;
+    private final boolean lockTables;
+    private final boolean populateCurrentTables;
+    private final List<Node> nodeBuffer;
+    private final List<DbFeatureHistory<DbFeature<Tag>>> nodeTagBuffer;
+    private final List<Way> wayBuffer;
+    private final List<DbFeatureHistory<DbFeature<Tag>>> wayTagBuffer;
+    private final List<DbFeatureHistory<DbOrderedFeature<WayNode>>> wayNodeBuffer;
+    private final List<Relation> relationBuffer;
+    private final List<DbFeatureHistory<DbFeature<Tag>>> relationTagBuffer;
+    private final List<DbFeatureHistory<DbOrderedFeature<RelationMember>>> relationMemberBuffer;
+    private long maxNodeId;
+    private long maxWayId;
+    private long maxRelationId;
+    private final TileCalculator tileCalculator;
+    private final MemberTypeRenderer memberTypeRenderer;
+    private boolean initialized;
+    private PreparedStatement singleNodeStatement;
+    private PreparedStatement bulkNodeStatement;
+    private PreparedStatement singleNodeTagStatement;
+    private PreparedStatement bulkNodeTagStatement;
+    private PreparedStatement singleWayStatement;
+    private PreparedStatement bulkWayStatement;
+    private PreparedStatement singleWayTagStatement;
+    private PreparedStatement bulkWayTagStatement;
+    private PreparedStatement singleWayNodeStatement;
+    private PreparedStatement bulkWayNodeStatement;
+    private PreparedStatement singleRelationStatement;
+    private PreparedStatement bulkRelationStatement;
+    private PreparedStatement singleRelationTagStatement;
+    private PreparedStatement bulkRelationTagStatement;
+    private PreparedStatement singleRelationMemberStatement;
+    private PreparedStatement bulkRelationMemberStatement;
+    private PreparedStatement loadCurrentNodesStatement;
+    private PreparedStatement loadCurrentNodeTagsStatement;
+    private PreparedStatement loadCurrentWaysStatement;
+    private PreparedStatement loadCurrentWayTagsStatement;
+	private PreparedStatement loadCurrentWayNodesStatement;
+	private PreparedStatement loadCurrentRelationsStatement;
+	private PreparedStatement loadCurrentRelationTagsStatement;
+	private PreparedStatement loadCurrentRelationMembersStatement;
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param loginCredentials Contains all information required to connect to the database.
+     * @param preferences Contains preferences configuring database behaviour.
+     * @param lockTables If true, all tables will be locked during loading.
+     * @param populateCurrentTables If true, the current tables will be populated as well as history
+     *        tables.
+     */
+    public ApidbWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences, boolean lockTables,
+            boolean populateCurrentTables) {
+        dbCtx = new DatabaseContext(loginCredentials);
+        
+        userManager = new UserManager(dbCtx);
+        changesetManager = new ChangesetManager(dbCtx);
+
+        schemaVersionValidator = new SchemaVersionValidator(loginCredentials, preferences);
+
+        this.lockTables = lockTables;
+        this.populateCurrentTables = populateCurrentTables;
+
+        nodeBuffer = new ArrayList<Node>();
+        nodeTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
+        wayBuffer = new ArrayList<Way>();
+        wayTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
+        wayNodeBuffer = new ArrayList<DbFeatureHistory<DbOrderedFeature<WayNode>>>();
+        relationBuffer = new ArrayList<Relation>();
+        relationTagBuffer = new ArrayList<DbFeatureHistory<DbFeature<Tag>>>();
+        relationMemberBuffer = new ArrayList<DbFeatureHistory<DbOrderedFeature<RelationMember>>>();
+
+        maxNodeId = 0;
+        maxWayId = 0;
+        maxRelationId = 0;
+
+        tileCalculator = new TileCalculator();
+        memberTypeRenderer = new MemberTypeRenderer();
+
+        initialized = false;
+    }
+    
+    
+    private void buildSqlStatements() {
+    	insertSqlSingleNode = buildSqlInsertStatement(INSERT_SQL_NODE_COLUMNS, INSERT_SQL_NODE_PARAMS, 1);
+		insertSqlBulkNode = buildSqlInsertStatement(INSERT_SQL_NODE_COLUMNS, INSERT_SQL_NODE_PARAMS,
+				INSERT_BULK_ROW_COUNT_NODE);
+		insertSqlSingleNodeTag = buildSqlInsertStatement(
+				INSERT_SQL_NODE_TAG_COLUMNS, INSERT_SQL_NODE_TAG_PARAMS, 1);
+		insertSqlBulkNodeTag = buildSqlInsertStatement(INSERT_SQL_NODE_TAG_COLUMNS, INSERT_SQL_NODE_TAG_PARAMS,
+				INSERT_BULK_ROW_COUNT_NODE_TAG);
+		insertSqlSingleWay = buildSqlInsertStatement(INSERT_SQL_WAY_COLUMNS, INSERT_SQL_WAY_PARAMS, 1);
+		insertSqlBulkWay = buildSqlInsertStatement(INSERT_SQL_WAY_COLUMNS, INSERT_SQL_WAY_PARAMS,
+				INSERT_BULK_ROW_COUNT_WAY);
+		insertSqlSingleWayTag = buildSqlInsertStatement(INSERT_SQL_WAY_TAG_COLUMNS, INSERT_SQL_WAY_TAG_PARAMS, 1);
+		insertSqlBulkWayTag = buildSqlInsertStatement(INSERT_SQL_WAY_TAG_COLUMNS, INSERT_SQL_WAY_TAG_PARAMS,
+				INSERT_BULK_ROW_COUNT_WAY_TAG);
+		insertSqlSingleWayNode = buildSqlInsertStatement(
+				INSERT_SQL_WAY_NODE_COLUMNS, INSERT_SQL_WAY_NODE_PARAMS, 1);
+		insertSqlBulkWayNode = buildSqlInsertStatement(INSERT_SQL_WAY_NODE_COLUMNS, INSERT_SQL_WAY_NODE_PARAMS,
+				INSERT_BULK_ROW_COUNT_WAY_NODE);
+		insertSqlSingleRelation = buildSqlInsertStatement(INSERT_SQL_RELATION_COLUMNS, INSERT_SQL_RELATION_PARAMS,
+				1);
+		insertSqlBulkRelation = buildSqlInsertStatement(INSERT_SQL_RELATION_COLUMNS, INSERT_SQL_RELATION_PARAMS,
+				INSERT_BULK_ROW_COUNT_RELATION);
+    	insertSqlSingleRelationTag = buildSqlInsertStatement(INSERT_SQL_RELATION_TAG_COLUMNS,
+				INSERT_SQL_RELATION_TAG_PARAMS, 1);
+		insertSqlBulkRelationTag = buildSqlInsertStatement(INSERT_SQL_RELATION_TAG_COLUMNS,
+				INSERT_SQL_RELATION_TAG_PARAMS, INSERT_BULK_ROW_COUNT_RELATION_TAG);
+    }
+    
+
+    /**
+     * Initialises prepared statements and obtains database locks. Can be called multiple times.
+     */
+    private void initialize() {
+        if (!initialized) {
+            schemaVersionValidator.validateVersion(ApidbVersionConstants.SCHEMA_MIGRATIONS);
+            
+            buildSqlStatements();
+            
+            switch (dbCtx.getDatabaseType()) {
+            case POSTGRESQL:
+    			insertSqlSingleRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
+    					INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL, 1);
+    			insertSqlBulkRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
+    					INSERT_SQL_RELATION_MEMBER_PARAMS_PGSQL, INSERT_BULK_ROW_COUNT_RELATION_MEMBER);
+                break;
+            case MYSQL:
+    			insertSqlSingleRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
+    					INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL, 1);
+    			insertSqlBulkRelationMember = buildSqlInsertStatement(INSERT_SQL_RELATION_MEMBER_COLUMNS,
+    					INSERT_SQL_RELATION_MEMBER_PARAMS_MYSQL, INSERT_BULK_ROW_COUNT_RELATION_MEMBER);
+                break;
+            default:
+                throw new OsmosisRuntimeException("Unknown database type " + dbCtx.getDatabaseType() + ".");
+            }
+
+            bulkNodeStatement = dbCtx.prepareStatement(insertSqlBulkNode);
+            singleNodeStatement = dbCtx.prepareStatement(insertSqlSingleNode);
+            bulkNodeTagStatement = dbCtx.prepareStatement(insertSqlBulkNodeTag);
+            singleNodeTagStatement = dbCtx.prepareStatement(insertSqlSingleNodeTag);
+            bulkWayStatement = dbCtx.prepareStatement(insertSqlBulkWay);
+            singleWayStatement = dbCtx.prepareStatement(insertSqlSingleWay);
+            bulkWayTagStatement = dbCtx.prepareStatement(insertSqlBulkWayTag);
+            singleWayTagStatement = dbCtx.prepareStatement(insertSqlSingleWayTag);
+            bulkWayNodeStatement = dbCtx.prepareStatement(insertSqlBulkWayNode);
+            singleWayNodeStatement = dbCtx.prepareStatement(insertSqlSingleWayNode);
+            bulkRelationStatement = dbCtx.prepareStatement(insertSqlBulkRelation);
+            singleRelationStatement = dbCtx.prepareStatement(insertSqlSingleRelation);
+            bulkRelationTagStatement = dbCtx.prepareStatement(insertSqlBulkRelationTag);
+            singleRelationTagStatement = dbCtx.prepareStatement(insertSqlSingleRelationTag);
+            bulkRelationMemberStatement = dbCtx.prepareStatement(insertSqlBulkRelationMember);
+            singleRelationMemberStatement = dbCtx.prepareStatement(insertSqlSingleRelationMember);
+
+            loadCurrentNodesStatement = dbCtx.prepareStatement(LOAD_CURRENT_NODES);
+            loadCurrentNodeTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_NODE_TAGS);
+            loadCurrentWaysStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAYS);
+            loadCurrentWayTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAY_TAGS);
+            loadCurrentWayNodesStatement = dbCtx.prepareStatement(LOAD_CURRENT_WAY_NODES);
+            loadCurrentRelationsStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATIONS);
+            loadCurrentRelationTagsStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATION_TAGS);
+            loadCurrentRelationMembersStatement = dbCtx.prepareStatement(LOAD_CURRENT_RELATION_MEMBERS);
+
+            // Disable indexes to improve load performance.
+            dbCtx.disableIndexes(DISABLE_KEY_TABLES);
+
+            // Lock tables if required to improve load performance.
+            if (lockTables) {
+            	dbCtx.lockTables(LOCK_TABLES);
+            }
+
+            initialized = true;
+        }
+    }
+
+    /**
+     * Sets node values as bind variable parameters to a node insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param node The node containing the data to be inserted.
+     */
+    private void populateNodeParameters(PreparedStatement statement, int initialIndex, Node node) {
+        int prmIndex;
+
+        prmIndex = initialIndex;
+
+        // We can't write an entity with a null timestamp.
+        if (node.getTimestamp() == null) {
+            throw new OsmosisRuntimeException("Node " + node.getId() + " does not have a timestamp set.");
+        }
+
+        try {
+            statement.setLong(prmIndex++, node.getId());
+            statement.setTimestamp(prmIndex++, new Timestamp(node.getTimestamp().getTime()));
+            statement.setInt(prmIndex++, node.getVersion());
+            statement.setBoolean(prmIndex++, true);
+            statement.setLong(prmIndex++, node.getChangesetId());
+            statement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node.getLatitude()));
+            statement.setInt(prmIndex++, FixedPrecisionCoordinateConvertor.convertToFixed(node.getLongitude()));
+            statement.setLong(prmIndex++, tileCalculator.calculateTile(node.getLatitude(), node.getLongitude()));
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a node.", e);
+        }
+    }
+
+    /**
+     * Sets way values as bind variable parameters to a way insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param way The way containing the data to be inserted.
+     */
+    private void populateWayParameters(PreparedStatement statement, int initialIndex, Way way) {
+        int prmIndex;
+
+        prmIndex = initialIndex;
+
+        // We can't write an entity with a null timestamp.
+        if (way.getTimestamp() == null) {
+            throw new OsmosisRuntimeException("Way " + way.getId() + " does not have a timestamp set.");
+        }
+
+        try {
+            statement.setLong(prmIndex++, way.getId());
+            statement.setTimestamp(prmIndex++, new Timestamp(way.getTimestamp().getTime()));
+            statement.setInt(prmIndex++, way.getVersion());
+            statement.setBoolean(prmIndex++, true);
+            statement.setLong(prmIndex++, way.getChangesetId());
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a way.", e);
+        }
+    }
+
+    /**
+     * Sets tag values as bind variable parameters to a tag insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param dbEntityTag The entity tag containing the data to be inserted.
+     */
+    private void populateEntityTagParameters(PreparedStatement statement, int initialIndex,
+        DbFeatureHistory<DbFeature<Tag>> dbEntityTag) {
+        int prmIndex;
+        Tag tag;
+
+        prmIndex = initialIndex;
+
+        tag = dbEntityTag.getFeature().getFeature();
+
+        try {
+            statement.setLong(prmIndex++, dbEntityTag.getFeature().getEntityId());
+            statement.setString(prmIndex++, tag.getKey());
+            statement.setString(prmIndex++, tag.getValue());
+            statement.setInt(prmIndex++, dbEntityTag.getVersion());
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for an entity tag.", e);
+        }
+    }
+
+    /**
+     * Sets node reference values as bind variable parameters to a way node insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param dbWayNode The way node containing the data to be inserted.
+     */
+    private void populateWayNodeParameters(PreparedStatement statement, int initialIndex,
+        DbFeatureHistory<DbOrderedFeature<WayNode>> dbWayNode) {
+        int prmIndex;
+
+        prmIndex = initialIndex;
+
+        try {
+            statement.setLong(prmIndex++, dbWayNode.getFeature().getEntityId());
+            statement.setLong(prmIndex++, dbWayNode.getFeature().getFeature().getNodeId());
+            statement.setInt(prmIndex++, dbWayNode.getFeature().getSequenceId());
+            statement.setInt(prmIndex++, dbWayNode.getVersion());
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a way node.", e);
+        }
+    }
+
+    /**
+     * Sets relation values as bind variable parameters to a relation insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param relation The way containing the data to be inserted.
+     */
+    private void populateRelationParameters(PreparedStatement statement, int initialIndex, Relation relation) {
+        int prmIndex;
+
+        prmIndex = initialIndex;
+
+        // We can't write an entity with a null timestamp.
+        if (relation.getTimestamp() == null) {
+            throw new OsmosisRuntimeException("Relation " + relation.getId() + " does not have a timestamp set.");
+        }
+
+        try {
+            statement.setLong(prmIndex++, relation.getId());
+            statement.setTimestamp(prmIndex++, new Timestamp(relation.getTimestamp().getTime()));
+            statement.setInt(prmIndex++, relation.getVersion());
+            statement.setBoolean(prmIndex++, true);
+            statement.setLong(prmIndex++, relation.getChangesetId());
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a relation.", e);
+        }
+    }
+
+    /**
+     * Sets member reference values as bind variable parameters to a relation member insert query.
+     * 
+     * @param statement The prepared statement to add the values to.
+     * @param initialIndex The offset index of the first variable to set.
+     * @param dbRelationMember The relation member containing the data to be inserted.
+     */
+    private void populateRelationMemberParameters(PreparedStatement statement, int initialIndex,
+        DbFeatureHistory<DbOrderedFeature<RelationMember>> dbRelationMember) {
+        int prmIndex;
+        RelationMember relationMember;
+
+        prmIndex = initialIndex;
+
+        relationMember = dbRelationMember.getFeature().getFeature();
+
+        try {
+            statement.setLong(prmIndex++, dbRelationMember.getFeature().getEntityId());
+            statement.setString(prmIndex++, memberTypeRenderer.render(relationMember.getMemberType()));
+            statement.setLong(prmIndex++, relationMember.getMemberId());
+            statement.setInt(prmIndex++, dbRelationMember.getFeature().getSequenceId());
+            statement.setString(prmIndex++, relationMember.getMemberRole());
+            statement.setInt(prmIndex++, dbRelationMember.getVersion());
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to set a prepared statement parameter for a relation member.", e);
+        }
+    }
+
+    /**
+     * Flushes nodes to the database. If complete is false, this will only write nodes until the
+     * remaining node count is less than the multi-row insert statement row count. If complete is
+     * true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushNodes(boolean complete) {
+        while (nodeBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE) {
+            int prmIndex;
+            List<Node> processedNodes;
+
+            processedNodes = new ArrayList<Node>(INSERT_BULK_ROW_COUNT_NODE);
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE; i++) {
+                Node node;
+
+                node = nodeBuffer.remove(0);
+                processedNodes.add(node);
+
+                populateNodeParameters(bulkNodeStatement, prmIndex, node);
+                prmIndex += INSERT_PRM_COUNT_NODE;
+            }
+
+            try {
+                bulkNodeStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert nodes into the database.", e);
+            }
+
+            for (Node node : processedNodes) {
+                addNodeTags(node);
+            }
+        }
+
+        if (complete) {
+            while (nodeBuffer.size() > 0) {
+                Node node;
+
+                node = nodeBuffer.remove(0);
+
+                populateNodeParameters(singleNodeStatement, 1, node);
+
+                try {
+                    singleNodeStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a node into the database.", e);
+                }
+
+                addNodeTags(node);
+            }
+        }
+    }
+
+    /**
+     * Flushes node tags to the database. If complete is false, this will only write node tags until
+     * the remaining node tag count is less than the multi-row insert statement row count. If
+     * complete is true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushNodeTags(boolean complete) {
+        while (nodeTagBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE_TAG) {
+            int prmIndex;
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE_TAG; i++) {
+                populateEntityTagParameters(bulkNodeTagStatement, prmIndex, nodeTagBuffer.remove(0));
+                prmIndex += INSERT_PRM_COUNT_NODE_TAG;
+            }
+
+            try {
+                bulkNodeTagStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert node tags into the database.", e);
+            }
+        }
+
+        if (complete) {
+            while (nodeTagBuffer.size() > 0) {
+                populateEntityTagParameters(singleNodeTagStatement, 1, nodeTagBuffer.remove(0));
+
+                try {
+                    singleNodeTagStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a node tag into the database.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Flushes ways to the database. If complete is false, this will only write ways until the
+     * remaining way count is less than the multi-row insert statement row count. If complete is
+     * true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushWays(boolean complete) {
+        while (wayBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY) {
+            List<Way> processedWays;
+            int prmIndex;
+
+            processedWays = new ArrayList<Way>(INSERT_BULK_ROW_COUNT_WAY);
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY; i++) {
+                Way way;
+
+                way = wayBuffer.remove(0);
+                processedWays.add(way);
+
+                populateWayParameters(bulkWayStatement, prmIndex, way);
+                prmIndex += INSERT_PRM_COUNT_WAY;
+            }
+
+            try {
+                bulkWayStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert ways into the database.", e);
+            }
+
+            for (Way way : processedWays) {
+                addWayTags(way);
+                addWayNodes(way);
+            }
+        }
+
+        if (complete) {
+            while (wayBuffer.size() > 0) {
+                Way way;
+
+                way = wayBuffer.remove(0);
+
+                populateWayParameters(singleWayStatement, 1, way);
+
+                try {
+                    singleWayStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a way into the database.", e);
+                }
+
+                addWayTags(way);
+                addWayNodes(way);
+            }
+        }
+    }
+
+    /**
+     * Flushes way tags to the database. If complete is false, this will only write way tags until
+     * the remaining way tag count is less than the multi-row insert statement row count. If
+     * complete is true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushWayTags(boolean complete) {
+        while (wayTagBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_TAG) {
+            int prmIndex;
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_TAG; i++) {
+                populateEntityTagParameters(bulkWayTagStatement, prmIndex, wayTagBuffer.remove(0));
+                prmIndex += INSERT_PRM_COUNT_WAY_TAG;
+            }
+
+            try {
+                bulkWayTagStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert way tags into the database.", e);
+            }
+        }
+
+        if (complete) {
+            while (wayTagBuffer.size() > 0) {
+                populateEntityTagParameters(singleWayTagStatement, 1, wayTagBuffer.remove(0));
+
+                try {
+                    singleWayTagStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a way tag into the database.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Flushes way nodes to the database. If complete is false, this will only write way nodes until
+     * the remaining way node count is less than the multi-row insert statement row count. If
+     * complete is true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushWayNodes(boolean complete) {
+        while (wayNodeBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_NODE) {
+            int prmIndex;
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_NODE; i++) {
+                populateWayNodeParameters(bulkWayNodeStatement, prmIndex, wayNodeBuffer.remove(0));
+                prmIndex += INSERT_PRM_COUNT_WAY_NODE;
+            }
+
+            try {
+                bulkWayNodeStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert way nodes into the database.", e);
+            }
+        }
+
+        if (complete) {
+            while (wayNodeBuffer.size() > 0) {
+                populateWayNodeParameters(singleWayNodeStatement, 1, wayNodeBuffer.remove(0));
+
+                try {
+                    singleWayNodeStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a way node into the database.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Flushes relations to the database. If complete is false, this will only write relations until
+     * the remaining way count is less than the multi-row insert statement row count. If complete is
+     * true, all remaining rows will be written using single row insert statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushRelations(boolean complete) {
+        while (relationBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION) {
+            List<Relation> processedRelations;
+            int prmIndex;
+
+            processedRelations = new ArrayList<Relation>(INSERT_BULK_ROW_COUNT_RELATION);
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION; i++) {
+                Relation relation;
+
+                relation = relationBuffer.remove(0);
+                processedRelations.add(relation);
+
+                populateRelationParameters(bulkRelationStatement, prmIndex, relation);
+                prmIndex += INSERT_PRM_COUNT_RELATION;
+            }
+
+            try {
+                bulkRelationStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert relations into the database.", e);
+            }
+
+            for (Relation relation : processedRelations) {
+                addRelationTags(relation);
+                addRelationMembers(relation);
+            }
+        }
+
+        if (complete) {
+            while (relationBuffer.size() > 0) {
+                Relation relation;
+
+                relation = relationBuffer.remove(0);
+
+                populateRelationParameters(singleRelationStatement, 1, relation);
+
+                try {
+                    singleRelationStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a relation into the database.", e);
+                }
+
+                addRelationTags(relation);
+                addRelationMembers(relation);
+            }
+        }
+    }
+
+    /**
+     * Flushes relation tags to the database. If complete is false, this will only write relation
+     * tags until the remaining relation tag count is less than the multi-row insert statement row
+     * count. If complete is true, all remaining rows will be written using single row insert
+     * statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushRelationTags(boolean complete) {
+        while (relationTagBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_TAG) {
+            int prmIndex;
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_TAG; i++) {
+                populateEntityTagParameters(bulkRelationTagStatement, prmIndex, relationTagBuffer.remove(0));
+                prmIndex += INSERT_PRM_COUNT_RELATION_TAG;
+            }
+
+            try {
+                bulkRelationTagStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert relation tags into the database.", e);
+            }
+        }
+
+        if (complete) {
+            while (relationTagBuffer.size() > 0) {
+                populateEntityTagParameters(singleRelationTagStatement, 1, relationTagBuffer.remove(0));
+
+                try {
+                    singleRelationTagStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a relation tag into the database.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Flushes relation members to the database. If complete is false, this will only write relation
+     * members until the remaining relation member count is less than the multi-row insert statement
+     * row count. If complete is true, all remaining rows will be written using single row insert
+     * statements.
+     * 
+     * @param complete If true, all data will be written to the database. If false, some data may be
+     *        left until more data is available.
+     */
+    private void flushRelationMembers(boolean complete) {
+        while (relationMemberBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_MEMBER) {
+            int prmIndex;
+
+            prmIndex = 1;
+            for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_MEMBER; i++) {
+                populateRelationMemberParameters(bulkRelationMemberStatement, prmIndex, relationMemberBuffer.remove(0));
+                prmIndex += INSERT_PRM_COUNT_RELATION_MEMBER;
+            }
+
+            try {
+                bulkRelationMemberStatement.executeUpdate();
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to bulk insert relation members into the database.", e);
+            }
+        }
+
+        if (complete) {
+            while (relationMemberBuffer.size() > 0) {
+                populateRelationMemberParameters(singleRelationMemberStatement, 1, relationMemberBuffer.remove(0));
+
+                try {
+                    singleRelationMemberStatement.executeUpdate();
+                } catch (SQLException e) {
+                    throw new OsmosisRuntimeException("Unable to insert a relation member into the database.", e);
+                }
+            }
+        }
+    }
+    
+    
+    private void populateCurrentNodes() {
+        // Copy data into the current node tables.
+        for (long i = 0; i < maxNodeId; i += LOAD_CURRENT_NODE_ROW_COUNT) {
+            // Node
+            try {
+                loadCurrentNodesStatement.setLong(1, i);
+                loadCurrentNodesStatement.setLong(2, i + LOAD_CURRENT_NODE_ROW_COUNT);
+
+                loadCurrentNodesStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current nodes.", e);
+            }
+
+            // Node tags
+            try {
+                loadCurrentNodeTagsStatement.setLong(1, i);
+                loadCurrentNodeTagsStatement.setLong(2, i + LOAD_CURRENT_NODE_ROW_COUNT);
+
+                loadCurrentNodeTagsStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current node tags.", e);
+            }
+
+            dbCtx.commit();
+        }
+    }
+    
+    
+    private void populateCurrentWays() {
+        for (long i = 0; i < maxWayId; i += LOAD_CURRENT_WAY_ROW_COUNT) {
+            // Way
+            try {
+                loadCurrentWaysStatement.setLong(1, i);
+                loadCurrentWaysStatement.setLong(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
+
+                loadCurrentWaysStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current ways.", e);
+            }
+
+            // Way tags
+            try {
+                loadCurrentWayTagsStatement.setLong(1, i);
+                loadCurrentWayTagsStatement.setLong(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
+
+                loadCurrentWayTagsStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current way tags.", e);
+            }
+
+            // Way nodes
+            try {
+                loadCurrentWayNodesStatement.setLong(1, i);
+                loadCurrentWayNodesStatement.setLong(2, i + LOAD_CURRENT_WAY_ROW_COUNT);
+
+                loadCurrentWayNodesStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current way nodes.", e);
+            }
+
+            dbCtx.commit();
+        }
+    }
+    
+    
+    private void populateCurrentRelations() {
+        for (long i = 0; i < maxRelationId; i += LOAD_CURRENT_RELATION_ROW_COUNT) {
+            // Way
+            try {
+                loadCurrentRelationsStatement.setLong(1, i);
+                loadCurrentRelationsStatement.setLong(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
+
+                loadCurrentRelationsStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current relations.", e);
+            }
+
+            // Relation tags
+            try {
+                loadCurrentRelationTagsStatement.setLong(1, i);
+                loadCurrentRelationTagsStatement.setLong(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
+
+                loadCurrentRelationTagsStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current relation tags.", e);
+            }
+
+            // Relation members
+            try {
+                loadCurrentRelationMembersStatement.setLong(1, i);
+                loadCurrentRelationMembersStatement.setLong(2, i + LOAD_CURRENT_RELATION_ROW_COUNT);
+
+                loadCurrentRelationMembersStatement.execute();
+
+            } catch (SQLException e) {
+                throw new OsmosisRuntimeException("Unable to load current relation members.", e);
+            }
+
+            dbCtx.commit();
+        }
+    }
+    
+    
+    private void populateCurrentTables() {
+    	if (populateCurrentTables) {
+    		populateCurrentNodes();
+    		populateCurrentWays();
+    		populateCurrentRelations();
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+
+
+    /**
+     * Writes any buffered data to the database and commits.
+     */
+    @Override
+    public void complete() {
+        initialize();
+
+        flushNodes(true);
+        flushNodeTags(true);
+        flushWays(true);
+        flushWayTags(true);
+        flushWayNodes(true);
+        flushRelations(true);
+        flushRelationTags(true);
+        flushRelationMembers(true);
+
+        // Re-enable indexes now that the load has completed.
+        dbCtx.enableIndexes(DISABLE_KEY_TABLES);
+
+        populateCurrentTables();
+
+        // Unlock tables (if they were locked) now that we have completed.
+        if (lockTables) {
+        	dbCtx.unlockTables(LOCK_TABLES);
+        }
+
+        dbCtx.commit();
+    }
+
+    /**
+     * Releases all database resources.
+     */
+    public void release() {
+        userManager.release();
+
+        dbCtx.release();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(EntityContainer entityContainer) {
+    	Entity entity;
+    	
+        initialize();
+
+        entity = entityContainer.getEntity();
+        userManager.addOrUpdateUser(entityContainer.getEntity().getUser());
+        changesetManager.addChangesetIfRequired(entity.getChangesetId(), entity.getUser());
+
+        entityContainer.process(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(BoundContainer boundContainer) {
+        // Do nothing.
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(NodeContainer nodeContainer) {
+        Node node;
+        long nodeId;
+
+        node = nodeContainer.getEntity();
+        nodeId = node.getId();
+
+        if (nodeId >= maxNodeId) {
+            maxNodeId = nodeId + 1;
+        }
+
+        nodeBuffer.add(node);
+
+        flushNodes(false);
+    }
+
+    /**
+     * Process the node tags.
+     * 
+     * @param node The node to be processed.
+     */
+    private void addNodeTags(Node node) {
+        for (Tag tag : node.getTags()) {
+            nodeTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(node.getId(), tag), node
+                    .getVersion()));
+        }
+
+        flushNodeTags(false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(WayContainer wayContainer) {
+        Way way;
+        long wayId;
+
+        flushNodes(true);
+
+        way = wayContainer.getEntity();
+        wayId = way.getId();
+
+        if (wayId >= maxWayId) {
+            maxWayId = wayId + 1;
+        }
+
+        wayBuffer.add(way);
+
+        flushWays(false);
+    }
+
+    /**
+     * Process the way tags.
+     * 
+     * @param way The way to be processed.
+     */
+    private void addWayTags(Way way) {
+        for (Tag tag : way.getTags()) {
+            wayTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(way.getId(), tag), way
+                    .getVersion()));
+        }
+
+        flushWayTags(false);
+    }
+
+    /**
+     * Process the way nodes.
+     * 
+     * @param way The way to be processed.
+     */
+    private void addWayNodes(Way way) {
+        List<WayNode> nodeReferenceList;
+
+        nodeReferenceList = way.getWayNodes();
+
+        for (int i = 0; i < nodeReferenceList.size(); i++) {
+            wayNodeBuffer.add(new DbFeatureHistory<DbOrderedFeature<WayNode>>(new DbOrderedFeature<WayNode>(
+                    way.getId(), nodeReferenceList.get(i), i + 1), way.getVersion()));
+        }
+
+        flushWayNodes(false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(RelationContainer relationContainer) {
+        Relation relation;
+        long relationId;
+
+        flushWays(true);
+
+        relation = relationContainer.getEntity();
+        relationId = relation.getId();
+
+        if (relationId >= maxRelationId) {
+            maxRelationId = relationId + 1;
+        }
+
+        relationBuffer.add(relation);
+
+        flushRelations(false);
+    }
+
+    /**
+     * Process the relation tags.
+     * 
+     * @param relation The relation to be processed.
+     */
+    private void addRelationTags(Relation relation) {
+        for (Tag tag : relation.getTags()) {
+            relationTagBuffer.add(new DbFeatureHistory<DbFeature<Tag>>(new DbFeature<Tag>(relation.getId(), tag),
+                    relation.getVersion()));
+        }
+
+        flushRelationTags(false);
+    }
+
+    /**
+     * Process the relation members.
+     * 
+     * @param relation The relation to be processed.
+     */
+    private void addRelationMembers(Relation relation) {
+        List<RelationMember> memberReferenceList;
+
+        memberReferenceList = relation.getMembers();
+
+        for (int i = 0; i < memberReferenceList.size(); i++) {
+            relationMemberBuffer.add(new DbFeatureHistory<DbOrderedFeature<RelationMember>>(
+                    new DbOrderedFeature<RelationMember>(relation.getId(), memberReferenceList.get(i), i + 1), relation
+                            .getVersion()));
+        }
+
+        flushRelationMembers(false);
+    }
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriterFactory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriterFactory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriterFactory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbWriterFactory.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ActionChangeWriter.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ActionChangeWriter.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ActionChangeWriter.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ActionChangeWriter.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/AllEntityDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/AllEntityDao.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/AllEntityDao.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/AllEntityDao.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeWriter.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeWriter.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeWriter.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangeWriter.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManager.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManager.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManager.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManager.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DeltaToDiffReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DeltaToDiffReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DeltaToDiffReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DeltaToDiffReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityContainerReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityContainerReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityContainerReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityContainerReader.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java
new file mode 100644
index 0000000..6610303
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDao.java
@@ -0,0 +1,557 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainerFactory;
+import org.openstreetmap.osmosis.core.database.DbFeature;
+import org.openstreetmap.osmosis.core.database.DbFeatureHistory;
+import org.openstreetmap.osmosis.core.database.DbFeatureHistoryComparator;
+import org.openstreetmap.osmosis.core.database.DbFeatureHistoryRowMapper;
+import org.openstreetmap.osmosis.core.database.DbFeatureRowMapper;
+import org.openstreetmap.osmosis.core.database.RowMapperListener;
+import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+
+/**
+ * Provides functionality common to all top level entity daos.
+ * 
+ * @param <T>
+ *            The entity type to be supported.
+ */
+public abstract class EntityDao<T extends Entity> {
+	private static final Logger LOG = Logger.getLogger(EntityDao.class.getName());
+
+	private JdbcTemplate jdbcTemplate;
+	private NamedParameterJdbcTemplate namedParamJdbcTemplate;
+	private String entityName;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param jdbcTemplate
+	 *            Used to access the database.
+	 * @param entityName
+	 *            The name of the entity. Used for building dynamic sql queries.
+	 */
+	protected EntityDao(JdbcTemplate jdbcTemplate, String entityName) {
+		this.jdbcTemplate = jdbcTemplate;
+		this.namedParamJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+		this.entityName = entityName;
+	}
+	
+	
+	/**
+	 * Provides access to the named parameter jdbc template.
+	 * 
+	 * @return The jdbc template.
+	 */
+	protected NamedParameterJdbcTemplate getNamedParamJdbcTemplate() {
+		return namedParamJdbcTemplate;
+	}
+
+
+	/**
+	 * Produces an array of additional column names specific to this entity type to be returned by
+	 * entity queries.
+	 * 
+	 * @return The column names.
+	 */
+	protected abstract String[] getTypeSpecificFieldNames();
+	
+	
+	/**
+	 * Creates a row mapper that receives common entity data objects and produces full entity
+	 * objects.
+	 * 
+	 * @param entityListener
+	 *            The full entity object listener.
+	 * @return The full entity row mapper.
+	 */
+	protected abstract RowMapperListener<CommonEntityData> getEntityRowMapper(RowMapperListener<T> entityListener);
+	
+	
+	/**
+	 * Gets the entity container factory for the entity type.
+	 * 
+	 * @return The factory.
+	 */
+	protected abstract EntityContainerFactory<T> getContainerFactory();
+
+
+	/**
+	 * Gets the history feature populators for the entity type.
+	 * 
+	 * @param selectedEntityStatement
+	 *            The statement for obtaining the id and version pairs of entity records selected.
+	 * @param parameterSource
+	 *            The parameters required to execute the selected entity statement.
+	 * @return The history feature populators.
+	 */
+	protected abstract List<FeatureHistoryPopulator<T, ?, ?>> getFeatureHistoryPopulators(
+			String selectedEntityStatement, MapSqlParameterSource parameterSource);
+	
+	
+	private String buildFeaturelessEntityHistoryQuery(String selectedEntityStatement) {
+		StringBuilder sql;
+
+		sql = new StringBuilder();
+		sql.append("SELECT e.");
+		sql.append(entityName);
+		sql.append("_id AS id, e.version, e.timestamp, e.visible, u.data_public,");
+		sql.append(" u.id AS user_id, u.display_name, e.changeset_id");
+
+		for (String fieldName : getTypeSpecificFieldNames()) {
+			sql.append(", e.");
+			sql.append(fieldName);
+		}
+
+		sql.append(" FROM ");
+		sql.append(entityName);
+		sql.append("s e");
+		sql.append(" INNER JOIN ");
+		sql.append(selectedEntityStatement);
+		sql.append(" t ON e.");
+		sql.append(entityName);
+		sql.append("_id = t.");
+		sql.append(entityName);
+		sql.append("_id AND e.version = t.version");
+		sql.append(" INNER JOIN changesets c ON e.changeset_id = c.id INNER JOIN users u ON c.user_id = u.id");
+		
+		LOG.log(Level.FINER, "Entity history query: " + sql);
+
+		return sql.toString();
+	}
+
+
+	private ReleasableIterator<EntityHistory<T>> getFeaturelessEntityHistory(
+			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
+		
+		FileBasedSort<EntityHistory<T>> sortingStore =
+			new FileBasedSort<EntityHistory<T>>(
+				new SingleClassObjectSerializationFactory(EntityHistory.class),
+				new EntityHistoryComparator<T>(), true);
+		
+		try {
+			String sql;
+			SortingStoreRowMapperListener<EntityHistory<T>> storeListener;
+			EntityHistoryRowMapper<T> entityHistoryRowMapper;
+			RowMapperListener<CommonEntityData> entityRowMapper;
+			EntityDataRowMapper entityDataRowMapper;
+			ReleasableIterator<EntityHistory<T>> resultIterator;
+			
+			sql = buildFeaturelessEntityHistoryQuery(selectedEntityStatement);
+			
+			// Sends all received data into the object store.
+			storeListener = new SortingStoreRowMapperListener<EntityHistory<T>>(sortingStore);
+			// Retrieves the visible attribute allowing modifies to be distinguished
+			// from deletes.
+			entityHistoryRowMapper = new EntityHistoryRowMapper<T>(storeListener);
+			// Retrieves the entity type specific columns and builds the entity objects.
+			entityRowMapper = getEntityRowMapper(entityHistoryRowMapper);
+			// Retrieves the common entity information.
+			entityDataRowMapper = new EntityDataRowMapper(entityRowMapper, false);
+			
+			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
+			namedParamJdbcTemplate.query(sql, parameterSource, entityDataRowMapper);
+			
+			// Open a iterator on the store that will release the store upon completion.
+			resultIterator = new StoreReleasingIterator<EntityHistory<T>>(sortingStore.iterate(), sortingStore);
+			
+			// The store itself shouldn't be released now that it has been attached to the iterator.
+			sortingStore = null;
+			
+			return resultIterator;
+			
+		} finally {
+			if (sortingStore != null) {
+				sortingStore.release();
+			}
+		}
+	}
+	
+	
+	private ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> getTagHistory(String selectedEntityStatement,
+			MapSqlParameterSource parameterSource) {
+		
+		FileBasedSort<DbFeatureHistory<DbFeature<Tag>>> sortingStore =
+			new FileBasedSort<DbFeatureHistory<DbFeature<Tag>>>(
+				new SingleClassObjectSerializationFactory(DbFeatureHistory.class),
+				new DbFeatureHistoryComparator<Tag>(), true);
+		
+		try {
+			String sql;
+			SortingStoreRowMapperListener<DbFeatureHistory<DbFeature<Tag>>> storeListener;
+			DbFeatureHistoryRowMapper<DbFeature<Tag>> dbFeatureHistoryRowMapper;
+			DbFeatureRowMapper<Tag> dbFeatureRowMapper;
+			TagRowMapper tagRowMapper;
+			ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> resultIterator;
+			
+			sql =
+				"SELECT et."
+				+ entityName
+				+ "_id AS id, et.k, et.v, et.version"
+				+ " FROM "
+				+ entityName
+				+ "_tags et"
+				+ " INNER JOIN "
+				+ selectedEntityStatement
+				+ " t ON et."
+				+ entityName
+				+ "_id = t."
+				+ entityName
+				+ "_id AND et.version = t.version";
+			
+			LOG.log(Level.FINER, "Tag history query: " + sql);
+			
+			// Sends all received data into the object store.
+			storeListener = new SortingStoreRowMapperListener<DbFeatureHistory<DbFeature<Tag>>>(sortingStore);
+			// Retrieves the version information associated with the tag.
+			dbFeatureHistoryRowMapper = new DbFeatureHistoryRowMapper<DbFeature<Tag>>(storeListener);
+			// Retrieves the entity information associated with the tag.
+			dbFeatureRowMapper = new DbFeatureRowMapper<Tag>(dbFeatureHistoryRowMapper);
+			// Retrieves the basic tag information.
+			tagRowMapper = new TagRowMapper(dbFeatureRowMapper);
+			
+			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
+			namedParamJdbcTemplate.query(sql, parameterSource, tagRowMapper);
+			
+			// Open a iterator on the store that will release the store upon completion.
+			resultIterator = new StoreReleasingIterator<DbFeatureHistory<DbFeature<Tag>>>(sortingStore.iterate(),
+					sortingStore);
+			
+			// The store itself shouldn't be released now that it has been attached to the iterator.
+			sortingStore = null;
+			
+			return resultIterator;
+			
+		} finally {
+			if (sortingStore != null) {
+				sortingStore.release();
+			}
+		}
+	}
+	
+	
+	private ReleasableIterator<EntityHistory<T>> getEntityHistory(
+			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
+		ReleasableContainer releasableContainer;
+		
+		releasableContainer = new ReleasableContainer();
+		try {
+			ReleasableIterator<EntityHistory<T>> entityIterator;
+			ReleasableIterator<DbFeatureHistory<DbFeature<Tag>>> tagIterator;
+			List<FeatureHistoryPopulator<T, ?, ?>> featurePopulators;
+			EntityHistoryReader<T> entityHistoryReader;
+			
+			entityIterator = releasableContainer.add(
+					getFeaturelessEntityHistory(selectedEntityStatement, parameterSource));
+			tagIterator = releasableContainer.add(
+					getTagHistory(selectedEntityStatement, parameterSource));
+			
+			featurePopulators = getFeatureHistoryPopulators(selectedEntityStatement, parameterSource);
+			for (FeatureHistoryPopulator<T, ?, ?> featurePopulator : featurePopulators) {
+				releasableContainer.add(featurePopulator);
+			}
+			
+			entityHistoryReader = new EntityHistoryReader<T>(entityIterator, tagIterator, featurePopulators);
+			
+			// The sources are now all attached to the history reader so we don't want to release
+			// them in the finally block.
+			releasableContainer.clear();
+			
+			return entityHistoryReader;
+			
+		} finally {
+			releasableContainer.release();
+		}
+	}
+	
+	
+	private ReleasableIterator<ChangeContainer> getChangeHistory(
+			String selectedEntityStatement, MapSqlParameterSource parameterSource) {
+		
+		return new ChangeReader<T>(getEntityHistory(selectedEntityStatement, parameterSource), getContainerFactory());
+	}
+	
+	
+	private List<Integer> buildTransactionRanges(long bottomTransactionId, long topTransactionId) {
+		List<Integer> rangeValues;
+		int currentXid;
+		int finishXid;
+		
+		// Begin building the values to use in the WHERE clause transaction ranges. Each pair of ids
+		// in this list will become a range selection.
+		rangeValues = new ArrayList<Integer>();
+		
+		// If the bottom and top values are identical then no ranges are required. If we don't add
+		// this check here we could end up querying for all 4 billion transaction ids or all data in
+		// the database.
+		if (bottomTransactionId == topTransactionId) {
+			return rangeValues;
+		}
+		
+		// We must now convert the top and bottom long representations of xids into their integer
+		// format because that is how they are indexed in the database.
+		
+		// The bottom id is the first transaction id that has not been read yet, so we begin reading from it.
+		currentXid = (int) bottomTransactionId;
+		rangeValues.add(currentXid);
+		
+		// The top transaction id is the first unassigned transaction id, so we must stop reading one short of that.
+		finishXid = ((int) topTransactionId) - 1;
+		
+		// Process until we have enough ranges to reach the finish xid.
+		do {
+			// Determine how to terminate the current transaction range.
+			if (currentXid <= 2 && finishXid >= 0) {
+				// The range overlaps special ids 0-2 which should never be queried on.
+				
+				// Terminate the current range before the special values.
+				rangeValues.add(-1);
+				// Begin the new range after the special values.
+				rangeValues.add(3);
+				
+				currentXid = 3;
+				
+			} else if (finishXid < currentXid) {
+				// The range crosses the integer overflow point. Only do this check once we are
+				// past 2 because the xid special values 0-2 may need to be crossed first.
+				
+				// Terminate the current range at the maximum int value.
+				rangeValues.add(Integer.MAX_VALUE);
+				// Begin a new range at the minimum int value.
+				rangeValues.add(Integer.MIN_VALUE);
+				
+				currentXid = Integer.MIN_VALUE;
+				
+			} else {
+				// There are no problematic transaction id values between the current value and
+				// the top transaction id so terminate the current range at the top transaction
+				// id.
+				rangeValues.add(finishXid);
+				currentXid = finishXid;
+			}
+		} while (currentXid != finishXid);
+		
+		return rangeValues;
+	}
+	
+	
+	private void buildTransactionRangeWhereClause(StringBuilder sql, MapSqlParameterSource parameters,
+			long bottomTransactionId, long topTransactionId) {
+		List<Integer> rangeValues;
+		
+		// Determine the transaction ranges required to select all transactions between the bottom
+		// and top values. This takes into account transaction id wrapping issues and reserved ids.
+		rangeValues = buildTransactionRanges(bottomTransactionId, topTransactionId);
+		
+		// Create a range clause for each range pair.
+		for (int i = 0; i < rangeValues.size(); i = i + 2) {
+			if (i > 0) {
+				sql.append(" OR ");
+			}
+			sql.append("(");
+			sql.append("xid_to_int4(xmin) >= :rangeValue").append(i);
+			sql.append(" AND ");
+			sql.append("xid_to_int4(xmin) <= :rangeValue").append(i + 1);
+			sql.append(")");
+			
+			parameters.addValue("rangeValue" + i, rangeValues.get(i), Types.INTEGER);
+			parameters.addValue("rangeValue" + (i + 1), rangeValues.get(i + 1), Types.INTEGER);
+		}
+	}
+	
+	
+	private void buildTransactionIdListWhereClause(StringBuilder sql, List<Long> transactionIdList) {
+		for (int i = 0; i < transactionIdList.size(); i++) {
+			if (i > 0) {
+				sql.append(",");
+			}
+		
+			// Must cast to int to allow the integer based xmin index to be used correctly.
+			sql.append((int) transactionIdList.get(i).longValue());
+		}
+	}
+
+
+	/**
+	 * Retrieves the changes that have were made by a set of transactions.
+	 * 
+	 * @param predicates
+	 *            Contains the predicates defining the transactions to be queried.
+	 * @return An iterator pointing at the identified records.
+	 */
+	public ReleasableIterator<ChangeContainer> getHistory(ReplicationQueryPredicates predicates) {
+		String selectedEntityTableName;
+		StringBuilder sql;
+		MapSqlParameterSource parameterSource;
+		ReleasableIterator<ChangeContainer> historyIterator;
+		
+		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
+		// prevent this. Note that this is not recommended practice according to documentation
+		// but fixing this would require modifying the table statistics gathering
+		// configuration on the production database to produce better plans.
+		jdbcTemplate.execute(
+				"set local enable_seqscan = false;"
+				+ "set local enable_mergejoin = false;"
+				+ "set local enable_hashjoin = false");
+		
+		parameterSource = new MapSqlParameterSource();
+		
+		selectedEntityTableName = "tmp_" + entityName + "s";
+		
+		sql = new StringBuilder();
+		sql.append("CREATE TEMPORARY TABLE ");
+		sql.append(selectedEntityTableName);
+		sql.append(" ON COMMIT DROP");
+		sql.append(" AS SELECT ");
+		sql.append(entityName);
+		sql.append("_id, version FROM ");
+		sql.append(entityName);
+		sql.append("s WHERE ((");
+		// Add the main transaction ranges to the where clause.
+		buildTransactionRangeWhereClause(sql, parameterSource, predicates.getBottomTransactionId(), predicates
+				.getTopTransactionId());
+		sql.append(")");
+		// If previously active transactions have become ready since the last invocation we include those as well.
+		if (predicates.getReadyList().size() > 0) {
+			sql.append(" OR xid_to_int4(xmin) IN (");
+			buildTransactionIdListWhereClause(sql, predicates.getReadyList());
+			sql.append(")");
+		}
+		sql.append(")");
+		// Any active transactions must be explicitly excluded.
+		if (predicates.getActiveList().size() > 0) {
+			sql.append(" AND xid_to_int4(xmin) NOT IN (");
+			buildTransactionIdListWhereClause(sql, predicates.getActiveList());
+			sql.append(")");
+		}
+		
+		sql.append(" AND redaction_id IS NULL");
+		
+		LOG.log(Level.FINER, "Entity identification query: " + sql);
+		
+		namedParamJdbcTemplate.update(sql.toString(), parameterSource);
+		
+		jdbcTemplate.update("ALTER TABLE ONLY " + selectedEntityTableName
+				+ " ADD CONSTRAINT pk_" + selectedEntityTableName
+				+ " PRIMARY KEY (" + entityName + "_id, version)");
+		jdbcTemplate.update("ANALYZE " + selectedEntityTableName);
+		
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.log(Level.FINER,
+					jdbcTemplate.queryForInt("SELECT Count(" + entityName + "_id) FROM " + selectedEntityTableName)
+					+ " " + entityName + " records located.");
+		}
+		
+		// Extract the data and obtain an iterator for the results.
+		historyIterator = getChangeHistory(selectedEntityTableName, new MapSqlParameterSource());
+		
+		// The temp table is no longer required and can be deleted.
+		jdbcTemplate.execute("DROP TABLE " + selectedEntityTableName);
+		
+		return historyIterator;
+	}
+
+
+	/**
+	 * Retrieves the changes that have were made between two points in time.
+	 * 
+	 * @param intervalBegin
+	 *            Marks the beginning (inclusive) of the time interval to be checked.
+	 * @param intervalEnd
+	 *            Marks the end (exclusive) of the time interval to be checked.
+	 * @return An iterator pointing at the identified records.
+	 */
+	public ReleasableIterator<ChangeContainer> getHistory(Date intervalBegin, Date intervalEnd) {
+		String selectedEntityTableName;
+		StringBuilder sql;
+		MapSqlParameterSource parameterSource;
+		ReleasableIterator<ChangeContainer> historyIterator;
+		
+		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
+		// prevent this. Note that this is not recommended practice according to documentation
+		// but fixing this would require modifying the table statistics gathering
+		// configuration on the production database to produce better plans.
+		jdbcTemplate.execute(
+				"set local enable_seqscan = false;"
+				+ "set local enable_mergejoin = false;"
+				+ "set local enable_hashjoin = false");
+		
+		selectedEntityTableName = "tmp_" + entityName + "s";
+		
+		sql = new StringBuilder();
+		sql.append("CREATE TEMPORARY TABLE ");
+		sql.append(selectedEntityTableName);
+		sql.append(" ON COMMIT DROP");
+		sql.append(" AS SELECT ");
+		sql.append(entityName);
+		sql.append("_id, version FROM ");
+		sql.append(entityName);
+		sql.append("s WHERE timestamp > :intervalBegin AND timestamp <= :intervalEnd");
+		
+		LOG.log(Level.FINER, "Entity identification query: " + sql);
+
+		parameterSource = new MapSqlParameterSource();
+		parameterSource.addValue("intervalBegin", intervalBegin, Types.TIMESTAMP);
+		parameterSource.addValue("intervalEnd", intervalEnd, Types.TIMESTAMP);
+		
+		namedParamJdbcTemplate.update(sql.toString(), parameterSource);
+		
+		jdbcTemplate.update("ANALYZE " + selectedEntityTableName);
+		
+		// Extract the data and obtain an iterator for the results.
+		historyIterator = getChangeHistory(selectedEntityTableName, new MapSqlParameterSource());
+		
+		// The temp table is no longer required and can be deleted.
+		jdbcTemplate.execute("DROP TABLE " + selectedEntityTableName);
+		
+		return historyIterator;
+	}
+	
+	
+	/**
+	 * Retrieves all changes in the database.
+	 * 
+	 * @return An iterator pointing at the identified records.
+	 */
+	public ReleasableIterator<ChangeContainer> getHistory() {
+		// Join the entity table to itself which will return all records.
+		return getChangeHistory(entityName + "s", new MapSqlParameterSource());
+	}
+	
+	
+	/**
+	 * Retrieves all current data in the database.
+	 * 
+	 * @return An iterator pointing at the current records.
+	 */
+	public ReleasableIterator<EntityContainer> getCurrent() {
+		// Join the entity table to the current version of itself which will return all current
+		// records.
+		return new EntityContainerReader<T>(
+				getEntityHistory("(SELECT id AS " + entityName + "_id, version FROM current_"
+							+ entityName + "s WHERE visible = TRUE)",
+						new MapSqlParameterSource()), getContainerFactory());
+	}
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDataRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDataRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDataRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityDataRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistory.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistory.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistory.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistory.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryComparator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryComparator.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryComparator.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryComparator.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryListReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryListReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryListReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryListReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntityHistoryRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntitySnapshotReader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntitySnapshotReader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntitySnapshotReader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/EntitySnapshotReader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FeatureHistoryPopulator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FeatureHistoryPopulator.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FeatureHistoryPopulator.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/FeatureHistoryPopulator.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeParser.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeParser.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeParser.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeParser.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeRenderer.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeRenderer.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeRenderer.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MemberTypeRenderer.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeDao.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeDao.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeDao.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/NodeRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationDao.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationDao.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationDao.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationMemberRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationMemberRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationMemberRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationMemberRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/RelationRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationQueryPredicates.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationQueryPredicates.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationQueryPredicates.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationQueryPredicates.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSource.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSource.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSource.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSource.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java
new file mode 100644
index 0000000..710404c
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationState.java
@@ -0,0 +1,237 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+
+/**
+ * Contains the state to be remembered between replication invocations. This state ensures that no
+ * data is missed during replication, and none is repeated.
+ */
+public class ReplicationState extends org.openstreetmap.osmosis.replication.common.ReplicationState {
+	private long txnMax;
+	private long txnMaxQueried;
+	private List<Long> txnActive;
+	private List<Long> txnReady;
+
+
+	/**
+	 * Creates a new instance with all values set to defaults.
+	 */
+	public ReplicationState() {
+		super();
+		this.txnMax = 0;
+		this.txnMaxQueried = 0;
+		this.txnActive = new ArrayList<Long>();
+		this.txnReady = new ArrayList<Long>();
+	}
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param txnMax
+	 *            The maximum transaction id in the database.
+	 * @param txnMaxQueried
+	 *            The maximum transaction id currently replicated from the database.
+	 * @param txnActive
+	 *            The currently active transaction ids.
+	 * @param txnReady
+	 *            The previously active transaction ids that can now be queried.
+	 * @param timestamp
+	 *            The maximum timestamp of data currently read from the database.
+	 * @param sequenceNumber
+	 *            The replication sequence number.
+	 */
+	public ReplicationState(long txnMax, long txnMaxQueried, List<Long> txnActive, List<Long> txnReady,
+			Date timestamp, long sequenceNumber) {
+		super(timestamp, sequenceNumber);
+		this.txnMax = txnMax;
+		this.txnMaxQueried = txnMaxQueried;
+		this.txnActive = new ArrayList<Long>(txnActive);
+		this.txnReady = new ArrayList<Long>(txnReady);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param properties
+	 *            The properties to load state from.
+	 */
+	public ReplicationState(Map<String, String> properties) {
+		load(properties);
+	}
+	
+	
+	/**
+	 * Loads all state from the provided properties object.
+	 * 
+	 * @param properties
+	 *            The properties to be read.
+	 */
+	public void load(Map<String, String> properties) {
+		super.load(properties);
+		txnMax = Long.parseLong(properties.get("txnMax"));
+		txnMaxQueried = Long.parseLong(properties.get("txnMaxQueried"));
+		txnActive = fromString(properties.get("txnActiveList"));
+		txnReady = fromString(properties.get("txnReadyList"));
+	}
+
+
+	@Override
+	public void store(Map<String, String> properties) {
+		super.store(properties);
+		properties.put("txnMax", Long.toString(txnMax));
+		properties.put("txnMaxQueried", Long.toString(txnMaxQueried));
+		properties.put("txnActiveList", toString(txnActive));
+		properties.put("txnReadyList", toString(txnReady));
+	}
+	
+	
+	@Override
+	public Map<String, String> store() {
+		Map<String, String> properties = new HashMap<String, String>();
+		store(properties);
+		return properties;
+	}
+
+
+	private String toString(List<Long> values) {
+		StringBuilder buffer;
+		
+		buffer = new StringBuilder();
+		for (long value : values) {
+			if (buffer.length() > 0) {
+				buffer.append(',');
+			}
+			buffer.append(value);
+		}
+		
+		return buffer.toString();
+	}
+	
+	
+	private List<Long> fromString(String values) {
+		StringTokenizer tokens;
+		List<Long> result;
+		
+		tokens = new StringTokenizer(values, ",");
+		
+		result = new ArrayList<Long>();
+		while (tokens.hasMoreTokens()) {
+			result.add(Long.parseLong(tokens.nextToken()));
+		}
+		
+		return result;
+	}
+
+
+	/**
+	 * Gets the maximum transaction id in the database.
+	 * 
+	 * @return The transaction id.
+	 */
+	public long getTxnMax() {
+		return txnMax;
+	}
+	
+
+	/**
+	 * Sets the maximum transaction id in the database.
+	 * 
+	 * @param txnMax
+	 *            The transaction id.
+	 */
+	public void setTxnMax(long txnMax) {
+		this.txnMax = txnMax;
+	}
+
+
+	/**
+	 * Gets the maximum transaction id currently replicated from the database.
+	 * 
+	 * @return The transaction id.
+	 */
+	public long getTxnMaxQueried() {
+		return txnMaxQueried;
+	}
+
+
+	/**
+	 * Sets the maximum transaction id currently replicated from the database.
+	 * 
+	 * @param txnMaxQueried
+	 *            The transaction id.
+	 */
+	public void setTxnMaxQueried(long txnMaxQueried) {
+		this.txnMaxQueried = txnMaxQueried;
+	}
+
+
+	/**
+	 * Gets the currently active transaction ids. These cannot be replicated until they have
+	 * committed.
+	 * 
+	 * @return The list of transaction ids.
+	 */
+	public List<Long> getTxnActive() {
+		return txnActive;
+	}
+
+
+	/**
+	 * Gets the previously active transaction ids that can now be queried.
+	 * 
+	 * @return The list of transaction ids.
+	 */
+	public List<Long> getTxnReady() {
+		return txnReady;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		boolean result;
+		
+		if (obj instanceof ReplicationState) {
+			ReplicationState compareState = (ReplicationState) obj;
+			
+			if (super.equals(obj)
+					&& txnMax == compareState.txnMax
+					&& txnMaxQueried == compareState.txnMaxQueried
+					&& txnActive.equals(compareState.txnActive)
+					&& txnReady.equals(compareState.txnReady)) {
+				result = true;
+			} else {
+				result = false;
+			}
+		} else {
+			result = false;
+		}
+		
+		return result;
+	}
+
+
+	@Override
+	public int hashCode() {
+		return super.hashCode() + (int) txnMax + (int) txnMaxQueried;
+	}
+
+
+	@Override
+	public String toString() {
+		return "ReplicationState(txnMax=" + txnMax + ", txnMaxQueried=" + txnMaxQueried + ", txnActive=" + txnActive
+				+ ", txnReady=" + txnReady + ", timestamp=" + getTimestamp() + ", sequenceNumber="
+				+ getSequenceNumber() + ")";
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java
new file mode 100644
index 0000000..9a4dc4c
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/Replicator.java
@@ -0,0 +1,408 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * Replicates changes from the database utilising transaction snapshots.
+ */
+public class Replicator {
+
+	private static final Logger LOG = Logger.getLogger(Replicator.class.getName());
+
+	/**
+	 * The number of special transactions beginning from 0.
+	 */
+	private static final int SPECIAL_TRANSACTION_OFFSET = 3;
+	/**
+	 * This is the maximum number of transaction ids sent in a single query. If
+	 * larger than 2 power 16 it fails due to a 16 bit number failing, but still
+	 * fails below that with a stack limit being exceeded. The current value is
+	 * near to the maximum value known to work, it will work slightly higher but
+	 * this is a round number. It is dependent on the max_stack_depth parameter
+	 * defined in postgresql.conf.
+	 */
+	private static final int TRANSACTION_QUERY_SIZE_MAX = 25000;
+
+	private ChangeSink changeSink;
+	private ReplicationSource source;
+	private TransactionManager txnManager;
+	private SystemTimeLoader systemTimeLoader;
+	private int iterations;
+	private int minInterval;
+	private int maxInterval;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param source
+	 *            The source for all replication changes.
+	 * @param changeSink
+	 *            The destination for all replicated changes.
+	 * @param snapshotLoader
+	 *            Loads transaction snapshots from the database.
+	 * @param systemTimeLoader
+	 *            Loads the current system time from the database.
+	 * @param iterations
+	 *            The number of replication intervals to execute. 0 means
+	 *            infinite.
+	 * @param minInterval
+	 *            The minimum number of milliseconds between intervals.
+	 * @param maxInterval
+	 *            The maximum number of milliseconds between intervals if no new
+	 *            data is available. This isn't a hard limit because processing
+	 *            latency may increase the duration.
+	 */
+	public Replicator(ReplicationSource source, ChangeSink changeSink,
+			TransactionManager snapshotLoader, SystemTimeLoader systemTimeLoader, int iterations,
+			int minInterval, int maxInterval) {
+		this.source = source;
+		this.changeSink = changeSink;
+		this.txnManager = snapshotLoader;
+		this.systemTimeLoader = systemTimeLoader;
+		this.iterations = iterations;
+		this.minInterval = minInterval;
+		this.maxInterval = maxInterval;
+	}
+
+
+	/**
+	 * Obtains a new transaction shapshot from the database and updates the
+	 * state to match.
+	 * 
+	 * @param state
+	 *            The replication state.
+	 */
+	private void obtainNewSnapshot(ReplicationState state) {
+		TransactionSnapshot transactionSnapshot;
+
+		// Obtain the latest transaction snapshot from the database.
+		transactionSnapshot = txnManager.getTransactionSnapshot();
+
+		// Update the xmax value.
+		state.setTxnMax(transactionSnapshot.getXMax());
+
+		// Any items in the old active transaction list but not in the new
+		// active transaction list must be added to the ready list.
+		for (Iterator<Long> i = state.getTxnActive().iterator(); i.hasNext();) {
+			Long id;
+
+			id = i.next();
+
+			if (!transactionSnapshot.getXIpList().contains(id)) {
+				// They only need to be added if the maximum queried xmax has
+				// been passed.
+				if (compareTxnIds(id, state.getTxnMaxQueried()) < 0) {
+					state.getTxnReady().add(id);
+				}
+			}
+		}
+
+		// The active transaction list must be updated to match the latest
+		// snapshot.
+		state.getTxnActive().clear();
+		state.getTxnActive().addAll(transactionSnapshot.getXIpList());
+
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.finer("Updated replication state with new snapshot, maxTxnQueried=" + state.getTxnMaxQueried()
+					+ ", maxTxn=" + state.getTxnMax() + ", txnActiveList=" + state.getTxnActive() + ", txnReadyList="
+					+ state.getTxnReady() + ".");
+		}
+	}
+
+
+	/**
+	 * This performs a comparison of the two transaction ids using 32-bit
+	 * arithmetic. The result is (id1 - id2), but with both numbers cast to an
+	 * integer prior to the comparison. This provides a correct result even in
+	 * the case where the transaction id has wrapped around to 0.
+	 * 
+	 * @param id1
+	 *            The first transaction id.
+	 * @param id2
+	 *            The second transaction id.
+	 * @return The difference between the two transaction ids (id1 - id2).
+	 */
+	private int compareTxnIds(long id1, long id2) {
+		return ((int) id1) - ((int) id2);
+	}
+
+
+	/**
+	 * This adds an offset to the transaction id. It takes into account the
+	 * special values 0-2 and adds an extra 3 to the offset accordingly.
+	 * 
+	 * @param id
+	 *            The transaction id.
+	 * @param increment
+	 *            The amount to increment the id.
+	 * @return The result transaction id.
+	 */
+	private long incrementTxnId(long id, int increment) {
+		int oldId;
+		int newId;
+
+		oldId = (int) id;
+		newId = oldId + increment;
+
+		if (oldId < 0 && newId >= 0) {
+			newId += SPECIAL_TRANSACTION_OFFSET;
+		}
+
+		return newId;
+	}
+
+
+	private ReplicationQueryPredicates buildQueryPredicates(ReplicationState state) {
+		long topTransactionId;
+		int rangeLength;
+		ReplicationQueryPredicates predicates;
+
+		// The top transaction id of the next query is the current xMax up to a
+		// maximum of
+		// TRANSACTION_QUERY_SIZE_MAX transactions.
+		topTransactionId = state.getTxnMax();
+		rangeLength = compareTxnIds(topTransactionId, state.getTxnMaxQueried());
+		if (rangeLength > TRANSACTION_QUERY_SIZE_MAX) {
+			topTransactionId = incrementTxnId(state.getTxnMaxQueried(), TRANSACTION_QUERY_SIZE_MAX);
+		}
+
+		// Build the predicate object with the new range.
+		predicates = new ReplicationQueryPredicates(state.getTxnMaxQueried(), topTransactionId);
+
+		// Update the state with the new queried marker.
+		state.setTxnMaxQueried(topTransactionId);
+
+		// Copy the active transaction list into the predicate.
+		predicates.getActiveList().addAll(state.getTxnActive());
+
+		// The state object will only contain ready ids for transaction ranges
+		// that have passed already so we must add all of them to the predicate
+		// so they get included in this query.
+		predicates.getReadyList().addAll(state.getTxnReady());
+
+		// The ready list can be cleared on the state object now.
+		state.getTxnReady().clear();
+
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.finer("Query predicates updated, bottomXid=" + predicates.getBottomTransactionId() + ", topXid="
+					+ predicates.getTopTransactionId() + ", activeXidList=" + predicates.getActiveList()
+					+ ", readyXidList=" + predicates.getReadyList() + ".");
+		}
+
+		return predicates;
+	}
+
+
+	private void copyChanges(ReleasableIterator<ChangeContainer> sourceIterator, ReplicationState state) {
+		try {
+			Date currentTimestamp;
+
+			// As we process, we must update the timestamp to match the latest
+			// record we have received.
+			currentTimestamp = state.getTimestamp();
+
+			while (sourceIterator.hasNext()) {
+				ChangeContainer change;
+				Date nextTimestamp;
+
+				change = sourceIterator.next();
+				nextTimestamp = change.getEntityContainer().getEntity().getTimestamp();
+
+				if (currentTimestamp.compareTo(nextTimestamp) < 0) {
+					currentTimestamp = nextTimestamp;
+				}
+
+				changeSink.process(change);
+			}
+
+			state.setTimestamp(currentTimestamp);
+
+		} finally {
+			sourceIterator.release();
+		}
+	}
+
+
+	/**
+	 * Replicates the next set of changes from the database.
+	 */
+	public void replicate() {
+		try {
+			replicateLoop();
+
+		} finally {
+			changeSink.release();
+		}
+	}
+
+
+	/**
+	 * The main replication loop. This continues until the maximum number of
+	 * replication intervals has been reached.
+	 */
+	private void replicateLoop() {
+		// Perform replication up to the number of iterations, or infinitely if
+		// set to 0.
+		for (int iterationCount = 1; true; iterationCount++) {
+
+			// Perform the replication interval.
+			txnManager.executeWithinTransaction(new Runnable() {
+				@Override
+				public void run() {
+					replicateImpl();
+				}
+			});
+			
+			// Stop if we've reached the target number of iterations.
+			if (iterations > 0 && iterationCount >= iterations) {
+				LOG.fine("Exiting replication loop.");
+				break;
+			}
+		}
+	}
+
+
+	/**
+	 * Replicates the next set of changes from the database.
+	 */
+	private void replicateImpl() {
+		ReplicationState state;
+		ReplicationQueryPredicates predicates;
+		Date systemTimestamp;
+		Map<String, Object> metaData;
+		
+		// Create an initial replication state.
+		state = new ReplicationState();
+		
+		// Initialise the sink and provide it with a reference to the state
+		// object. A single state instance is shared by both ends of the
+		// pipeline.
+		metaData = new HashMap<String, Object>(1);
+		metaData.put(ReplicationState.META_DATA_KEY, state);
+		changeSink.initialize(metaData);
+		
+		// Wait until the minimum delay interval has been reached.
+		while (true) {
+			/*
+			 * Determine the time of processing. Note that we must do this after
+			 * obtaining the database transaction snapshot. A key rule in
+			 * replication is that the timestamp we specify in our replication state
+			 * is always equal to or later than all data timestamps. This allows
+			 * consumers to know that when they pick a timestamp to start
+			 * replicating from that *all* data created after that timestamp will be
+			 * included in subsequent replications.
+			 */
+			systemTimestamp = systemTimeLoader.getSystemTime();
+			if (LOG.isLoggable(Level.FINER)) {
+				LOG.finer("Loaded system time " + systemTimestamp + " from the database.");
+			}
+			
+			// Continue onto next step if we've reached the minimum interval or
+			// if our remaining interval exceeds the maximum (clock skew).
+			long remainingInterval = state.getTimestamp().getTime() + minInterval - systemTimestamp.getTime();
+			if (remainingInterval <= 0 || remainingInterval > minInterval) {
+				break;
+			} else {
+				try {
+					Thread.sleep(remainingInterval);
+				} catch (InterruptedException e) {
+					throw new OsmosisRuntimeException("Unable to sleep until next replication iteration.", e);
+				}
+			}
+		}
+
+		// Wait until either data becomes available or the maximum interval is reached.
+		while (true) {
+			// Update our view of the current database transaction state.
+			obtainNewSnapshot(state);
+			
+			// Continue onto next step if data is available.
+			if (state.getTxnMaxQueried() != state.getTxnMax() || state.getTxnReady().size() > 0) {
+				break;
+			}
+			
+			systemTimestamp = systemTimeLoader.getSystemTime();
+			if (LOG.isLoggable(Level.FINER)) {
+				LOG.finer("Loaded system time " + systemTimestamp + " from the database.");
+			}
+			
+			// Continue onto next step if we've reached the maximum interval or
+			// if our remaining interval exceeds the maximum (clock skew).
+			long remainingInterval = state.getTimestamp().getTime() + maxInterval - systemTimestamp.getTime();
+			if (remainingInterval <= 0 || remainingInterval > maxInterval) {
+				break;
+			} else {
+				long sleepInterval = remainingInterval;
+				if (sleepInterval > minInterval) {
+					sleepInterval = minInterval;
+				}
+				try {
+					Thread.sleep(sleepInterval);
+				} catch (InterruptedException e) {
+					throw new OsmosisRuntimeException("Unable to sleep until data becomes available.", e);
+				}
+			}
+		}
+
+		LOG.fine("Processing replication sequence.");
+		
+		/*
+		 * We must get the latest timestamp before proceeding. Using an earlier
+		 * timestamp runs the risk of marking a replication sequence with a
+		 * timestamp that is too early which may lead to replication clients
+		 * starting with a later sequence than they should.
+		 */
+		systemTimestamp = systemTimeLoader.getSystemTime();
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.finer("Loaded system time " + systemTimestamp + " from the database.");
+		}
+		
+		// If this is the first interval we are setting an initial state but not
+		// performing any replication.
+		if (state.getSequenceNumber() == 0) {
+			// We are starting from the current point so we are at the current
+			// database timestamp and transaction id.
+			state.setTimestamp(systemTimestamp);
+			state.setTxnMaxQueried(state.getTxnMax());
+			
+		} else {
+			// Obtain the predicates to use during the query.
+			predicates = buildQueryPredicates(state);
+	
+			// Write the changes to the destination.
+			if (predicates.getBottomTransactionId() != predicates.getTopTransactionId()) {
+				copyChanges(source.getHistory(predicates), state);
+			}
+	
+			/*
+			 * If we have completely caught up to the database, we update the
+			 * timestamp to the database system timestamp. Otherwise we leave
+			 * the timestamp set at the value determined while processing
+			 * changes. We update to the system timestamp when caught up to
+			 * ensure that a current timestamp is provided to consumers in the
+			 * case where no data has been created.
+			 */
+			if (compareTxnIds(state.getTxnMaxQueried(), state.getTxnMax()) >= 0) {
+				state.setTimestamp(systemTimestamp);
+			}
+		}
+
+		// Commit changes.
+		changeSink.complete();
+		
+		LOG.fine("Replication sequence complete.");
+	}
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SchemaVersionValidator.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SchemaVersionValidator.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SchemaVersionValidator.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SchemaVersionValidator.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SystemTimeLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SystemTimeLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SystemTimeLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/SystemTimeLoader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagCollectionLoader.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagCollectionLoader.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagCollectionLoader.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagCollectionLoader.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TagRowMapper.java
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java
new file mode 100644
index 0000000..14f745a
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TimeDao.java
@@ -0,0 +1,39 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.Date;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * A DAO providing access to the system time on the database server. This avoids relying on the
+ * clock of this system which may be different.
+ */
+public class TimeDao implements SystemTimeLoader {
+	
+	private JdbcTemplate jdbcTemplate;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param jdbcTemplate
+	 *            Used to access the database.
+	 */
+	public TimeDao(JdbcTemplate jdbcTemplate) {
+		this.jdbcTemplate = jdbcTemplate;
+	}
+	
+	
+	/**
+	 * Gets the system time of the database server.
+	 * 
+	 * @return The timestamp.
+	 */
+	public Date getSystemTime() {
+		// The timeofday function is the only one that returns wall clock time.
+		// Others return the time of the start of the transaction.
+		return jdbcTemplate.queryForObject("SELECT clock_timestamp()", Date.class);
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java
new file mode 100644
index 0000000..22420fe
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionDao.java
@@ -0,0 +1,65 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.apidb.common.DatabaseContext2;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+
+
+/**
+ * Reads active transaction ids from the database allowing up-to-current queries to be performed
+ * when extracting changesets from the history tables.
+ */
+public class TransactionDao implements TransactionManager {
+	private static final Logger LOG = Logger.getLogger(TransactionDao.class.getName());
+	
+	private DatabaseContext2 dbCtx;
+	private JdbcTemplate jdbcTemplate;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            Used to access the database.
+	 */
+	public TransactionDao(DatabaseContext2 dbCtx) {
+		this.dbCtx = dbCtx;
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+	}
+	
+	
+	@Override
+	public TransactionSnapshot getTransactionSnapshot() {
+		String snapshotString;
+		TransactionSnapshot snapshot; 
+		
+		snapshotString = jdbcTemplate.queryForObject("SELECT txid_current_snapshot()", String.class);
+		
+		snapshot = new TransactionSnapshot(snapshotString);
+		
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.finer("Loaded new database snapshot, xmin=" + snapshot.getXMin()
+					+ ", xmax=" + snapshot.getXMax()
+					+ ", xiplist=" + snapshot.getXIpList());
+		}
+		
+		return snapshot;
+	}
+
+
+	@Override
+	public void executeWithinTransaction(final Runnable target) {
+		dbCtx.executeWithinTransaction(new TransactionCallbackWithoutResult() {
+			@Override
+			protected void doInTransactionWithoutResult(TransactionStatus arg0) {
+				target.run();
+			}
+		});
+	}
+}
diff --git a/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionManager.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionManager.java
new file mode 100644
index 0000000..9c1cea2
--- /dev/null
+++ b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionManager.java
@@ -0,0 +1,24 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+
+/**
+ * Obtains transaction snapshots used for replication.
+ */
+public interface TransactionManager {
+	/**
+	 * Obtains the current database snapshot.
+	 * 
+	 * @return The transaction snapshot.
+	 */
+	TransactionSnapshot getTransactionSnapshot();
+	
+	
+	/**
+	 * Executes the specified object within a transaction.
+	 * 
+	 * @param target
+	 *            The object containing the logic to execute.
+	 */
+	void executeWithinTransaction(Runnable target);
+}
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshot.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshot.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshot.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshot.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/UserManager.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/UserManager.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/UserManager.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/UserManager.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayDao.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayDao.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayDao.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayDao.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayNodeRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayNodeRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayNodeRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayNodeRowMapper.java
diff --git a/apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayRowMapper.java b/osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayRowMapper.java
similarity index 100%
rename from apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayRowMapper.java
rename to osmosis-apidb/src/main/java/org/openstreetmap/osmosis/apidb/v0_6/impl/WayRowMapper.java
diff --git a/apidb/src/main/resources/osmosis-plugins.conf b/osmosis-apidb/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from apidb/src/main/resources/osmosis-plugins.conf
rename to osmosis-apidb/src/main/resources/osmosis-plugins.conf
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApiDbTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApiDbTest.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApiDbTest.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApiDbTest.java
diff --git a/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java
new file mode 100644
index 0000000..a7e7be7
--- /dev/null
+++ b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/ApidbFileReplicatorTest.java
@@ -0,0 +1,126 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.apidb.v0_6.impl.DatabaseUtilities;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the file-based database replicator.
+ */
+public class ApidbFileReplicatorTest extends AbstractDataTest {
+
+    private final DatabaseUtilities dbUtils = new DatabaseUtilities(dataUtils);
+
+
+    /**
+     * A basic test loading an osm file into an API database and verifying that it gets replicated correctly.
+     * 
+     * @throws IOException if any file operations fail.
+     */
+    @Test
+    public void testLoadAndDump() throws IOException {
+        File authFile;
+        File snapshotFile;
+        File changesetFile;
+        File outputFile;
+        File workingDirectory;
+
+        // Generate input files.
+        authFile = dbUtils.getAuthorizationFile();
+        snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
+        changesetFile = dataUtils.createDataFile("v0_6/db-replicate-changeset.osc");
+        outputFile = dataUtils.newFile();
+        workingDirectory = dataUtils.newFolder();
+
+        // Remove all existing data from the database.
+        dbUtils.truncateDatabase();
+        
+        // Initialise replication.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--replicate-apidb-0.6",
+        		"authFile=" + authFile.getPath(),
+                "allowIncorrectSchemaVersion=true",
+        		"--write-replication",
+        		"workingDirectory=" + workingDirectory.getPath()
+                });
+
+        // Load the database with a dataset.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-xml-0.6",
+        		snapshotFile.getPath(),
+        		"--write-apidb-0.6",
+                "authFile=" + authFile.getPath(),
+        		"allowIncorrectSchemaVersion=true"
+                });
+        
+        // Run replication.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--replicate-apidb-0.6",
+        		"authFile=" + authFile.getPath(),
+                "allowIncorrectSchemaVersion=true",
+        		"--write-replication",
+        		"workingDirectory=" + workingDirectory.getPath()
+                });
+
+        // Apply the changeset file to the database.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-xml-change-0.6",
+        		changesetFile.getPath(),
+        		"--write-apidb-change-0.6",
+                "authFile=" + authFile.getPath(),
+        		"allowIncorrectSchemaVersion=true" });
+        
+        // Run replication.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--replicate-apidb-0.6",
+        		"authFile=" + authFile.getPath(),
+                "allowIncorrectSchemaVersion=true",
+        		"--write-replication",
+        		"workingDirectory=" + workingDirectory.getPath()
+                });
+        
+        // Ensure that replication runs successfully even if no data is available.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--replicate-apidb-0.6",
+        		"authFile=" + authFile.getPath(),
+                "allowIncorrectSchemaVersion=true",
+        		"--write-replication",
+        		"workingDirectory=" + workingDirectory.getPath()
+                });
+        
+        // Ensure that replication can run with multiple loops.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--replicate-apidb-0.6",
+        		"authFile=" + authFile.getPath(),
+                "allowIncorrectSchemaVersion=true",
+        		"iterations=2",
+        		"--write-replication",
+        		"workingDirectory=" + workingDirectory.getPath()
+                });
+        
+        // Decompress the result file.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-xml-change-0.6",
+        		new File(workingDirectory, "000/000/002.osc.gz").getPath(),
+        		"--write-xml-change-0.6",
+        		outputFile.getPath()
+                });
+
+        // Validate that the replicated file matches the input file.
+        dataUtils.compareFiles(changesetFile, outputFile);
+    }
+}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManagerTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManagerTest.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManagerTest.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ChangesetManagerTest.java
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DatabaseUtilities.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DatabaseUtilities.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DatabaseUtilities.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/DatabaseUtilities.java
diff --git a/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java
new file mode 100644
index 0000000..39aebc2
--- /dev/null
+++ b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationDestination.java
@@ -0,0 +1,104 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * A mocked replication destination allowing the existing replication state to be loaded and the
+ * current state maintained. All data processing calls such as process will be ignored.
+ */
+public class MockReplicationDestination implements ChangeSink {
+	
+	private boolean stateExists;
+	private ReplicationState currentState;
+	private Map<String, String> storedState;
+	
+	
+	/**
+	 * Creates a new instance with no state.
+	 */
+	public MockReplicationDestination() {
+		stateExists = false;
+		storedState = new HashMap<String, String>();
+	}
+	
+	
+	/**
+	 * Creates a new instance with an initial state.
+	 * 
+	 * @param initialState
+	 *            The initial replication state.
+	 */
+	public MockReplicationDestination(ReplicationState initialState) {
+		this();
+		
+		initialState.store(storedState);
+		stateExists = true;
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Get the replication state from the upstream task.
+		if (!metaData.containsKey(ReplicationState.META_DATA_KEY)) {
+			throw new OsmosisRuntimeException(
+					"No replication state has been provided in metadata key " + ReplicationState.META_DATA_KEY + ".");
+		}
+		currentState = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+		
+		// Initialise the state from the stored state if it exists and increment
+		// the sequence number.
+		if (stateExists) {
+			currentState.load(storedState);
+			currentState.setSequenceNumber(currentState.getSequenceNumber() + 1);
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		currentState.store(storedState);
+		stateExists = true;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * Returns the current state object tracked internally. This will be a state
+	 * object provided by a caller in the initialize method. It will remain
+	 * available after complete and release have been called.
+	 * 
+	 * @return The current state.
+	 */
+	public ReplicationState getCurrentState() {
+		return currentState;
+	}
+}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationSource.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationSource.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationSource.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockReplicationSource.java
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockSystemTimeLoader.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockSystemTimeLoader.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockSystemTimeLoader.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockSystemTimeLoader.java
diff --git a/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java
new file mode 100644
index 0000000..d8971e3
--- /dev/null
+++ b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/MockTransactionSnapshotLoader.java
@@ -0,0 +1,36 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A mocked transaction snapshot loader allowing canned snapshots to be returned.
+ */
+public class MockTransactionSnapshotLoader implements TransactionManager {
+
+	private List<TransactionSnapshot> snapshots = new ArrayList<TransactionSnapshot>();
+
+
+	/**
+	 * Gets the currently available snapshots.
+	 * 
+	 * @return The snapshots.
+	 */
+	public List<TransactionSnapshot> getSnapshots() {
+		return snapshots;
+	}
+
+
+	@Override
+	public TransactionSnapshot getTransactionSnapshot() {
+		return snapshots.remove(0);
+	}
+
+
+	@Override
+	public void executeWithinTransaction(Runnable target) {
+		target.run();
+	}
+}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSequenceFormatterTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSequenceFormatterTest.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSequenceFormatterTest.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicationSequenceFormatterTest.java
diff --git a/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java
new file mode 100644
index 0000000..ec128df
--- /dev/null
+++ b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/ReplicatorTest.java
@@ -0,0 +1,312 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.apidb.v0_6.impl;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Tests for the Replicator class.
+ */
+public class ReplicatorTest {
+
+	private Date buildDate(String rawDate) {
+		try {
+			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(rawDate);
+		} catch (ParseException e) {
+			throw new OsmosisRuntimeException("The date could not be parsed.", e);
+		}
+	}
+
+
+	/**
+	 * Tests replication behaviour during initialisation. Initialisation occurs the first time
+	 * replication is run.
+	 */
+	@Test
+	public void testInitialization() {
+		Replicator replicator;
+		MockReplicationSource source;
+		MockReplicationDestination destination;
+		MockTransactionSnapshotLoader snapshotLoader;
+		MockSystemTimeLoader timeLoader;
+		ReplicationState state;
+		
+		// Build the mocks.
+		source = new MockReplicationSource();
+		destination = new MockReplicationDestination();
+		snapshotLoader = new MockTransactionSnapshotLoader();
+		timeLoader = new MockSystemTimeLoader();
+		
+		// Instantiate the new replicator.
+		replicator = new Replicator(source, destination, snapshotLoader, timeLoader, 1, 0, 0);
+		
+		// Provide initialisation data.
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:14"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:14"));
+		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:200:110,112"));
+		
+		// Launch the replication process.
+		replicator.replicate();
+		
+		// Verify the final state.
+		state = destination.getCurrentState();
+		Assert.assertEquals("Incorrect final state.",
+				new ReplicationState(
+						200,
+						200,
+						Arrays.asList(new Long[]{110L, 112L}),
+						Arrays.asList(new Long[]{}),
+						buildDate("2009-10-11 12:13:14"),
+						0),
+				state);
+	}
+
+
+	/**
+	 * Tests replication behaviour when no replication is required.
+	 */
+	@Test
+	public void testNoAction() {
+		Replicator replicator;
+		MockReplicationSource source;
+		MockReplicationDestination destination;
+		MockTransactionSnapshotLoader snapshotLoader;
+		MockSystemTimeLoader timeLoader;
+		ReplicationState initialState;
+		ReplicationState finalState;
+		
+		// Build initial replication state.
+		initialState = new ReplicationState(
+				200,
+				200,
+				Arrays.asList(new Long[]{110L, 112L}),
+				Arrays.asList(new Long[]{}),
+				buildDate("2009-10-11 12:13:14"),
+				0);
+		
+		// Build the mocks.
+		source = new MockReplicationSource();
+		destination = new MockReplicationDestination(initialState);
+		snapshotLoader = new MockTransactionSnapshotLoader();
+		timeLoader = new MockSystemTimeLoader();
+		
+		// Instantiate the new replicator.
+		replicator = new Replicator(source, destination, snapshotLoader, timeLoader, 1, 0, 0);
+		
+		// We want the snapshot loader to return the same snapshot to simulate no database changes.
+		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:200:110,112"));
+		// But we want the clock time to have progressed.
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		
+		// Launch the replication process.
+		replicator.replicate();
+		
+		// Verify that the final state does not match the initial state, but that the only
+		// difference is the time and increment sequence number.
+		finalState = destination.getCurrentState();
+		Assert.assertFalse("Final state should not match initial state.", finalState.equals(initialState));
+		finalState.setTimestamp(initialState.getTimestamp());
+		finalState.setSequenceNumber(finalState.getSequenceNumber() - 1);
+		Assert.assertTrue("Final state should match initial state after updating timestamp.",
+				finalState.equals(initialState));
+		
+		// Verify that no changes were replicated.
+		Assert.assertTrue("No changes should have been replicated.", source.getPredicatesList().size() == 0);
+	}
+
+
+	/**
+	 * Tests replication behaviour when a simple replication interval is required.
+	 */
+	@Test
+	public void testSimpleIncrement() {
+		Replicator replicator;
+		MockReplicationSource source;
+		MockReplicationDestination destination;
+		MockTransactionSnapshotLoader snapshotLoader;
+		MockSystemTimeLoader timeLoader;
+		ReplicationState state;
+		ReplicationQueryPredicates predicates;
+		
+		// Build initial replication state.
+		state = new ReplicationState(
+				200,
+				200,
+				Arrays.asList(new Long[]{}),
+				Arrays.asList(new Long[]{}),
+				buildDate("2009-10-11 12:13:14"),
+				0);
+		
+		// Build the mocks.
+		source = new MockReplicationSource();
+		destination = new MockReplicationDestination(state);
+		snapshotLoader = new MockTransactionSnapshotLoader();
+		timeLoader = new MockSystemTimeLoader();
+		
+		// Instantiate the new replicator.
+		replicator = new Replicator(source, destination, snapshotLoader, timeLoader, 1, 0, 0);
+		
+		// Set the snapshot loader to return a snapshot with higher xMax.
+		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:220"));
+		// We also want the clock time to have progressed.
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		
+		// Launch the replication process.
+		replicator.replicate();
+		
+		// Verify that the final state is correct.
+		state = destination.getCurrentState();
+		Assert.assertEquals("Incorrect final state.",
+				new ReplicationState(
+						220,
+						220,
+						Arrays.asList(new Long[]{}),
+						Arrays.asList(new Long[]{}),
+						buildDate("2009-10-11 12:13:15"),
+						1),
+				state);
+		
+		// Verify that the correct changes were replicated.
+		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
+		predicates = source.getPredicatesList().get(0);
+		Assert.assertEquals("Incorrect active list.", Collections.emptyList(), predicates.getActiveList());
+		Assert.assertEquals("Incorrect ready list.", Collections.emptyList(), predicates.getReadyList());
+		Assert.assertEquals("Incorrect bottom transaction id.", 200, predicates.getBottomTransactionId());
+		Assert.assertEquals("Incorrect top transaction id.", 220, predicates.getTopTransactionId());
+	}
+
+
+	/**
+	 * Tests replication behaviour when active list manipulation is required.
+	 */
+	@Test
+	public void testInFlightTxnIncrement() {
+		Replicator replicator;
+		MockReplicationSource source;
+		MockReplicationDestination destination;
+		MockTransactionSnapshotLoader snapshotLoader;
+		MockSystemTimeLoader timeLoader;
+		ReplicationState state;
+		ReplicationQueryPredicates predicates;
+		
+		// Build initial replication state.
+		state = new ReplicationState(
+				200,
+				200,
+				Arrays.asList(new Long[]{180L, 185L}),
+				Arrays.asList(new Long[]{}),
+				buildDate("2009-10-11 12:13:14"),
+				0);
+		
+		// Build the mocks.
+		source = new MockReplicationSource();
+		destination = new MockReplicationDestination(state);
+		snapshotLoader = new MockTransactionSnapshotLoader();
+		timeLoader = new MockSystemTimeLoader();
+		
+		// Instantiate the new replicator.
+		replicator = new Replicator(source, destination, snapshotLoader, timeLoader, 1, 0, 0);
+		
+		// Set the snapshot loader to return a snapshot with higher xMax.
+		snapshotLoader.getSnapshots().add(new TransactionSnapshot("100:220:185"));
+		// We also want the clock time to have progressed.
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		
+		// Launch the replication process.
+		replicator.replicate();
+		
+		// Verify that the final state is correct.
+		state = destination.getCurrentState();
+		Assert.assertEquals("Incorrect final state.",
+				new ReplicationState(
+						220,
+						220,
+						Arrays.asList(new Long[]{185L}),
+						Arrays.asList(new Long[]{}),
+						buildDate("2009-10-11 12:13:15"),
+						1),
+				state);
+		
+		// Verify that the correct changes were replicated.
+		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
+		predicates = source.getPredicatesList().get(0);
+		Assert.assertEquals("Incorrect active list.", Arrays.asList(new Long[]{185L}), predicates.getActiveList());
+		Assert.assertEquals("Incorrect ready list.", Arrays.asList(new Long[]{180L}), predicates.getReadyList());
+		Assert.assertEquals("Incorrect bottom transaction id.", 200, predicates.getBottomTransactionId());
+		Assert.assertEquals("Incorrect top transaction id.", 220, predicates.getTopTransactionId());
+	}
+
+
+	/**
+	 * Tests replication behaviour when catching up from outage and some active transactions are overtaken.
+	 */
+	@Test
+	public void testOutageCatchupWithActiveTxns() {
+		Replicator replicator;
+		MockReplicationSource source;
+		MockReplicationDestination destination;
+		MockTransactionSnapshotLoader snapshotLoader;
+		MockSystemTimeLoader timeLoader;
+		ReplicationState state;
+		ReplicationQueryPredicates predicates;
+		
+		// Build initial replication state.
+		state = new ReplicationState(
+				5,
+				5,
+				Arrays.asList(new Long[]{24000L, 26000L}),
+				Arrays.asList(new Long[]{}),
+				buildDate("2009-10-11 12:13:14"),
+				0);
+		
+		// Build the mocks.
+		source = new MockReplicationSource();
+		destination = new MockReplicationDestination(state);
+		snapshotLoader = new MockTransactionSnapshotLoader();
+		timeLoader = new MockSystemTimeLoader();
+		
+		// Instantiate the new replicator.
+		replicator = new Replicator(source, destination, snapshotLoader, timeLoader, 1, 0, 0);
+		
+		// Set the snapshot loader to return a snapshot with higher xMax.
+		snapshotLoader.getSnapshots().add(new TransactionSnapshot("20000:30000:26000"));
+		// We also want the clock time to have progressed.
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		timeLoader.getTimes().add(buildDate("2009-10-11 12:13:15"));
+		
+		// Launch the replication process.
+		replicator.replicate();
+		
+		// Verify that the final state is correct.
+		state = destination.getCurrentState();
+		Assert.assertEquals("Incorrect final state.",
+				new ReplicationState(
+						30000,
+						25005,
+						Arrays.asList(new Long[]{26000L}),
+						Arrays.asList(new Long[]{}),
+						buildDate("2009-10-11 12:13:14"),
+						1),
+				state);
+		
+		// Verify that the correct changes were replicated.
+		Assert.assertTrue("A single interval should have been replicated.", source.getPredicatesList().size() == 1);
+		predicates = source.getPredicatesList().get(0);
+		Assert.assertEquals("Incorrect active list.", Arrays.asList(new Long[]{26000L}), predicates.getActiveList());
+		Assert.assertEquals("Incorrect ready list.", Arrays.asList(new Long[]{}), predicates.getReadyList());
+		Assert.assertEquals("Incorrect bottom transaction id.", 5, predicates.getBottomTransactionId());
+		Assert.assertEquals("Incorrect top transaction id.", 25005, predicates.getTopTransactionId());
+	}
+}
diff --git a/apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotTest.java b/osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotTest.java
similarity index 100%
rename from apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotTest.java
rename to osmosis-apidb/src/test/java/org/openstreetmap/osmosis/apidb/v0_6/impl/TransactionSnapshotTest.java
diff --git a/apidb/src/test/resources/data/template/v0_6/apidb-authfile.txt b/osmosis-apidb/src/test/resources/data/template/v0_6/apidb-authfile.txt
similarity index 100%
rename from apidb/src/test/resources/data/template/v0_6/apidb-authfile.txt
rename to osmosis-apidb/src/test/resources/data/template/v0_6/apidb-authfile.txt
diff --git a/apidb/src/test/resources/data/template/v0_6/db-changeset-b.osc b/osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset-b.osc
similarity index 100%
rename from apidb/src/test/resources/data/template/v0_6/db-changeset-b.osc
rename to osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset-b.osc
diff --git a/osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm
new file mode 100644
index 0000000..92765e7
--- /dev/null
+++ b/osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset-expected.osm
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1-revised"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <member type="node" ref="7" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/apidb/src/test/resources/data/template/v0_6/db-changeset.osc b/osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset.osc
similarity index 100%
rename from apidb/src/test/resources/data/template/v0_6/db-changeset.osc
rename to osmosis-apidb/src/test/resources/data/template/v0_6/db-changeset.osc
diff --git a/apidb/src/test/resources/data/template/v0_6/db-replicate-changeset.osc b/osmosis-apidb/src/test/resources/data/template/v0_6/db-replicate-changeset.osc
similarity index 100%
rename from apidb/src/test/resources/data/template/v0_6/db-replicate-changeset.osc
rename to osmosis-apidb/src/test/resources/data/template/v0_6/db-replicate-changeset.osc
diff --git a/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm b/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm
new file mode 100644
index 0000000..25bac50
--- /dev/null
+++ b/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot-b.osm
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot.osm b/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot.osm
new file mode 100644
index 0000000..fd0631c
--- /dev/null
+++ b/osmosis-apidb/src/test/resources/data/template/v0_6/db-snapshot.osm
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/.checkstyle b/osmosis-areafilter/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-areafilter/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-areafilter/.gitignore b/osmosis-areafilter/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-areafilter/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-areafilter/build.gradle b/osmosis-areafilter/build.gradle
new file mode 100644
index 0000000..1a7b210
--- /dev/null
+++ b/osmosis-areafilter/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    testCompile project(':osmosis-xml')
+    testCompile project(':osmosis-testutil')
+}
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/AreaFilterPluginLoader.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/AreaFilterPluginLoader.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/AreaFilterPluginLoader.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/AreaFilterPluginLoader.java
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/common/PolygonFileReader.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/common/PolygonFileReader.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/common/PolygonFileReader.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/common/PolygonFileReader.java
diff --git a/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java
new file mode 100644
index 0000000..088e3c3
--- /dev/null
+++ b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilter.java
@@ -0,0 +1,659 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.areafilter.v0_6;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.filter.common.IdTracker;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * A base class for all tasks filter entities within an area.
+ * 
+ * @author Brett Henderson
+ * @author Karl Newman
+ */
+public abstract class AreaFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private IdTracker availableNodes; // Nodes within the area.
+	private IdTracker requiredNodes; // Nodes needed to complete referencing entities.
+	private IdTracker availableWays; // Ways within the area.
+	private IdTracker requiredWays; // Ways needed to complete referencing relations.
+	private IdTracker availableRelations; // Relations within the area.
+	private IdTracker requiredRelations; // Relations needed to complete referencing relations.
+	private boolean clipIncompleteEntities;
+	private boolean completeWays;
+	private boolean completeRelations;
+	private boolean storeEntities;
+    private boolean cascadingRelations;
+	private SimpleObjectStore<WayContainer> allWays;
+	private SimpleObjectStore<NodeContainer> allNodes;
+    // this duplicates as a container for held-back relations in the cascadingRelations case:
+	private SimpleObjectStore<RelationContainer> allRelations; 
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param idTrackerType
+	 *            Defines the id tracker implementation to use.
+	 * @param clipIncompleteEntities
+	 *            If true, entities referring to non-existent entities will be
+	 *            modified to ensure referential integrity. For example, ways
+	 *            will be modified to only include nodes inside the area.
+	 * @param completeWays
+	 *            Include all nodes for ways which have at least one node inside
+	 *            the filtered area.
+	 * @param completeRelations
+	 *            Include all relations referenced by other relations which have
+	 *            members inside the filtered area.
+	 * @param cascadingRelations
+	 *            Make sure that a relation referencing a relation which is included
+	 *            will also be included.
+	 */
+	public AreaFilter(
+			IdTrackerType idTrackerType, boolean clipIncompleteEntities, boolean completeWays,
+			boolean completeRelations, boolean cascadingRelations) {
+		this.clipIncompleteEntities = clipIncompleteEntities;
+		// Allowing complete relations without complete ways is very difficult and not allowed for
+		// now.
+		this.completeWays = completeWays || completeRelations;
+		this.completeRelations = completeRelations;
+        // cascadingRelations is included for free with any of the complete options so you don't
+        // need it if those are set.
+        this.cascadingRelations = cascadingRelations && !completeRelations && !completeWays;
+		
+		availableNodes = IdTrackerFactory.createInstance(idTrackerType);
+		requiredNodes = IdTrackerFactory.createInstance(idTrackerType);
+		availableWays = IdTrackerFactory.createInstance(idTrackerType);
+		requiredWays = IdTrackerFactory.createInstance(idTrackerType);
+		availableRelations = IdTrackerFactory.createInstance(idTrackerType);
+		requiredRelations = IdTrackerFactory.createInstance(idTrackerType);
+		
+		// If either complete ways or complete relations are required, then all data must be stored
+		// during processing.
+		storeEntities = completeWays || completeRelations;
+		if (storeEntities) {
+			allNodes = new SimpleObjectStore<NodeContainer>(
+					new SingleClassObjectSerializationFactory(NodeContainer.class), "afn", true);
+			allWays = new SimpleObjectStore<WayContainer>(
+					new SingleClassObjectSerializationFactory(WayContainer.class), "afw", true);
+			allRelations =
+				new SimpleObjectStore<RelationContainer>(
+						new SingleClassObjectSerializationFactory(RelationContainer.class), "afr", true);
+		} else if (cascadingRelations) {
+            allRelations = 
+				new SimpleObjectStore<RelationContainer>(
+						new SingleClassObjectSerializationFactory(RelationContainer.class), "afr", true);
+        }
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+	/**
+	 * Indicates if the node lies within the area required.
+	 * 
+	 * @param node
+	 *            The node to be checked.
+	 * @return True if the node lies within the area.
+	 */
+	protected abstract boolean isNodeWithinArea(Node node);
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		Node node;
+		
+		node = container.getEntity();
+		
+		// Check if we're storing entities for later.
+		if (storeEntities) {
+			allNodes.add(container);
+		}
+		
+		// Only add the node if it lies within the box boundaries.
+		if (isNodeWithinArea(node)) {
+			availableNodes.set(node.getId());
+			
+			// If we're not storing entities, we pass it on immediately.
+			if (!storeEntities) {
+				emitNode(container);
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		Way way;
+		boolean inArea;
+		
+		way = container.getEntity();
+
+		// Check if we're storing entities for later.
+		if (storeEntities) {
+			allWays.add(container);
+		}
+		
+		// First look through all the nodes to see if any are within the filtered area
+		inArea = false;
+		for (WayNode nodeReference : way.getWayNodes()) {
+			if (availableNodes.get(nodeReference.getNodeId())) {
+				inArea = true;
+				break;
+			}
+		}
+		
+		// If the way has at least one node in the filtered area.
+		if (inArea) {
+			availableWays.set(way.getId());
+			
+			// If complete ways are desired, mark any unavailable nodes as required.
+			if (completeWays) {
+				for (WayNode nodeReference : way.getWayNodes()) {
+					long nodeId = nodeReference.getNodeId();
+					
+					if (!availableNodes.get(nodeId)) {
+						requiredNodes.set(nodeId);
+					}
+				}
+			}
+			
+			// If we're not storing entities, we pass it on immediately.
+			if (!storeEntities) {
+				emitWay(container);
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		Relation relation;
+		boolean inArea;
+        boolean holdBackRelation;
+		
+		relation = container.getEntity();
+		
+		// First look through all the node and way members to see if any are within the filtered area
+		inArea = false;
+        holdBackRelation = false;
+
+		for (RelationMember member : relation.getMembers()) {
+			switch (member.getMemberType()) {
+			case Node:
+				inArea = availableNodes.get(member.getMemberId());
+				break;
+			case Way:
+				inArea = availableWays.get(member.getMemberId());
+				break;
+			case Relation:
+				inArea = availableRelations.get(member.getMemberId());
+				break;
+			default:
+				break;
+			}
+			
+			if (inArea) {
+				break;
+			}
+		}
+
+        if (cascadingRelations) { //  && referencesOtherRelation && (!inArea || clipIncompleteEntities)) {
+            holdBackRelation = true;
+        }
+
+		// Check if we're storing entities for later.
+		if (storeEntities || holdBackRelation) {
+			allRelations.add(container);
+        }
+		
+		// If the relation has at least one member in the filtered area.
+		if (inArea) {
+			availableRelations.set(relation.getId());
+			
+			// If we're not storing entities, we pass it on immediately.
+			if (!storeEntities && !holdBackRelation) {
+				emitRelation(container);
+			}
+		}
+	}
+
+
+	/**
+	 * Sends a node to the sink. This will perform any necessary transformations on the node before
+	 * sending it.
+	 * 
+	 * @param nodeContainer
+	 *            Node to be sent.
+	 */
+	private void emitNode(NodeContainer nodeContainer) {
+		sink.process(nodeContainer);
+	}
+
+
+	/**
+	 * Sends a way to the sink. This will perform any necessary transformations on the way before
+	 * sending it.
+	 * 
+	 * @param wayContainer
+	 *            Way to be sent.
+	 */
+	private void emitWay(WayContainer wayContainer) {
+		if (clipIncompleteEntities) {
+			WayContainer filteredWayContainer;
+			Way filteredWay;
+			
+			filteredWayContainer = wayContainer.getWriteableInstance();
+			filteredWay = filteredWayContainer.getEntity();
+			
+			// Remove node references for nodes that are unavailable.
+			for (Iterator<WayNode> i = filteredWay.getWayNodes().iterator(); i.hasNext();) {
+				WayNode nodeReference = i.next();
+				
+				if (!availableNodes.get(nodeReference.getNodeId())) {
+					i.remove();
+				}
+			}
+			
+			// Only add ways that contain nodes.
+			if (filteredWay.getWayNodes().size() > 0) {
+				sink.process(filteredWayContainer);
+			}
+			
+		} else {
+			sink.process(wayContainer);
+		}
+	}
+
+
+	/**
+	 * Sends a relation to the sink. This will perform any necessary transformations on the way before
+	 * sending it.
+	 * 
+	 * @param relationContainer
+	 *            Relation to be sent.
+	 */
+	private void emitRelation(RelationContainer relationContainer) {
+    	if (clipIncompleteEntities) {
+    		RelationContainer filteredRelationContainer;
+    		Relation filteredRelation;
+    		
+		    filteredRelationContainer = relationContainer.getWriteableInstance();
+		    filteredRelation = filteredRelationContainer.getEntity();
+		    
+		    // Remove members for entities that are unavailable.
+		    for (Iterator<RelationMember> i = filteredRelation.getMembers().iterator(); i.hasNext();) {
+		    	RelationMember member = i.next();
+		    	EntityType memberType;
+		    	long memberId;
+		    	
+		    	memberType = member.getMemberType();
+		    	memberId = member.getMemberId();
+		    	
+		    	switch (memberType) {
+		    	case Node:
+		    		if (!availableNodes.get(memberId)) {
+						i.remove();
+					}
+		    		break;
+		    	case Way:
+		    		if (!availableWays.get(memberId)) {
+						i.remove();
+					}
+		    		break;
+		    	case Relation:
+		    		if (!availableRelations.get(memberId)) {
+						i.remove();
+					}
+		    		break;
+		    	default:
+		    			break;
+		    	}
+		    }
+			
+			// Only add relations that contain entities.
+			if (filteredRelation.getMembers().size() > 0) {
+				sink.process(filteredRelationContainer);
+			}
+			
+    	} else {
+    		sink.process(relationContainer);
+    	}
+	}
+	
+	
+	private boolean selectParentRelationsPass() {
+		ReleasableIterator<RelationContainer> i = allRelations.iterate();
+		
+		try {
+			int selectionCount;
+			
+			selectionCount = 0;
+			
+			while (i.hasNext()) {
+				Relation relation = i.next().getEntity();
+				long relationId = relation.getId();
+				
+				// Ignore relations that have already been selected.
+				if (!availableRelations.get(relationId)) {
+					
+					// This relation becomes an available relation if one of its member
+					// relations is also available.
+					for (RelationMember member : relation.getMembers()) {
+						if (member.getMemberType().equals(EntityType.Relation)) {
+							if (availableRelations.get(member.getMemberId())) {
+								availableRelations.set(relationId);
+								selectionCount++;
+							}
+						}
+					}
+				}
+			}
+			
+			return selectionCount > 0;
+			
+		} finally {
+			i.release();
+		}
+	}
+
+
+	/**
+	 * Walk up the relation tree. This means iterating through relations until all parent relations
+	 * of existing relations are marked in the available list. We may have to do this multiple times
+	 * depending on the nesting level of relations.
+	 */
+	private void selectParentRelations() {
+		boolean selectionsMade;
+		
+		do {
+			selectionsMade = selectParentRelationsPass();
+		} while (selectionsMade);
+	}
+
+
+	/**
+	 * Select all relation members of type relation for existing selected relations. This may need
+	 * to be called several times until all children are selected.
+	 * 
+	 * @return True if additional selections were made an another pass is needed.
+	 */
+	private boolean selectChildRelationsPass() {
+		ReleasableIterator<RelationContainer> i = allRelations.iterate();
+		
+		try {
+			int selectionCount;
+			
+			selectionCount = 0;
+			
+			while (i.hasNext()) {
+				Relation relation = i.next().getEntity();
+				long relationId = relation.getId();
+				
+				// Only examine available relations.
+				if (availableRelations.get(relationId)) {
+					// Select the child if it hasn't already been selected.
+					for (RelationMember member : relation.getMembers()) {
+						if (member.getMemberType().equals(EntityType.Relation)) {
+							long memberId = member.getMemberId();
+							
+							if (!availableRelations.get(memberId)) {
+								availableRelations.set(memberId);
+								selectionCount++;
+							}
+						}
+					}
+				}
+			}
+			
+			return selectionCount > 0;
+			
+		} finally {
+			i.release();
+		}
+	}
+	
+	
+	/**
+	 * Select all relation members of type node or way for existing selected relations.
+	 */
+	private void selectChildNonRelationsPass() {
+		ReleasableIterator<RelationContainer> i = allRelations.iterate();
+		
+		try {
+			while (i.hasNext()) {
+				Relation relation = i.next().getEntity();
+				long relationId = relation.getId();
+				
+				// Only examine available relations.
+				if (availableRelations.get(relationId)) {
+					// Select the member if it hasn't already been selected.
+					for (RelationMember member : relation.getMembers()) {
+						switch (member.getMemberType()) {
+						case Node:
+							availableNodes.set(member.getMemberId());
+							break;
+						case Way:
+							availableWays.set(member.getMemberId());
+							break;
+						default:
+							break;
+						}
+					}
+				}
+			}
+			
+		} finally {
+			i.release();
+		}
+	}
+	
+	
+	/**
+	 * Select all nodes within already selected ways.
+	 */
+	private void selectWayNodes() {
+		ReleasableIterator<WayContainer> i = allWays.iterate();
+		
+		try {
+			while (i.hasNext()) {
+				Way way = i.next().getEntity();
+				long wayId = way.getId();
+				
+				// Only examine available relations.
+				if (availableWays.get(wayId)) {
+					// Select all nodes within the way.
+					for (WayNode wayNode : way.getWayNodes()) {
+						availableNodes.set(wayNode.getNodeId());
+					}
+				}
+			}
+			
+		} finally {
+			i.release();
+		}
+	}
+	
+	
+	private void buildCompleteRelations() {
+		boolean selectionsMade;
+		
+		// Select all child relation members of type relation. 
+		do {
+			selectionsMade = selectChildRelationsPass();
+		} while (selectionsMade);
+		
+		// Select all child relation members of type way or node.
+		selectChildNonRelationsPass();
+		
+		// Select all way nodes of existing nodes.
+		selectWayNodes();
+	}
+    
+    
+    private void pumpNodesToSink() {
+    	ReleasableIterator<NodeContainer> i = allNodes.iterate();
+    	
+    	try {
+    		while (i.hasNext()) {
+				NodeContainer nodeContainer = i.next();
+				if (availableNodes.get(nodeContainer.getEntity().getId())) {
+					emitNode(nodeContainer);
+				}
+			}
+    		
+    	} finally {
+    		i.release();
+    	}
+    }
+    
+    
+    private void pumpWaysToSink() {
+    	ReleasableIterator<WayContainer> i = allWays.iterate();
+    	
+    	try {
+    		while (i.hasNext()) {
+				WayContainer wayContainer = i.next();
+				if (availableWays.get(wayContainer.getEntity().getId())) {
+					emitWay(wayContainer);
+				}
+			}
+    		
+    	} finally {
+    		i.release();
+    	}
+    }
+    
+    
+    private void pumpRelationsToSink() {
+    	ReleasableIterator<RelationContainer> i = allRelations.iterate();
+    	
+    	try {
+    		while (i.hasNext()) {
+				RelationContainer relationContainer = i.next();
+				if (availableRelations.get(relationContainer.getEntity().getId())) {
+					emitRelation(relationContainer);
+				}
+			}
+    		
+    	} finally {
+    		i.release();
+    	}
+    }
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		// If we've stored entities temporarily, we now need to forward the selected ones to the output.
+		if (storeEntities) {
+			// Select all parents of current relations.
+			selectParentRelations();
+			
+			// Merge required ids into available ids.
+			availableNodes.setAll(requiredNodes);
+			availableWays.setAll(requiredWays);
+			availableRelations.setAll(requiredRelations);
+			requiredNodes = null;
+			requiredWays = null;
+			requiredRelations = null;
+			
+			if (completeRelations) {
+				buildCompleteRelations();
+			}
+			
+			// Send the selected entities to the output.
+			pumpNodesToSink();
+			pumpWaysToSink();
+			pumpRelationsToSink();
+		} else if (cascadingRelations) {
+			// Select all parents of current relations.
+			selectParentRelations();
+			availableRelations.setAll(requiredRelations);
+			
+			// nodes, ways, and relations *not* referencing other relations will already have
+            // been written in this mode. we only pump the remaining ones, relations that 
+            // reference other relations. this may result in an un-ordered relation stream.
+			pumpRelationsToSink();
+        }
+		
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		if (allNodes != null) {
+			allNodes.release();
+		}
+		if (allWays != null) {
+			allWays.release();			
+		}
+		if (allRelations != null) {
+			allRelations.release();
+		}
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java
new file mode 100644
index 0000000..b5dcafe
--- /dev/null
+++ b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTaskManagerFactory.java
@@ -0,0 +1,31 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.areafilter.v0_6;
+
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+
+
+/**
+ * Extends the basic task manager factory functionality with area filter task
+ * specific common methods.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class AreaFilterTaskManagerFactory extends TaskManagerFactory {
+	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.Dynamic;
+
+
+	/**
+	 * Utility method for retrieving the login credentials for a database
+	 * connection.
+	 * 
+	 * @param taskConfig
+	 *            Contains all information required to instantiate and configure
+	 *            the task.
+	 * @return The entity identifier tracker type.
+	 */
+	protected IdTrackerType getIdTrackerType(TaskConfiguration taskConfig) {
+		return DEFAULT_ID_TRACKER_TYPE;
+	}
+}
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilter.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilter.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilter.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilter.java
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterFactory.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterFactory.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterFactory.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterFactory.java
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilter.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilter.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilter.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilter.java
diff --git a/areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterFactory.java b/osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterFactory.java
similarity index 100%
rename from areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterFactory.java
rename to osmosis-areafilter/src/main/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterFactory.java
diff --git a/areafilter/src/main/resources/osmosis-plugins.conf b/osmosis-areafilter/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from areafilter/src/main/resources/osmosis-plugins.conf
rename to osmosis-areafilter/src/main/resources/osmosis-plugins.conf
diff --git a/areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTest.java b/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTest.java
similarity index 100%
rename from areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTest.java
rename to osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/AreaFilterTest.java
diff --git a/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java b/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java
new file mode 100644
index 0000000..026d299
--- /dev/null
+++ b/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/BoundingBoxFilterTest.java
@@ -0,0 +1,181 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.areafilter.v0_6;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+
+/**
+ * @author Karl Newman
+ * 
+ */
+public class BoundingBoxFilterTest {
+
+	private SinkEntityInspector entityInspector;
+	private AreaFilter simpleAreaFilter;
+	private Bound intersectingBound;
+	private Bound nonIntersectingBound;
+	private Node inAreaNode;
+	private Node outOfAreaNode;
+	private Node edgeNodeEast;
+	private Node edgeNodeWest;
+	private Node edgeNodeNorth;
+	private Node edgeNodeSouth;
+
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		OsmUser user;
+		List<Tag> tags;
+		
+		user = new OsmUser(12, "OsmosisTest");
+		
+		// All nodes have an empty tags list.
+		tags = new ArrayList<Tag>();
+		
+		entityInspector = new SinkEntityInspector();
+		// simpleAreaFilter doesn't cross antimeridian; no complete ways or relations
+		simpleAreaFilter = new BoundingBoxFilter(
+		        IdTrackerType.Dynamic,
+		        -20,
+		        20,
+		        20,
+		        -20,
+		        false,
+		        false,
+		        false,
+		        false);
+		simpleAreaFilter.setSink(entityInspector);
+		intersectingBound = new Bound(30, 10, 30, 10, "intersecting");
+		nonIntersectingBound = new Bound(-30, -50, 10, -10, "nonintersecting");
+		inAreaNode = new Node(new CommonEntityData(1234, 0, new Date(), user, 0, tags), 10, 10);
+		outOfAreaNode = new Node(new CommonEntityData(1235, 0, new Date(), user, 0, tags), 30, 30);
+		edgeNodeEast = new Node(new CommonEntityData(1236, 0, new Date(), user, 0, tags), 10, 20);
+		edgeNodeWest = new Node(new CommonEntityData(1237, 0, new Date(), user, 0, tags), 10, -20);
+		edgeNodeNorth = new Node(new CommonEntityData(1238, 0, new Date(), user, 0, tags), 20, 10);
+		edgeNodeSouth = new Node(new CommonEntityData(1239, 0, new Date(), user, 0, tags), -20, 10);
+	}
+
+
+	/**
+	 * Performs post-test activities.
+	 */
+	@After
+	public void tearDown() {
+		simpleAreaFilter.release();
+	}
+
+
+	/**
+	 * Test passing a Bound which intersects the filter area.
+	 */
+	@Test
+	public final void testProcessBoundContainer1() {
+		Bound compareBound;
+		simpleAreaFilter.process(new BoundContainer(intersectingBound));
+		simpleAreaFilter.complete();
+		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getLeft(), 10) == 0);
+		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getBottom(), 10) == 0);
+		assertTrue(compareBound.getOrigin().equals("intersecting"));
+	}
+
+
+	/**
+	 * Test the non-passing of a Bound which does not intersect the filter area.
+	 */
+	@Test
+	public final void testProcessBoundContainer2() {
+		simpleAreaFilter.process(new BoundContainer(nonIntersectingBound));
+		simpleAreaFilter.complete();
+		assertNull(entityInspector.getLastEntityContainer());
+	}
+
+
+	/**
+	 * Test a node inside the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea1() {
+		assertTrue(
+		        "Node lying inside filter area not considered inside area",
+		        simpleAreaFilter.isNodeWithinArea(inAreaNode));
+	}
+
+
+	/**
+	 * Test a node outside the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea2() {
+		assertFalse(
+		        "Node lying outside filter area not considered outside area",
+		        simpleAreaFilter.isNodeWithinArea(outOfAreaNode));
+	}
+
+
+	/**
+	 * Test a node on the East edge of the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea3() {
+		assertTrue(
+		        "Node lying on East edge of filter area not considered inside area",
+		        simpleAreaFilter.isNodeWithinArea(edgeNodeEast));
+	}
+
+
+	/**
+	 * Test a node on the West edge of the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea4() {
+		assertTrue(
+		        "Node lying on West edge of filter area not considered inside area",
+		        simpleAreaFilter.isNodeWithinArea(edgeNodeWest));
+	}
+
+
+	/**
+	 * Test a node on the North edge of the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea5() {
+		assertTrue(
+		        "Node lying on North edge of filter area not considered inside area",
+		        simpleAreaFilter.isNodeWithinArea(edgeNodeNorth));
+	}
+
+
+	/**
+	 * Test a node on the South edge of the area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea6() {
+		assertTrue(
+		        "Node lying on South edge of filter area not considered inside area",
+		        simpleAreaFilter.isNodeWithinArea(edgeNodeSouth));
+	}
+}
diff --git a/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java b/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java
new file mode 100644
index 0000000..e411cf2
--- /dev/null
+++ b/osmosis-areafilter/src/test/java/org/openstreetmap/osmosis/areafilter/v0_6/PolygonFilterTest.java
@@ -0,0 +1,156 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.areafilter.v0_6;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+
+/**
+ * Tests the polygon area filter implementation.
+ * 
+ * @author Karl Newman
+ */
+public class PolygonFilterTest {
+
+	private File polygonFile;
+	private SinkEntityInspector entityInspector;
+	private AreaFilter polyAreaFilter;
+	private Bound intersectingBound;
+	private Bound crossingIntersectingBound;
+	private Bound nonIntersectingBound;
+	private Node inAreaNode;
+	private Node outOfAreaNode;
+	private Node edgeNode;
+
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		OsmUser user;
+		List<Tag> tags;
+		
+		user = new OsmUser(12, "OsmosisTest");
+		
+		// All nodes have an empty tags list.
+		tags = new ArrayList<Tag>();
+		
+		polygonFile = new File(getClass().getResource("testPolygon.txt").getFile());
+		entityInspector = new SinkEntityInspector();
+		// polyAreaFilter has a notch out of the Northeast corner.
+		polyAreaFilter = new PolygonFilter(IdTrackerType.Dynamic, polygonFile, false, false, false, false);
+		polyAreaFilter.setSink(entityInspector);
+		intersectingBound = new Bound(30, 0, 30, 0, "intersecting");
+		crossingIntersectingBound = new Bound(-10, 10, 30, -30, "crossing intersecting");
+		nonIntersectingBound = new Bound(30, 15, 30, 15, "nonintersecting");
+		inAreaNode = new Node(new CommonEntityData(1234, 0, new Date(), user, 0, tags), 5, 10);
+		outOfAreaNode = new Node(new CommonEntityData(1235, 0, new Date(), user, 0, tags), 15, 15);
+		edgeNode = new Node(new CommonEntityData(1236, 0, new Date(), user, 0, tags), 15, 10);
+	}
+
+
+	/**
+	 * Performs post-test activities.
+	 */
+	@After
+	public void tearDown() {
+		polyAreaFilter.release();
+	}
+
+
+	/**
+	 * Test passing a Bound which intersects the filter area.
+	 */
+	@Test
+	public final void testProcessBoundContainer1() {
+		Bound compareBound;
+		polyAreaFilter.process(new BoundContainer(intersectingBound));
+		polyAreaFilter.complete();
+		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getLeft(), 0) == 0);
+		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getBottom(), 0) == 0);
+		assertTrue(compareBound.getOrigin().equals("intersecting"));
+	}
+
+
+	/**
+	 * Test passing a Bound which crosses the antimeredian and intersects the filter area.
+	 */
+	@Test
+	public final void testProcessBoundContainer2() {
+		Bound compareBound;
+		polyAreaFilter.process(new BoundContainer(crossingIntersectingBound));
+		polyAreaFilter.complete();
+		compareBound = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertTrue(Double.compare(compareBound.getRight(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getLeft(), -20) == 0);
+		assertTrue(Double.compare(compareBound.getTop(), 20) == 0);
+		assertTrue(Double.compare(compareBound.getBottom(), -20) == 0);
+		assertTrue(compareBound.getOrigin().equals("crossing intersecting"));
+	}
+
+
+	/**
+	 * Test the non-passing of a Bound which does not intersect the filter area.
+	 */
+	@Test
+	public final void testProcessBoundContainer3() {
+		polyAreaFilter.process(new BoundContainer(nonIntersectingBound));
+		polyAreaFilter.complete();
+		assertNull(entityInspector.getLastEntityContainer());
+	}
+
+
+	/**
+	 * Test a Node that falls inside the filter area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea1() {
+		assertTrue(
+		        "Node lying inside filter area not considered inside area.",
+		        polyAreaFilter.isNodeWithinArea(inAreaNode));
+	}
+
+
+	/**
+	 * Test a Node that falls outside the filter area (inside the notched-out area of the polygon).
+	 */
+	@Test
+	public final void testIsNodeWithinArea2() {
+		assertFalse(
+		        "Node lying outside filter area not considered outside area.",
+		        polyAreaFilter.isNodeWithinArea(outOfAreaNode));
+	}
+
+
+	/**
+	 * Test a Node that falls on the edge of the filter area.
+	 */
+	@Test
+	public final void testIsNodeWithinArea3() {
+		assertFalse(
+		        "Node lying on edge of filter area not considered inside area.",
+		        polyAreaFilter.isNodeWithinArea(edgeNode));
+	}
+}
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm
new file mode 100644
index 0000000..b8734f5
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-in.osm
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode1"/>
+  </node>
+  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="name" v="outAreaWay1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="5" role="relationrole"/>
+    <tag k="name" v="inAreaRelation1"/>
+    <tag k="comment" v="this relation is pulled in by relation 5"/>
+  </relation>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="2" role="wayrole"/>
+    <tag k="name" v="outAreaRelation4"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm
new file mode 100644
index 0000000..cf1d33d
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-cascadingrelations.osm
@@ -0,0 +1,34 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-10.00000" minlat="-10.00000" maxlon="10.00000" maxlat="10.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="5" role="relationrole"/>
+    <tag k="comment" v="this relation is pulled in by relation 5"/>
+    <tag k="name" v="inAreaRelation1"/>
+  </relation>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm
new file mode 100644
index 0000000..ef569df
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-clipincompleteentities.osm
@@ -0,0 +1,27 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-10.00000" minlat="-10.00000" maxlon="10.00000" maxlat="10.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm
new file mode 100644
index 0000000..302cc2e
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completerelations.osm
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-10.00000" minlat="-10.00000" maxlon="10.00000" maxlat="10.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode1"/>
+  </node>
+  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="name" v="outAreaWay1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="5" role="relationrole"/>
+    <tag k="comment" v="this relation is pulled in by relation 5"/>
+    <tag k="name" v="inAreaRelation1"/>
+  </relation>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="2" role="wayrole"/>
+    <tag k="name" v="outAreaRelation4"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm
new file mode 100644
index 0000000..3b278d0
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-completeways.osm
@@ -0,0 +1,37 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-10.00000" minlat="-10.00000" maxlon="10.00000" maxlat="10.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode1"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="5" role="relationrole"/>
+    <tag k="comment" v="this relation is pulled in by relation 5"/>
+    <tag k="name" v="inAreaRelation1"/>
+  </relation>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm
new file mode 100644
index 0000000..247ef31
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-standard.osm
@@ -0,0 +1,29 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-10.00000" minlat="-10.00000" maxlon="10.00000" maxlat="10.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm
new file mode 100644
index 0000000..1a3b267
--- /dev/null
+++ b/osmosis-areafilter/src/test/resources/data/template/v0_6/areafilter-out-whole.osm
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode1"/>
+  </node>
+  <node id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="1">
+    <tag k="name" v="inAreaNode2"/>
+  </node>
+  <node id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode1"/>
+  </node>
+  <node id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-11" lon="11">
+    <tag k="name" v="outAreaNode2"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="name" v="inAreaWay1"/>
+  </way>
+  <way id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="name" v="outAreaWay1"/>
+  </way>
+  <relation id="2" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="1" role="noderole"/>
+    <tag k="name" v="inAreaRelation2"/>
+  </relation>
+  <relation id="3" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="name" v="inAreaRelation3"/>
+  </relation>
+  <relation id="4" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="way" ref="2" role="wayrole"/>
+    <tag k="name" v="outAreaRelation4"/>
+  </relation>
+  <relation id="5" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="relation" ref="2" role="relationrole"/>
+    <member type="relation" ref="4" role="relationrole"/>
+    <tag k="name" v="inAreaRelation5"/>
+  </relation>
+</osm>
diff --git a/areafilter/src/test/resources/org/openstreetmap/osmosis/areafilter/v0_6/testPolygon.txt b/osmosis-areafilter/src/test/resources/org/openstreetmap/osmosis/areafilter/v0_6/testPolygon.txt
similarity index 100%
rename from areafilter/src/test/resources/org/openstreetmap/osmosis/areafilter/v0_6/testPolygon.txt
rename to osmosis-areafilter/src/test/resources/org/openstreetmap/osmosis/areafilter/v0_6/testPolygon.txt
diff --git a/osmosis-core/.checkstyle b/osmosis-core/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-core/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-core/.gitignore b/osmosis-core/.gitignore
new file mode 100644
index 0000000..15d6f89
--- /dev/null
+++ b/osmosis-core/.gitignore
@@ -0,0 +1,8 @@
+.classpath
+.project
+.settings
+/bin
+/build
+/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java
+/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml
+
diff --git a/osmosis-core/build.gradle b/osmosis-core/build.gradle
new file mode 100644
index 0000000..72e5b4e
--- /dev/null
+++ b/osmosis-core/build.gradle
@@ -0,0 +1,74 @@
+configurations {
+    // Exclude because all necessary APIs are included in the JDK.
+    all*.exclude group: 'xml-apis', module: 'xml-apis'
+    // Exclude because Stax is included in the JDK from java 1.6 onwards.
+    all*.exclude group: 'javax.xml.stream', module: 'stax-api'
+}
+
+dependencies {
+    compile group: 'net.sf.jpf', name: 'jpf', version: dependencyVersionJpf
+    compile group: 'org.codehaus.woodstox', name: 'woodstox-core-lgpl', version: dependencyVersionWoodstoxCore
+    compile group: 'org.codehaus.woodstox', name: 'stax2-api', version: dependencyVersionWoodstoxStax2
+    compile group: 'org.apache.commons', name: 'commons-compress', version: dependencyVersionCommonsCompress
+    compile group: 'xerces', name: 'xercesImpl', version: dependencyVersionXerces
+}
+
+/*
+ * Define a custom task to automatically generate the OsmosisConstants file
+ * and update the java compilation task to depend on it.
+ */
+task generateJavaSources {
+	description = 'Generates the OsmosisConstants java file with the current version number.'
+
+	// Build file objects for our template file, and output java file.
+    def commonPathPrefix = "$projectDir/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java"
+    def outputFile = new File(commonPathPrefix)
+    def inputFile = new File(commonPathPrefix + ".template")
+
+    /* 
+     * Declare inputs and outputs of the task to allow gradle to determine if
+     * it is up to date.
+     */
+    inputs.file inputFile
+    inputs.property('version', version)
+    outputs.file outputFile
+
+    doLast {
+        // Insert the version string into the constants file.
+        def fileContent = inputFile.getText()
+        fileContent = fileContent.replace("no-version-specified", version)
+        outputFile.write(fileContent)
+    }
+}
+// Define task dependency to ensure constants file is always up to date.
+compileJava.dependsOn generateJavaSources
+
+
+/*
+ * Define a custom task to automatically generate the plugin.xml file
+ * and update the copy resources task to depend on it.
+ */
+task generateResources {
+	description = 'Generates the plugin.xml file with the current version number.'
+
+	// Build file objects for our template file, and output java file.
+    def commonPathPrefix = "$projectDir/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml"
+    def outputFile = new File(commonPathPrefix)
+    def inputFile = new File(commonPathPrefix + ".template")
+
+    /* 
+     * Declare inputs and outputs of the task to allow gradle to determine if
+     * it is up to date.
+     */
+    inputs.file inputFile
+    outputs.file outputFile
+
+    doLast {
+        // Insert the version string into the constants file.
+        def fileContent = inputFile.getText()
+        fileContent = fileContent.replace("no-version-specified", version)
+        outputFile.write(fileContent)
+    }
+}
+// Define task dependency to ensure constants file is always up to date.
+processResources.dependsOn generateResources
diff --git a/core/doc/OsmApi06.xsd b/osmosis-core/doc/OsmApi06.xsd
similarity index 100%
rename from core/doc/OsmApi06.xsd
rename to osmosis-core/doc/OsmApi06.xsd
diff --git a/core/doc/contributors.txt b/osmosis-core/doc/contributors.txt
similarity index 100%
rename from core/doc/contributors.txt
rename to osmosis-core/doc/contributors.txt
diff --git a/core/src/assembly/distribution.xml b/osmosis-core/src/assembly/distribution.xml
similarity index 100%
rename from core/src/assembly/distribution.xml
rename to osmosis-core/src/assembly/distribution.xml
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java
new file mode 100644
index 0000000..22ed197
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/CorePluginLoader.java
@@ -0,0 +1,116 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.bound.v0_6.BoundComputerFactory;
+import org.openstreetmap.osmosis.core.bound.v0_6.BoundSetterFactory;
+import org.openstreetmap.osmosis.core.buffer.v0_6.ChangeBufferFactory;
+import org.openstreetmap.osmosis.core.buffer.v0_6.EntityBufferFactory;
+import org.openstreetmap.osmosis.core.misc.v0_6.EmptyChangeReaderFactory;
+import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReaderFactory;
+import org.openstreetmap.osmosis.core.misc.v0_6.NullChangeWriterFactory;
+import org.openstreetmap.osmosis.core.misc.v0_6.NullWriterFactory;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.core.progress.v0_6.ChangeProgressLoggerFactory;
+import org.openstreetmap.osmosis.core.progress.v0_6.EntityProgressLoggerFactory;
+import org.openstreetmap.osmosis.core.report.v0_6.EntityReporterFactory;
+import org.openstreetmap.osmosis.core.report.v0_6.IntegrityReporterFactory;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForSeekableApplierComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorterFactory;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeTagSorterFactory;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntitySorterFactory;
+import org.openstreetmap.osmosis.core.sort.v0_6.TagSorterFactory;
+import org.openstreetmap.osmosis.core.tee.v0_6.ChangeTeeFactory;
+import org.openstreetmap.osmosis.core.tee.v0_6.EntityTeeFactory;
+
+
+/**
+ * The plugin loader for the core tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class CorePluginLoader implements PluginLoader {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		Map<String, TaskManagerFactory> factoryMap;
+		EntitySorterFactory entitySorterFactory06;
+		ChangeSorterFactory changeSorterFactory06;
+
+		factoryMap = new HashMap<String, TaskManagerFactory>();
+
+		// Configure factories that require additional information.
+		entitySorterFactory06 = new EntitySorterFactory();
+		entitySorterFactory06.registerComparator("TypeThenId", new EntityContainerComparator(
+				new EntityByTypeThenIdComparator()), true);
+		changeSorterFactory06 = new ChangeSorterFactory();
+		changeSorterFactory06.registerComparator("streamable", new ChangeForStreamableApplierComparator(), true);
+		changeSorterFactory06.registerComparator("seekable", new ChangeForSeekableApplierComparator(), false);
+
+		// Register factories.
+		factoryMap.put("sort", entitySorterFactory06);
+		factoryMap.put("s", entitySorterFactory06);
+		factoryMap.put("sort-change", changeSorterFactory06);
+		factoryMap.put("sc", changeSorterFactory06);
+		factoryMap.put("write-null", new NullWriterFactory());
+		factoryMap.put("wn", new NullWriterFactory());
+		factoryMap.put("write-null-change", new NullChangeWriterFactory());
+		factoryMap.put("wnc", new NullChangeWriterFactory());
+		factoryMap.put("buffer", new EntityBufferFactory());
+		factoryMap.put("b", new EntityBufferFactory());
+		factoryMap.put("buffer-change", new ChangeBufferFactory());
+		factoryMap.put("bc", new ChangeBufferFactory());
+		factoryMap.put("report-entity", new EntityReporterFactory());
+		factoryMap.put("re", new EntityReporterFactory());
+		factoryMap.put("report-integrity", new IntegrityReporterFactory());
+		factoryMap.put("ri", new IntegrityReporterFactory());
+		factoryMap.put("log-progress", new EntityProgressLoggerFactory());
+		factoryMap.put("lp", new EntityProgressLoggerFactory());
+		factoryMap.put("log-progress-change", new ChangeProgressLoggerFactory());
+		factoryMap.put("lpc", new ChangeProgressLoggerFactory());
+		factoryMap.put("tee", new EntityTeeFactory());
+		factoryMap.put("t", new EntityTeeFactory());
+		factoryMap.put("tee-change", new ChangeTeeFactory());
+		factoryMap.put("tc", new ChangeTeeFactory());
+		factoryMap.put("read-empty", new EmptyReaderFactory());
+		factoryMap.put("rem", new EmptyReaderFactory());
+		factoryMap.put("read-empty-change", new EmptyChangeReaderFactory());
+		factoryMap.put("remc", new EmptyChangeReaderFactory());
+
+		factoryMap.put("compute-bounding-box", new BoundComputerFactory());
+		factoryMap.put("cbb", new BoundComputerFactory());
+		factoryMap.put("set-bounding-box", new BoundSetterFactory());
+		factoryMap.put("sbb", new BoundSetterFactory());
+
+		factoryMap.put("sort-0.6", entitySorterFactory06);
+		factoryMap.put("sort-change-0.6", changeSorterFactory06);
+		factoryMap.put("write-null-0.6", new NullWriterFactory());
+		factoryMap.put("write-null-change-0.6", new NullChangeWriterFactory());
+		factoryMap.put("buffer-0.6", new EntityBufferFactory());
+		factoryMap.put("buffer-change-0.6", new ChangeBufferFactory());
+		factoryMap.put("report-entity-0.6", new EntityReporterFactory());
+		factoryMap.put("report-integrity-0.6", new IntegrityReporterFactory());
+		factoryMap.put("log-progress-0.6", new EntityProgressLoggerFactory());
+		factoryMap.put("log-progress-change-0.6", new ChangeProgressLoggerFactory());
+		factoryMap.put("tee-0.6", new EntityTeeFactory());
+		factoryMap.put("tee-change-0.6", new ChangeTeeFactory());
+		factoryMap.put("read-empty-0.6", new EmptyReaderFactory());
+		factoryMap.put("read-empty-change-0.6", new EmptyChangeReaderFactory());
+		factoryMap.put("tag-sort-0.6", new TagSorterFactory());
+		factoryMap.put("tag-sort-change-0.6", new ChangeTagSorterFactory());
+
+		factoryMap.put("compute-bounding-box-0.6", new BoundComputerFactory());
+		factoryMap.put("set-bounding-box-0.6", new BoundSetterFactory());
+		
+		return factoryMap;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/LogLevels.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/LogLevels.java
new file mode 100644
index 0000000..7931e87
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/LogLevels.java
@@ -0,0 +1,59 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core;
+
+import java.util.logging.Level;
+
+
+/**
+ * Utility class for mapping integer numbers to log levels.
+ * 
+ * @author Brett Henderson
+ */
+public final class LogLevels {
+
+	/**
+	 * This class cannot be instantiated.
+	 */
+	private LogLevels() {
+		// Not callable.
+	}
+
+	/**
+	 * This is the default offset into the LOG_LEVELS array.
+	 */
+	public static final int DEFAULT_LEVEL_INDEX = 3;
+
+	/**
+	 * Defines the log levels supported from the command line.
+	 */
+	public static final Level [] LOG_LEVELS = {
+		Level.OFF,
+		Level.SEVERE,
+		Level.WARNING,
+		Level.INFO,
+		Level.FINE,
+		Level.FINER,
+		Level.FINEST
+	};
+
+
+	/**
+	 * Map a log level index to a log level. This will clip log levels to
+	 * allowable levels if the value is outside the maximum bounds.
+	 * 
+	 * @param logLevelIndex
+	 *            The requested log level index.
+	 * @return The log level associated with the index.
+	 */
+	public static Level getLogLevel(int logLevelIndex) {
+		// Ensure that the log level is within allowable bounds.
+		if (logLevelIndex < 0) {
+			logLevelIndex = 0;
+		}
+		if (logLevelIndex >= LOG_LEVELS.length) {
+			logLevelIndex = LOG_LEVELS.length - 1;
+		}
+
+		return LOG_LEVELS[logLevelIndex];
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java
new file mode 100644
index 0000000..f89b1bc
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/Osmosis.java
@@ -0,0 +1,143 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core; 
+
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.cli.CommandLineParser;
+import org.openstreetmap.osmosis.core.pipeline.common.Pipeline;
+
+
+/**
+ * The main entry point for the command line application.
+ * 
+ * @author Brett Henderson
+ */
+public final class Osmosis {
+	private static final Logger LOG = Logger.getLogger(Osmosis.class.getName());
+	
+	
+	/**
+	 * This class cannot be instantiated.
+	 */
+	private Osmosis() {
+	}
+	
+	
+	/**
+	 * The entry point to the application.
+	 * 
+	 * @param args
+	 *            The command line arguments.
+	 */
+	public static void main(String[] args) {
+		try {
+			run(args);
+			
+			System.exit(0);
+			
+		} catch (Throwable t) {
+			LOG.log(Level.SEVERE, "Execution aborted.", t);
+		}
+		
+		System.exit(1);
+	}
+	
+	
+	/**
+	 * This contains the real functionality of the main method. It is kept
+	 * separate to allow the application to be invoked within other applications
+	 * without a System.exit being called.
+	 * <p>
+	 * Typically an application shouldn't directly invoke this method, it should
+	 * instantiate its own pipeline.
+	 * 
+	 * @param args
+	 *            The command line arguments.
+	 */
+	public static void run(String[] args) {
+		CommandLineParser commandLineParser;
+		TaskRegistrar taskRegistrar;
+		Pipeline pipeline;
+		long startTime;
+		long finishTime;
+		
+		startTime = System.currentTimeMillis();
+		
+		configureLoggingConsole();
+		
+		commandLineParser = new CommandLineParser();
+		
+		// Parse the command line arguments into a consumable form.
+		commandLineParser.parse(args);
+		
+		// Configure the new logging level.
+		configureLoggingLevel(commandLineParser.getLogLevelIndex());
+		
+		LOG.info("Osmosis Version " + OsmosisConstants.VERSION);
+		taskRegistrar = new TaskRegistrar();
+		taskRegistrar.initialize(commandLineParser.getPlugins());
+		
+		pipeline = new Pipeline(taskRegistrar.getFactoryRegister());
+		
+		LOG.info("Preparing pipeline.");
+		pipeline.prepare(commandLineParser.getTaskInfoList());
+		
+		LOG.info("Launching pipeline execution.");
+		pipeline.execute();
+		
+		LOG.info("Pipeline executing, waiting for completion.");
+		pipeline.waitForCompletion();
+		
+		LOG.info("Pipeline complete.");
+		
+		finishTime = System.currentTimeMillis();
+		
+		LOG.info("Total execution time: " + (finishTime - startTime) + " milliseconds.");
+	}
+	
+	
+	/**
+	 * Configures logging to write all output to the console.
+	 */
+	private static void configureLoggingConsole() {
+		Logger rootLogger;
+		Handler consoleHandler;
+		
+		rootLogger = Logger.getLogger("");
+		
+		// Remove any existing handlers.
+		for (Handler handler : rootLogger.getHandlers()) {
+			rootLogger.removeHandler(handler);
+		}
+		
+		// Add a new console handler.
+		consoleHandler = new ConsoleHandler();
+		consoleHandler.setLevel(Level.ALL);
+		rootLogger.addHandler(consoleHandler);
+	}
+	
+	
+	/**
+	 * Configures the logging level.
+	 * 
+	 * @param level
+	 *            The index of the logging level to apply.
+	 */
+	private static void configureLoggingLevel(int logLevelIndex) {
+		Logger rootLogger;
+		
+		rootLogger = Logger.getLogger("");
+		
+		// Set the required logging level.
+		rootLogger.setLevel(LogLevels.getLogLevel(logLevelIndex));
+		
+		// Set spring logging to one level lower.
+		Logger.getLogger("org.springframework").setLevel(LogLevels.getLogLevel(logLevelIndex - 1));
+		
+		// Minimise the JPF logging.
+		Logger.getLogger("org.java.plugin").setLevel(Level.WARNING);
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java.template b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java.template
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java.template
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisConstants.java.template
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/OsmosisException.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisException.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/OsmosisException.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisException.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/OsmosisRuntimeException.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisRuntimeException.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/OsmosisRuntimeException.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/OsmosisRuntimeException.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/TaskRegistrar.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/TaskRegistrar.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/TaskRegistrar.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/TaskRegistrar.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java
new file mode 100644
index 0000000..87be047
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputer.java
@@ -0,0 +1,154 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.bound.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.GenericObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Computes the minimal bounding box of an entity stream.
+ * 
+ * A bound entity is emitted iff there are nodes on the input stream.
+ * 
+ * Upstream bound entities are never passed through. If there is a bound entity
+ * on the input stream, it is overwritten if there are any nodes or removed if
+ * there are no nodes on the input stream.
+ * 
+ * This implementation caches all objects of the input stream in a simple object
+ * store.
+ * 
+ * @author Igor Podolskiy
+ */
+public class BoundComputer implements SinkSource, EntityProcessor {
+
+	private Sink sink;
+	private SimpleObjectStore<EntityContainer> objects;
+	private double top;
+	private double bottom;
+	private double left;
+	private double right;
+	private boolean nodesSeen;
+	private String origin;
+
+
+	/**
+	 * Creates a new bounding box computer instance.
+	 * 
+	 * @param origin
+	 *            The origin for the bound to set.
+	 */
+
+	public BoundComputer(String origin) {
+		objects = new SimpleObjectStore<EntityContainer>(new GenericObjectSerializationFactory(), "cbbo", true);
+		bottom = 0;
+		top = 0;
+		left = 0;
+		right = 0;
+		nodesSeen = false;
+		this.origin = origin;
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaTags) {
+		sink.initialize(metaTags);
+	}
+
+
+	@Override
+	public void process(EntityContainer entityContainer) {
+		entityContainer.process(this);
+	}
+
+
+	@Override
+	public void complete() {
+		objects.complete();
+
+		if (nodesSeen) {
+			sink.process(new BoundContainer(new Bound(right, left, top, bottom, this.origin)));
+		}
+
+		ReleasableIterator<EntityContainer> iter = null;
+
+		try {
+			iter = objects.iterate();
+
+			while (iter.hasNext()) {
+				sink.process(iter.next());
+			}
+		} finally {
+			if (iter != null) {
+				iter.release();
+			}
+		}
+
+		sink.complete();
+	}
+
+
+	@Override
+	public void release() {
+		sink.release();
+		objects.release();
+	}
+
+
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+
+
+	@Override
+	public void process(BoundContainer bound) {
+		// Do nothing, we'll generate a new bound later on
+	}
+
+
+	@Override
+	public void process(NodeContainer nodeContainer) {
+		Node node = nodeContainer.getEntity();
+
+		if (nodesSeen) {
+			left = Math.min(left, node.getLongitude());
+			right = Math.max(right, node.getLongitude());
+
+			bottom = Math.min(bottom, node.getLatitude());
+			top = Math.max(top, node.getLatitude());
+		} else {
+			left = node.getLongitude();
+			right = node.getLongitude();
+			top = node.getLatitude();
+			bottom = node.getLatitude();
+			nodesSeen = true;
+		}
+
+		objects.add(nodeContainer);
+	}
+
+
+	@Override
+	public void process(WayContainer way) {
+		objects.add(way);
+	}
+
+
+	@Override
+	public void process(RelationContainer relation) {
+		objects.add(relation);
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java
new file mode 100644
index 0000000..07b139c
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetter.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.bound.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Ensures the bound entity in the output stream exists with a specific value or
+ * is not present.
+ * 
+ * @author Igor Podolskiy
+ */
+public class BoundSetter implements SinkSource {
+
+	private Sink sink;
+	private boolean boundProcessed;
+	private Bound newBound;
+
+
+	/**
+	 * Creates a new instance of the bound setter.
+	 * 
+	 * @param newBound
+	 *            the new bound to set, or <pre>null</pre> to remove the bound
+	 */
+	public BoundSetter(Bound newBound) {
+		this.newBound = newBound;
+		this.boundProcessed = false;
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaTags) {
+		sink.initialize(metaTags);
+	}
+
+
+	@Override
+	public void process(EntityContainer entityContainer) {
+		if (boundProcessed) {
+			sink.process(entityContainer);
+		} else {
+			// processFirstEntity will send all data downstream as needed
+			processFirstEntity(entityContainer);
+			boundProcessed = true;
+		}
+	}
+
+
+	private void processFirstEntity(EntityContainer entityContainer) {
+		if (entityContainer.getEntity().getType() == EntityType.Bound) {
+			if (newBound == null) {
+				// Just returning won't pass the entity downstream
+				return;
+			} else {
+				sink.process(new BoundContainer(newBound));
+			}
+		} else {
+			if (newBound != null) {
+				sink.process(new BoundContainer(newBound));
+			}
+			sink.process(entityContainer);
+		}
+	}
+
+
+	@Override
+	public void complete() {
+		sink.complete();
+	}
+
+
+	@Override
+	public void release() {
+		sink.release();
+	}
+
+
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java
new file mode 100644
index 0000000..f2ace03
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBuffer.java
@@ -0,0 +1,95 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.buffer.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkRunnableChangeSource;
+
+
+/**
+ * Splits the pipeline so that it can be processed on multiple threads. The
+ * input thread to this task stores data in a buffer which blocks if it fills
+ * up. This task runs on a new thread which reads data from the buffer and
+ * writes it to the destination.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeBuffer implements ChangeSinkRunnableChangeSource {
+	private ChangeSink changeSink;
+	private DataPostbox<ChangeContainer> buffer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param bufferCapacity
+	 *            The size of the buffer to use.
+	 */
+	public ChangeBuffer(int bufferCapacity) {
+		buffer = new DataPostbox<ChangeContainer>(bufferCapacity);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		buffer.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		buffer.put(changeContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		buffer.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		buffer.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * Sends all input data to the sink.
+	 */
+	public void run() {
+		try {
+			changeSink.initialize(buffer.outputInitialize());
+			
+			while (buffer.hasNext()) {
+				changeSink.process(buffer.getNext());
+			}
+			
+			changeSink.complete();
+			buffer.outputComplete();
+			
+		} finally {
+			changeSink.release();
+			buffer.outputRelease();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBufferFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBufferFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBufferFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/ChangeBufferFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java
new file mode 100644
index 0000000..28db095
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBuffer.java
@@ -0,0 +1,95 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.buffer.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkRunnableSource;
+
+
+/**
+ * Splits the pipeline so that it can be processed on multiple threads. The
+ * input thread to this task stores data in a buffer which blocks if it fills
+ * up. This task runs on a new thread which reads data from the buffer and
+ * writes it to the destination.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityBuffer implements SinkRunnableSource {
+	private Sink sink;
+	private DataPostbox<EntityContainer> buffer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param bufferCapacity
+	 *            The size of the buffer to use.
+	 */
+	public EntityBuffer(int bufferCapacity) {
+		buffer = new DataPostbox<EntityContainer>(bufferCapacity);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		buffer.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		buffer.put(entityContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		buffer.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		buffer.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * Sends all input data to the sink.
+	 */
+	public void run() {
+		try {
+			sink.initialize(buffer.outputInitialize());
+			
+			while (buffer.hasNext()) {
+				sink.process(buffer.getNext());
+			}
+			
+			sink.complete();
+			buffer.outputComplete();
+			
+		} finally {
+			sink.release();
+			buffer.outputRelease();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBufferFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBufferFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBufferFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/buffer/v0_6/EntityBufferFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/change/v0_6/impl/TimestampSetter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/change/v0_6/impl/TimestampSetter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/change/v0_6/impl/TimestampSetter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/change/v0_6/impl/TimestampSetter.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java
new file mode 100644
index 0000000..81f6db9
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/cli/CommandLineParser.java
@@ -0,0 +1,384 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.cli;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.pipeline.common.PipelineConstants;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+
+
+/**
+ * Parses command line arguments into a form that can be consumed by the rest of
+ * the application.
+ * 
+ * @author Brett Henderson
+ */
+public class CommandLineParser {
+	
+	private static final String GLOBAL_ARGUMENT_PREFIX = "-";
+	private static final String TASK_ARGUMENT_PREFIX = "--";
+	private static final String OPTION_QUIET_SHORT = "q";
+	private static final String OPTION_QUIET_LONG = "quiet";
+	private static final String OPTION_VERBOSE_SHORT = "v";
+	private static final String OPTION_VERBOSE_LONG = "verbose";
+	private static final String OPTION_PLUGIN_SHORT = "p";
+	private static final String OPTION_PLUGIN_LONG = "plugin";
+	
+	
+	/**
+	 * The index into the LOG_LEVELS array for the default log level.
+	 */
+	private static final int DEFAULT_LOG_LEVEL_INDEX = 3;
+	
+	
+	private List<TaskConfiguration> taskConfigList;
+	private int quietValue;
+	private int verboseValue;
+	private List<String> plugins;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public CommandLineParser() {
+		taskConfigList = new ArrayList<TaskConfiguration>();
+		
+		quietValue = 0;
+		verboseValue = 0;
+		plugins = new ArrayList<String>();
+	}
+	
+	
+	/**
+	 * Parses the command line arguments.
+	 * 
+	 * @param programArgs
+	 *            The arguments.
+	 */
+	public void parse(String [] programArgs) {
+		List<GlobalOptionConfiguration> globalOptions;
+		
+		// Create the global options list.
+		globalOptions = new ArrayList<GlobalOptionConfiguration>();
+		
+		// Process the command line arguments to build all nodes in the pipeline.
+		for (int i = 0; i < programArgs.length;) {
+			String arg;
+			
+			arg = programArgs[i];
+			
+			if (arg.indexOf(TASK_ARGUMENT_PREFIX) == 0) {
+				i = parseTask(programArgs, i);
+			} else if (arg.indexOf(GLOBAL_ARGUMENT_PREFIX) == 0) {
+				i = parseGlobalOption(globalOptions, programArgs, i);
+			} else {
+				throw new OsmosisRuntimeException("Expected argument " + (i + 1) + " to be an option or task name.");
+			}
+		}
+		
+		// Process the global options.
+		for (GlobalOptionConfiguration globalOption : globalOptions) {
+			if (isArgumentForOption(OPTION_QUIET_SHORT, OPTION_QUIET_LONG, globalOption.name)) {
+				quietValue = parseOptionIntegerWithDefault(globalOption, 0) + 1;
+			} else if (isArgumentForOption(OPTION_VERBOSE_SHORT, OPTION_VERBOSE_LONG, globalOption.name)) {
+				verboseValue = parseOptionIntegerWithDefault(globalOption, 0) + 1;
+			} else if (isArgumentForOption(OPTION_PLUGIN_SHORT, OPTION_PLUGIN_LONG, globalOption.name)) {
+				plugins.add(parseOptionString(globalOption));
+			} else {
+				throw new OsmosisRuntimeException("Argument " + (globalOption.offset + 1)
+						+ " specifies an unrecognised option \"" + GLOBAL_ARGUMENT_PREFIX + globalOption.name
+						+ "\".");
+			}
+		}
+	}
+	
+	
+	/**
+	 * Checks if the current command line argument is for the specified option.
+	 * 
+	 * @param shortOptionName
+	 *            The short name of the option to check for.
+	 * @param longOptionName
+	 *            The long name of the option to check for.
+	 * @param argument
+	 *            The command line argument without the option prefix.
+	 * @return True if the argument is for the specified option.
+	 */
+	private boolean isArgumentForOption(String shortOptionName, String longOptionName, String argument) {
+		return shortOptionName.equals(argument) || longOptionName.equals(argument);
+	}
+	
+	
+	/**
+	 * Determines if an argument is a parameter to the current option/task or
+	 * the start of another option/task.
+	 * 
+	 * @param argument
+	 *            The argument.
+	 * @return True if this is an option parameter.
+	 */
+	private boolean isOptionParameter(String argument) {
+		if (argument.length() >= GLOBAL_ARGUMENT_PREFIX.length()) {
+			if (argument.substring(0, GLOBAL_ARGUMENT_PREFIX.length()).equals(GLOBAL_ARGUMENT_PREFIX)) {
+				return false;
+			}
+		}
+		
+		if (argument.length() >= TASK_ARGUMENT_PREFIX.length()) {
+			if (argument.substring(0, TASK_ARGUMENT_PREFIX.length()).equals(TASK_ARGUMENT_PREFIX)) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
+	
+	/**
+	 * Parses a command line option into an integer. If none is specified, zero
+	 * will be returned.
+	 * 
+	 * @param globalOption
+	 *            The global option to be parsed.
+	 * @return The integer value.
+	 */
+	private int parseOptionIntegerWithDefault(GlobalOptionConfiguration globalOption, int defaultValue) {
+		int result;
+		
+		// If no parameters are available, we use the default value.
+		if (globalOption.parameters.size() <= 0) {
+			return defaultValue;
+		}
+		
+		// An integer option may only have one parameter.
+		if (globalOption.parameters.size() > 1) {
+			throw new OsmosisRuntimeException(
+					"Expected argument " + (globalOption.offset + 1) + " to have no more than one parameter.");
+		}
+		
+		// Parse the option.
+		try {
+			result = Integer.parseInt(globalOption.parameters.get(0));
+			
+		} catch (NumberFormatException e) {
+			throw new OsmosisRuntimeException(
+					"Expected argument " + (globalOption.offset + 2) + " to contain an integer value.");
+		}
+		
+		return result;
+	}
+	
+	
+	/**
+	 * Parses a command line option into an string.
+	 * 
+	 * @param globalOption
+	 *            The global option to be parsed.
+	 */
+	private String parseOptionString(GlobalOptionConfiguration globalOption) {
+		// A string option must have one parameter.
+		if (globalOption.parameters.size() != 1) {
+			throw new OsmosisRuntimeException(
+					"Expected argument " + (globalOption.offset + 1) + " to have one parameter.");
+		}
+		
+		return globalOption.parameters.get(0);
+	}
+	
+	
+	/**
+	 * Parses the details of a single option.
+	 * 
+	 * @param globalOptions
+	 *            The list of global options being parsed, the new option will
+	 *            be stored into this object.
+	 * @param programArgs
+	 *            The command line arguments passed to this application.
+	 * @param offset
+	 *            The current offset through the command line arguments.
+	 * @return The new offset through the command line arguments.
+	 */
+	private int parseGlobalOption(List<GlobalOptionConfiguration> globalOptions, String [] programArgs, int offset) {
+		int i;
+		String argument;
+		GlobalOptionConfiguration globalOption;
+		
+		i = offset;
+		argument = programArgs[i++].substring(1);
+		
+		globalOption = new GlobalOptionConfiguration();
+		globalOption.name = argument;
+		globalOption.offset = offset;
+		
+		// Loop until the next option or task is reached and add the arguments as parameters to the option.
+		while ((i < programArgs.length) && isOptionParameter(programArgs[i])) {
+			globalOption.parameters.add(programArgs[i++]);
+		}
+		
+		// Add the fully populated global option object to the list.
+		globalOptions.add(globalOption);
+		
+		return i;
+	}
+	
+	
+	/**
+	 * Parses the details of a single task and creates a task information object.
+	 * 
+	 * @param programArgs
+	 *            The command line arguments passed to this application.
+	 * @param offset
+	 *            The current offset through the command line arguments.
+	 * @return The new offset through the command line arguments.
+	 */
+	private int parseTask(String [] programArgs, int offset) {
+		int i;
+		String taskType;
+		Map<String, String> taskArgs;
+		Map<String, String> pipeArgs;
+		String taskId;
+		int defaultArgIndex;
+		String defaultArg;
+		
+		i = offset;
+		
+		// Extract the task type from the current argument.
+		taskType = programArgs[i++].substring(TASK_ARGUMENT_PREFIX.length());
+		
+		// Build up a list of task and pipe arguments.
+		taskArgs = new HashMap<String, String>();
+		pipeArgs = new HashMap<String, String>();
+		defaultArg = null;
+		defaultArgIndex = -1;
+		while (i < programArgs.length) {
+			String arg;
+			int equalsIndex;
+			String argName;
+			String argValue;
+			
+			arg = programArgs[i];
+			
+			if (arg.indexOf(TASK_ARGUMENT_PREFIX) == 0) {
+				break;
+			}
+			
+			equalsIndex = arg.indexOf("=");
+			
+			// If an equals sign exists this is a named argument, otherwise it is a default argument.
+			if (equalsIndex >= 0) {
+				// Check if the name component of the argument exists.
+				if (equalsIndex == 0) {
+					throw new OsmosisRuntimeException(
+							"Argument " + (i + 1) + " doesn't contain a name before the '=' (ie. name=value).");
+				}
+				
+				// Check if the value component of the argument exists.
+				if (equalsIndex >= (arg.length() - 1)) {
+					throw new OsmosisRuntimeException(
+							"Argument " + (i + 1) + " doesn't contain a value after the '=' (ie. name=value).");
+				}
+				
+				// Split the argument into name and value.
+				argName = arg.substring(0, equalsIndex);
+				argValue = arg.substring(equalsIndex + 1);
+				
+				// Add pipeline arguments to pipeArgs, all other arguments to taskArgs.
+				// A pipeline arg is inPipe, inPipe.x, outPipe or outPipe.x.
+				if (
+						PipelineConstants.IN_PIPE_ARGUMENT_PREFIX.equals(argName)
+						|| argName.indexOf(PipelineConstants.IN_PIPE_ARGUMENT_PREFIX + ".") == 0
+						|| PipelineConstants.OUT_PIPE_ARGUMENT_PREFIX.equals(argName)
+						|| argName.indexOf(PipelineConstants.OUT_PIPE_ARGUMENT_PREFIX + ".") == 0) {
+					pipeArgs.put(argName, argValue);
+				} else {
+					taskArgs.put(argName, argValue);
+				}
+				
+			} else {
+				if (defaultArgIndex >= 0) {
+					throw new OsmosisRuntimeException(
+							"Only one default (un-named) argument can exist per task.  Arguments "
+							+ (i + 1) + " and " + (defaultArgIndex + 1) + " have no name.");
+				}
+				
+				defaultArg = arg;
+				defaultArgIndex = i;
+			}
+			
+			i++;
+		}
+		
+		// Build a unique task id.
+		taskId = (taskConfigList.size() + 1) + "-" + taskType;
+		
+		// Create a new task information object and add it to the list.
+		taskConfigList.add(
+			new TaskConfiguration(taskId, taskType, pipeArgs, taskArgs, defaultArg)
+		);
+		
+		return i;
+	}
+	
+	
+	/**
+	 * The list of task information objects.
+	 * 
+	 * @return The taskInfoList.
+	 */
+	public List<TaskConfiguration> getTaskInfoList() {
+		return taskConfigList;
+	}
+	
+	
+	/**
+	 * Gets the level of logging required. This is a number that can be used to
+	 * access a log level from the LogLevels class.
+	 * 
+	 * @return The index of the log level to be used.
+	 */
+	public int getLogLevelIndex() {
+		return DEFAULT_LOG_LEVEL_INDEX + verboseValue - quietValue;
+	}
+	
+	
+	/**
+	 * Returns the plugins to be loaded.
+	 * 
+	 * @return The list of plugin class names.
+	 */
+	public List<String> getPlugins() {
+		return plugins;
+	}
+	
+	
+	/**
+	 * A data storage class holding information relating to a global option
+	 * during parsing.
+	 */
+	private class GlobalOptionConfiguration {
+		/**
+		 * The name of the option.
+		 */
+		public String name;
+		/**
+		 * The parameters for the option.
+		 */
+		public List<String> parameters;
+		/**
+		 * The command line argument offset of this global option.
+		 */
+		public int offset;
+		
+		
+		/**
+		 * Creates a new instance.
+		 */
+		public GlobalOptionConfiguration() {
+			parameters = new ArrayList<String>();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/BoundContainerIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/ChangeContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/ChangeContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/ChangeContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/ChangeContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/Dataset.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/Dataset.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/Dataset.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/Dataset.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/DatasetContext.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/DatasetContext.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/DatasetContext.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/DatasetContext.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerBuilder.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerBuilder.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerBuilder.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerBuilder.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityContainerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityProcessor.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityProcessor.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityProcessor.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/EntityProcessor.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/NodeContainerIterator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java
new file mode 100644
index 0000000..20fcc45
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainer.java
@@ -0,0 +1,82 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.container.v0_6;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+
+
+/**
+ * Entity container implementation for relations.
+ * 
+ * @author Brett Henderson
+ */
+public class RelationContainer extends EntityContainer {
+	
+	private Relation relation;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param relation
+	 *            The relation to wrap.
+	 */
+	public RelationContainer(Relation relation) {
+		this.relation = relation;
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers
+	 *            within the store.
+	 */
+	public RelationContainer(StoreReader sr, StoreClassRegister scr) {
+		relation = new Relation(sr, scr);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void store(StoreWriter sw, StoreClassRegister scr) {
+		relation.store(sw, scr);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(EntityProcessor processor) {
+		processor.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Relation getEntity() {
+		return relation;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public RelationContainer getWriteableInstance() {
+		if (relation.isReadOnly()) {
+			return new RelationContainer(relation.getWriteableInstance());
+		} else {
+			return this;
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/RelationContainerIterator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java
new file mode 100644
index 0000000..ae367ae
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainer.java
@@ -0,0 +1,82 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.container.v0_6;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+
+
+/**
+ * Entity container implementation for ways.
+ * 
+ * @author Brett Henderson
+ */
+public class WayContainer extends EntityContainer {
+	
+	private Way way;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param way
+	 *            The way to wrap.
+	 */
+	public WayContainer(Way way) {
+		this.way = way;
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers
+	 *            within the store.
+	 */
+	public WayContainer(StoreReader sr, StoreClassRegister scr) {
+		way = new Way(sr, scr);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void store(StoreWriter sw, StoreClassRegister scr) {
+		way.store(sw, scr);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(EntityProcessor processor) {
+		processor.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Way getEntity() {
+		return way;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public WayContainer getWriteableInstance() {
+		if (way.isReadOnly()) {
+			return new WayContainer(way.getWriteableInstance());
+		} else {
+			return this;
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/container/v0_6/WayContainerIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/AuthenticationPropertiesLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/AuthenticationPropertiesLoader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/AuthenticationPropertiesLoader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/AuthenticationPropertiesLoader.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java
new file mode 100644
index 0000000..0774c5a
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseConstants.java
@@ -0,0 +1,114 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.database;
+
+
+/**
+ * Defines common constants shared between database tasks.
+ * 
+ * @author Brett Henderson
+ */
+public final class DatabaseConstants {
+	
+	/**
+	 * This class cannot be instantiated.
+	 */
+	private DatabaseConstants() {
+	}
+	
+
+    /**
+     * The task argument for specifying an database authorisation properties file.
+     */
+    public static final String TASK_ARG_AUTH_FILE = "authFile";
+
+    /**
+     * The task argument for specifying the host for a database connection.
+     */
+    public static final String TASK_ARG_HOST = "host";
+
+    /**
+     * The task argument for specifying the database instance for a database connection.
+     */
+    public static final String TASK_ARG_DATABASE = "database";
+
+    /**
+     * The task argument for specifying the user for a database connection.
+     */
+    public static final String TASK_ARG_USER = "user";
+    
+    /**
+     * The task argument for specifying the database type to be used.
+     */
+    public static final String TASK_ARG_DB_TYPE = "dbType";
+
+    /**
+     * The task argument for specifying the password for a database connection.
+     */
+    public static final String TASK_ARG_PASSWORD = "password";
+
+    /**
+     * The task argument for specifying whether schema version validation should be performed.
+     */
+    public static final String TASK_ARG_VALIDATE_SCHEMA_VERSION = "validateSchemaVersion";
+
+    /**
+     * The task argument for specifying what should occur if an invalid schema version is
+     * encountered.
+     */
+    public static final String TASK_ARG_ALLOW_INCORRECT_SCHEMA_VERSION = "allowIncorrectSchemaVersion";
+
+    /**
+     * The task argument for forcing a utf-8 database connection.
+     */
+    public static final String TASK_ARG_FORCE_UTF8 = "forceUtf8";
+
+    /**
+     * The task argument for enabling profiling on the database connection.
+     */
+    public static final String TASK_ARG_PROFILE_SQL = "profileSql";
+
+    /**
+     * The default host for a database connection.
+     */
+    public static final String TASK_DEFAULT_HOST = "localhost";
+
+    /**
+     * The default database for a database connection.
+     */
+    public static final String TASK_DEFAULT_DATABASE = "osm";
+
+    /**
+     * The default user for a database connection.
+     */
+    public static final String TASK_DEFAULT_USER = null;
+
+    /**
+     * The default password for a database connection.
+     */
+    public static final DatabaseType TASK_DEFAULT_DB_TYPE = DatabaseType.POSTGRESQL;
+
+    /**
+     * The default password for a database connection.
+     */
+    public static final String TASK_DEFAULT_PASSWORD = null;
+
+    /**
+     * The default value for whether schema version validation should be performed.
+     */
+    public static final boolean TASK_DEFAULT_VALIDATE_SCHEMA_VERSION = true;
+
+    /**
+     * The default value for whether the program should allow an incorrect schema version.
+     */
+    public static final boolean TASK_ALLOW_INCORRECT_SCHEMA_VERSION = false;
+
+    /**
+     * The default value for forcing a utf-8 connection.
+     */
+    public static final boolean TASK_DEFAULT_FORCE_UTF8 = false;
+
+    /**
+     * The default value for enabling profile on a database connection.
+     */
+    public static final boolean TASK_DEFAULT_PROFILE_SQL = false;
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseLoginCredentials.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseLoginCredentials.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseLoginCredentials.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseLoginCredentials.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabasePreferences.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabasePreferences.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DatabasePreferences.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabasePreferences.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseTaskManagerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseTaskManagerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseTaskManagerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseTaskManagerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseType.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseType.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseType.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DatabaseType.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeature.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeature.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeature.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeature.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryRowMapper.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryRowMapper.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryRowMapper.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureHistoryRowMapper.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureRowMapper.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureRowMapper.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureRowMapper.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbFeatureRowMapper.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeature.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeature.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeature.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeature.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureHistoryComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureHistoryComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureHistoryComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureHistoryComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureRowMapper.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureRowMapper.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureRowMapper.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/DbOrderedFeatureRowMapper.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/FeatureCollectionLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/FeatureCollectionLoader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/FeatureCollectionLoader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/FeatureCollectionLoader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/FeaturePopulator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/FeaturePopulator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/FeaturePopulator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/FeaturePopulator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/RelationMemberCollectionLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/RelationMemberCollectionLoader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/RelationMemberCollectionLoader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/RelationMemberCollectionLoader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/ReleasableStatementContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/ReleasableStatementContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/ReleasableStatementContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/ReleasableStatementContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/RowMapperListener.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/RowMapperListener.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/RowMapperListener.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/RowMapperListener.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/SortingStoreRowMapperListener.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/SortingStoreRowMapperListener.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/SortingStoreRowMapperListener.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/SortingStoreRowMapperListener.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/database/WayNodeCollectionLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/WayNodeCollectionLoader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/database/WayNodeCollectionLoader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/database/WayNodeCollectionLoader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/common/SimpleTimestampContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/SimpleTimestampContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/common/SimpleTimestampContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/SimpleTimestampContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampFormat.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampFormat.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampFormat.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/TimestampFormat.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/common/UnparsedTimestampContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/UnparsedTimestampContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/common/UnparsedTimestampContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/common/UnparsedTimestampContainer.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java
new file mode 100644
index 0000000..e5255b7
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Bound.java
@@ -0,0 +1,515 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.domain.v0_6;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+
+/**
+ * A data class representing an OSM data bound element.
+ * 
+ * @author Karl Newman
+ */
+public class Bound extends Entity implements Comparable<Bound> {
+	
+	private static final double MIN_LATITUDE = -90.0;
+	private static final double MAX_LATITUDE = 90.0;
+	private static final double MIN_LONGITUDE = -180.0;
+	private static final double MAX_LONGITUDE = 180.0;
+	
+	private double right;
+	private double left;
+	private double top;
+	private double bottom;
+	private String origin;
+	
+	
+	/**
+	 * Creates a new instance which covers the entire planet.
+	 * 
+	 * @param origin
+	 *            The origin (source) of the data, typically a URI
+	 * 
+	 */
+	public Bound(String origin) {
+		this(MAX_LONGITUDE, MIN_LONGITUDE, MAX_LATITUDE, MIN_LATITUDE, origin);
+	}
+
+
+	/**
+	 * Creates a new instance with the specified boundaries.
+	 * 
+	 * @param right
+	 *            The longitude coordinate of the right (East) edge of the bound
+	 * @param left
+	 *            The longitude coordinate of the left (West) edge of the bound
+	 * @param top
+	 *            The latitude coordinate of the top (North) edge of the bound
+	 * @param bottom
+	 *            The latitude coordinate of the bottom (South) edge of the bound
+	 * @param origin
+	 *            The origin (source) of the data, typically a URI
+	 */
+	public Bound(double right, double left, double top, double bottom, String origin) {
+		super(new CommonEntityData(0, 0, new Date(), OsmUser.NONE, 0)); // minimal underlying entity
+		
+		// Check if any coordinates are out of bounds
+		if (Double.compare(right, MAX_LONGITUDE + 1.0d) > 0
+		        || Double.compare(right, MIN_LONGITUDE - 1.0d) < 0
+		        || Double.compare(left, MAX_LONGITUDE + 1.0d) > 0
+		        || Double.compare(left, MIN_LONGITUDE - 1.0d) < 0
+		        || Double.compare(top, MAX_LATITUDE + 1.0d) > 0
+		        || Double.compare(top, MIN_LATITUDE - 1.0d) < 0
+		        || Double.compare(bottom, MAX_LATITUDE + 1.0d) > 0
+		        || Double.compare(bottom, MIN_LATITUDE - 1.0d) < 0) {
+			throw new IllegalArgumentException("Bound coordinates outside of valid range");
+		}
+		if (Double.compare(top, bottom) < 0) {
+			throw new IllegalArgumentException("Bound top < bottom");
+		}
+		this.right = right;
+		this.left = left;
+		this.top = top;
+		this.bottom = bottom;
+		this.origin = origin;
+	}
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers within the store.
+	 */
+	public Bound(StoreReader sr, StoreClassRegister scr) {
+		super(sr, scr);
+
+		this.right = sr.readDouble();
+		this.left = sr.readDouble();
+		this.top = sr.readDouble();
+		this.bottom = sr.readDouble();
+		this.origin = sr.readString();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void store(StoreWriter sw, StoreClassRegister scr) {
+		super.store(sw, scr);
+
+		sw.writeDouble(right);
+		sw.writeDouble(left);
+		sw.writeDouble(top);
+		sw.writeDouble(bottom);
+		sw.writeString(origin);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityType getType() {
+		return EntityType.Bound;
+	}
+
+
+	/**
+	 * @return The right (East) bound longitude
+	 */
+	public double getRight() {
+		return right;
+	}
+
+
+	/**
+	 * @return The left (West) bound longitude
+	 */
+	public double getLeft() {
+		return left;
+	}
+
+
+	/**
+	 * @return The top (North) bound latitude
+	 */
+	public double getTop() {
+		return top;
+	}
+
+
+	/**
+	 * @return The bottom (South) bound latitude
+	 */
+	public double getBottom() {
+		return bottom;
+	}
+
+
+	/**
+	 * @return the origin
+	 */
+	public String getOrigin() {
+		return origin;
+	}
+
+
+	/**
+	 * Calculate the intersected area of this with the specified bound.
+	 * 
+	 * @param intersectingBound
+	 *            Bound element with which to calculate the intersection
+	 * @return Bound Resultant intersection of the two bound object
+	 */
+	public Bound intersect(Bound intersectingBound) {
+		String newOrigin;
+		double newRight = 0.0, newLeft = 0.0, newTop, newBottom;
+
+		boolean intersect180, this180; // flags to indicate bound cross antimeridian
+
+		if (intersectingBound == null) {
+			return null; // no intersection
+		}
+		// first check the vertical intersection
+		newTop = Math.min(this.getTop(), intersectingBound.getTop());
+		newBottom = Math.max(this.getBottom(), intersectingBound.getBottom());
+		if (Double.compare(newBottom, newTop) >= 0) { // no north-south intersecting region
+			return null;
+		}
+
+		intersect180 = (Double.compare(intersectingBound.getLeft(), intersectingBound.getRight()) > 0);
+		this180 = (Double.compare(this.getLeft(), this.getRight()) > 0);
+
+		if ((intersect180 && this180) || !(intersect180 || this180)) {
+			// if both or neither cross the antimeridian, use the simple case
+			newRight = Math.min(this.getRight(), intersectingBound.getRight());
+			newLeft = Math.max(this.getLeft(), intersectingBound.getLeft());
+			if (!(intersect180 || this180) && (Double.compare(newLeft, newRight) >= 0)) {
+				/*
+				 * This is only applicable for the case where neither cross the antimeridian,
+				 * because if both cross, they must intersect.
+				 */
+				return null; // no intersecting area
+			}
+		} else {
+			Bound b1, b2; // stand-ins for this and intersectingBound
+
+			if (intersect180 && !this180) {
+				// passed parameter Bound crosses the antimeridian, this Bound doesn't
+				b1 = this;
+				b2 = intersectingBound;
+			} else {
+				// this Bound crosses the antimeridian, passed parameter Bound doesn't
+				b1 = intersectingBound;
+				b2 = this;
+			}
+			if (Double.compare(b1.getRight(), b2.getLeft()) > 0
+			        && Double.compare(b1.getLeft(), b2.getRight()) < 0) {
+				// intersects on both sides of the antimeridian--just pick the smaller of the
+				// two
+				Double diff1 = b1.getRight() - b1.getLeft();
+				Double diff2 = b2.getRight() - MIN_LONGITUDE + MAX_LONGITUDE - b2.getLeft();
+				if (Double.compare(diff1, diff2) <= 0) {
+					newRight = b1.getRight();
+					newLeft = b1.getLeft();
+				} else {
+					newRight = b2.getRight();
+					newLeft = b2.getLeft();
+				}
+			} else if (Double.compare(b1.getRight(), b2.getLeft()) > 0) {
+				// intersects on the East side of the antimeridian
+				newRight = b1.getRight();
+				newLeft = b2.getLeft();
+			} else if (Double.compare(b1.getLeft(), b2.getRight()) < 0) {
+				// intersects on the West side of the antimeridian
+				newRight = b2.getRight();
+				newLeft = b1.getLeft();
+			}
+		}
+		if (Double.compare(newRight, newLeft) == 0) {
+			return null;
+		}
+		
+		// Keep the origin string from this if it's not blank, otherwise use the origin string from
+		// the intersecting Bound
+		if (origin != "") {
+			newOrigin = origin;
+		} else {
+			newOrigin = intersectingBound.origin;
+		}
+		
+		return new Bound(newRight, newLeft, newTop, newBottom, newOrigin);
+	}
+
+
+	/**
+	 * Calculate the union area of this with the specified bound. Not a strict mathematical union,
+	 * but the smallest rectangular area which includes both bound. Thus, result may include areas
+	 * not contained in the original bound.
+	 * 
+	 * @param unionBound
+	 *            Bound element with which to calculate the union
+	 * @return Bound Resultant union of the two bound objects
+	 */
+	public Bound union(Bound unionBound) {
+		double newRight = 0.0, newLeft = 0.0, newTop, newBottom;
+		String newOrigin;
+
+		if (unionBound == null) {
+			return this; // nothing to compute a union with
+		}
+
+		// First compute the vertical union
+		newTop = Math.max(this.getTop(), unionBound.getTop());
+		newBottom = Math.min(this.getBottom(), unionBound.getBottom());
+		if (Double.compare(newBottom, newTop) >= 0) { // no north-south intersecting region
+			return null;
+		}
+		// Next check the (likely) common case where one of the bound covers the planet
+		if ((Double.compare(this.getLeft(), MIN_LONGITUDE) == 0 && Double.compare(
+		        this.getRight(),
+		        MAX_LONGITUDE) == 0)
+		        || (Double.compare(unionBound.getLeft(), MIN_LONGITUDE) == 0 && Double.compare(
+		                unionBound.getRight(),
+		                MAX_LONGITUDE) == 0)) {
+			newRight = MAX_LONGITUDE;
+			newLeft = MIN_LONGITUDE;
+		} else {
+			boolean union180, this180; // flags to indicate bound cross antimeridian
+			double size1, size2; // resulting union sizes for comparison
+
+			union180 = (Double.compare(unionBound.getLeft(), unionBound.getRight()) > 0);
+			this180 = (Double.compare(this.getLeft(), this.getRight()) > 0);
+
+			if (union180 && this180) {
+				// if both cross the antimeridian, then the union will cross, too.
+				newRight = Math.max(this.getRight(), unionBound.getRight());
+				newLeft = Math.min(this.getLeft(), unionBound.getLeft());
+			} else if (!(union180 || this180)) {
+				// neither cross the antimeridian, but the union might
+
+				// first calculate the size of a simple union which doesn't cross the antimeridian
+				size1 = Math.max(this.getRight(), unionBound.getRight())
+				        - Math.min(this.getLeft(), unionBound.getLeft());
+				// then calculate the size of the resulting union which does cross the antimeridian
+				size2 = (Math.min(this.getRight(), unionBound.getRight()) - MIN_LONGITUDE)
+				        + (MAX_LONGITUDE - Math.max(this.getLeft(), unionBound.getLeft()));
+
+				// now pick the smaller of the two
+				if (Double.compare(size1, size2) <= 0) {
+					newRight = Math.max(this.getRight(), unionBound.getRight());
+					newLeft = Math.min(this.getLeft(), unionBound.getLeft());
+				} else {
+					newRight = Math.min(this.getRight(), unionBound.getRight());
+					newLeft = Math.max(this.getLeft(), unionBound.getLeft());
+				}
+			} else {
+				// One of the Bound crosses the antimeridian, the other doesn't
+				Bound b1, b2;
+				if (union180 && !this180) {
+					// passed parameter Bound crosses the antimeridian, this Bound doesn't
+					b1 = unionBound;
+					b2 = this;
+				} else {
+					// this Bound crosses the antimeridian, passed parameter Bound doesn't
+					b1 = this;
+					b2 = unionBound;
+				}
+
+				// check for the case where the two Bound overlap on both edges such that the union
+				// covers the planet.
+				if (Double.compare(b1.getRight(), b2.getLeft()) >= 0
+				        && Double.compare(b1.getLeft(), b2.getRight()) <= 0) {
+					newLeft = MIN_LONGITUDE;
+					newRight = MAX_LONGITUDE;
+				} else {
+					// first calculate the size of a union with the simple bound added to the left
+					size1 = (Math.max(b1.getRight(), b2.getRight()) - MIN_LONGITUDE)
+					        + (MAX_LONGITUDE - b1.getLeft());
+					// first calculate the size of a union with the simple bound added to the right
+					size2 = (b1.getRight() - MIN_LONGITUDE)
+					        + (MAX_LONGITUDE - Math.min(b1.getLeft(), b2.getLeft()));
+
+					// now pick the smaller of the two
+					if (Double.compare(size1, size2) <= 0) {
+						newRight = Math.max(b1.getRight(), b2.getRight());
+						newLeft = b1.getLeft();
+					} else {
+						newRight = b1.getRight();
+						newLeft = Math.min(b1.getLeft(), b2.getLeft());
+					}
+				}
+			}
+		}
+
+		if (Double.compare(newRight, newLeft) == 0) {
+			return null;
+		}
+		
+		// Keep the origin string from this if it's not blank, otherwise use the origin string from
+		// the union Bound
+		if (this.getOrigin() != null && !this.getOrigin().equals("")) {
+			newOrigin = getOrigin();
+		} else {
+			newOrigin = unionBound.getOrigin();
+		}
+		
+		return new Bound(newRight, newLeft, newTop, newBottom, newOrigin);
+	}
+
+
+	/**
+	 * Retrieve a collection of Bound objects which collectively comprise the entirety of this
+	 * Bound but individually do not cross the antimeridian and thus can be used in simple area
+	 * operations. The degenerate case will return this Bound.
+	 * 
+	 * @return Iterable collection of Bound elements
+	 */
+	public Iterable<Bound> toSimpleBound() {
+		Collection<Bound> c = new LinkedList<Bound>();
+		if (Double.compare(this.getLeft(), this.getRight()) < 0) {
+			// simple case, just return this
+			c.add(this);
+		} else {
+			// split the bound into two parts--one on either side of the antimeridian
+			c.add(new Bound(
+			        MAX_LONGITUDE,
+			        this.getLeft(),
+			        this.getTop(),
+			        this.getBottom(),
+			        this.getOrigin()));
+			c.add(new Bound(
+			        this.getRight(),
+			        MIN_LONGITUDE,
+			        this.getTop(),
+			        this.getBottom(),
+			        this.getOrigin()));
+		}
+		return Collections.unmodifiableCollection(c);
+	}
+
+
+	/**
+	 * Compares this bound to the specified bound. The bound comparison is based on a comparison
+	 * of area, latitude, and longitude in that order.
+	 * 
+	 * @param comparisonBound
+	 *            The bound to compare to.
+	 * @return 0 if equal, < 0 if this sorts before comparison (this is "smaller"), and > 0 if this
+	 *         sorts before comparison (this is "bigger")
+	 */
+	public int compareTo(Bound comparisonBound) {
+		double areaT = 0.0, areaC = 0.0;
+		int result;
+
+		/*
+		 * This is a very simple "area" calculation just using the coordinate values, not accounting
+		 * for any projections.
+		 */
+		for (Bound b : this.toSimpleBound()) {
+			areaT += (b.getRight() - b.getLeft()) * (b.getTop() - b.getBottom());
+		}
+		for (Bound b : comparisonBound.toSimpleBound()) {
+			areaC += (b.getRight() - b.getLeft()) * (b.getTop() - b.getBottom());
+		}
+
+		// Use Double.compare (instead of < and >) to catch unique border cases
+		result = Double.compare(areaT, areaC);
+		if (result != 0) {
+			return result;
+		}
+
+		result = Double.compare(this.getTop(), comparisonBound.getTop());
+		if (result != 0) {
+			return result;
+		}
+
+		result = Double.compare(this.getBottom(), comparisonBound.getBottom());
+		if (result != 0) {
+			return result;
+		}
+
+		result = Double.compare(this.getLeft(), comparisonBound.getLeft());
+		if (result != 0) {
+			return result;
+		}
+
+		result = Double.compare(this.getRight(), comparisonBound.getRight());
+		if (result != 0) {
+			return result;
+		}
+
+		String myOrigin = this.getOrigin();
+		String otherOrigin = comparisonBound.getOrigin();
+		
+		// null origin is considered "less" than non-null origin
+		if (myOrigin == null) {
+			if (otherOrigin == null) {
+				return 0;
+			} else {
+				return -1;
+			}
+		} else {
+			if (otherOrigin == null) {
+				return 1;
+			} else {
+				return myOrigin.compareTo(otherOrigin);
+			}
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof Bound) {
+			return compareTo((Bound) o) == 0;
+		} else {
+			return false;
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		/*
+		 * As per the hashCode definition, this doesn't have to be unique it
+		 * just has to return the same value for any two objects that compare
+		 * equal. Using both id and version will provide a good distribution of
+		 * values but is simple to calculate.
+		 */
+		return (int) getId() + getVersion();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Bound getWriteableInstance() {
+		return this;
+	}
+
+    /** 
+     * ${@inheritDoc}.
+     */
+    @Override
+    public String toString() {
+        return "Bound(top=" + getTop() + ", bottom=" + getBottom() + ", left=" + getLeft() + ", right=" + getRight()
+				+ ")";
+    }
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/CommonEntityData.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/CommonEntityData.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/CommonEntityData.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/CommonEntityData.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Entity.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Entity.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Entity.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Entity.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityBuilder.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityBuilder.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityBuilder.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityBuilder.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityType.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityType.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityType.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/EntityType.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Node.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Node.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Node.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Node.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/NodeBuilder.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/NodeBuilder.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/NodeBuilder.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/NodeBuilder.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUser.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUser.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUser.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUser.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java
new file mode 100644
index 0000000..dcc894e
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Relation.java
@@ -0,0 +1,386 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.domain.v0_6;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+
+
+/**
+ * A data class representing a single OSM relation.
+ * 
+ * @author Brett Henderson
+ */
+public class Relation extends Entity implements Comparable<Relation> {
+	private List<RelationMember> members;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestamp
+	 *            The last updated timestamp.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @deprecated As of 0.40, replaced by Relation(entityData).
+	 */
+	public Relation(long id, int version, Date timestamp, OsmUser user, long changesetId) {
+		// Chain to the more-specific constructor
+		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestampContainer
+	 *            The container holding the timestamp in an alternative
+	 *            timestamp representation.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @deprecated As of 0.40, replaced by Relation(entityData).
+	 */
+	public Relation(long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId) {
+		super(id, version, timestampContainer, user, changesetId);
+		
+		this.members = new ArrayList<RelationMember>();
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param entityData
+	 *            The common entity data.
+	 */
+	public Relation(CommonEntityData entityData) {
+		super(entityData);
+		
+		this.members = new ArrayList<RelationMember>();
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestamp
+	 *            The last updated timestamp.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @param tags
+	 *            The tags to apply to the object.
+	 * @param members
+	 *            The members to apply to the object.
+	 * @deprecated As of 0.40, replaced by Relation(entityData, members).
+	 */
+	public Relation(
+			long id, int version, Date timestamp, OsmUser user, long changesetId, Collection<Tag> tags,
+			List<RelationMember> members) {
+		// Chain to the more-specific constructor
+		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId, tags, members);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestampContainer
+	 *            The container holding the timestamp in an alternative
+	 *            timestamp representation.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @param tags
+	 *            The tags to apply to the object.
+	 * @param members
+	 *            The members to apply to the object.
+	 * @deprecated As of 0.40, replaced by Relation(entityData, members).
+	 */
+	public Relation(
+			long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId,
+			Collection<Tag> tags, List<RelationMember> members) {
+		super(id, version, timestampContainer, user, changesetId, tags);
+		
+		this.members = new ArrayList<RelationMember>(members);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param entityData
+	 *            The common entity data.
+	 * @param members
+	 *            The members to apply to the object.
+	 */
+	public Relation(
+			CommonEntityData entityData, List<RelationMember> members) {
+		super(entityData);
+		
+		this.members = new ArrayList<RelationMember>(members);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param originalRelation
+	 *            The relation to clone from.
+	 */
+	private Relation(Relation originalRelation) {
+		super(originalRelation);
+		
+		this.members = new ArrayList<RelationMember>(originalRelation.members);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers
+	 *            within the store.
+	 */
+	public Relation(StoreReader sr, StoreClassRegister scr) {
+		super(sr, scr);
+		
+		int featureCount;
+		
+		featureCount = sr.readInteger();
+		
+		members = new ArrayList<RelationMember>();
+		for (int i = 0; i < featureCount; i++) {
+			members.add(new RelationMember(sr, scr));
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void store(StoreWriter sw, StoreClassRegister scr) {
+		super.store(sw, scr);
+		
+		sw.writeInteger(members.size());
+		for (RelationMember relationMember : members) {
+			relationMember.store(sw, scr);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityType getType() {
+		return EntityType.Relation;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof Relation) {
+			return compareTo((Relation) o) == 0;
+		} else {
+			return false;
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		/*
+		 * As per the hashCode definition, this doesn't have to be unique it
+		 * just has to return the same value for any two objects that compare
+		 * equal. Using both id and version will provide a good distribution of
+		 * values but is simple to calculate.
+		 */
+		return (int) getId() + getVersion();
+	}
+	
+	
+	/**
+	 * Compares this member list to the specified member list. The bigger list
+	 * is considered bigger, if that is equal then each relation member is
+	 * compared.
+	 * 
+	 * @param comparisonMemberList
+	 *            The member list to compare to.
+	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
+	 *         "bigger".
+	 */
+	protected int compareMemberList(Collection<RelationMember> comparisonMemberList) {
+		Iterator<RelationMember> i;
+		Iterator<RelationMember> j;
+		
+		// The list with the most entities is considered bigger.
+		if (members.size() != comparisonMemberList.size()) {
+			return members.size() - comparisonMemberList.size();
+		}
+		
+		// Check the individual node references.
+		i = members.iterator();
+		j = comparisonMemberList.iterator();
+		while (i.hasNext()) {
+			int result = i.next().compareTo(j.next());
+			
+			if (result != 0) {
+				return result;
+			}
+		}
+		
+		// There are no differences.
+		return 0;
+	}
+
+
+	/**
+	 * Compares this relation to the specified relation. The relation comparison
+	 * is based on a comparison of id, version, timestamp, and tags in that order.
+	 * 
+	 * @param comparisonRelation
+	 *            The relation to compare to.
+	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
+	 *         "bigger".
+	 */
+	public int compareTo(Relation comparisonRelation) {
+		int memberListResult;
+		
+		if (this.getId() < comparisonRelation.getId()) {
+			return -1;
+		}
+		if (this.getId() > comparisonRelation.getId()) {
+			return 1;
+		}
+
+		if (this.getVersion() < comparisonRelation.getVersion()) {
+			return -1;
+		}
+		if (this.getVersion() > comparisonRelation.getVersion()) {
+			return 1;
+		}
+
+		if (this.getTimestamp() == null && comparisonRelation.getTimestamp() != null) {
+			return -1;
+		}
+		if (this.getTimestamp() != null && comparisonRelation.getTimestamp() == null) {
+			return 1;
+		}
+		if (this.getTimestamp() != null && comparisonRelation.getTimestamp() != null) {
+			int result;
+			
+			result = this.getTimestamp().compareTo(comparisonRelation.getTimestamp());
+			
+			if (result != 0) {
+				return result;
+			}
+		}
+		
+		memberListResult = compareMemberList(
+			comparisonRelation.members
+		);
+		
+		if (memberListResult != 0) {
+			return memberListResult;
+		}
+		
+		return compareTags(comparisonRelation.getTags());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void makeReadOnly() {
+		if (!isReadOnly()) {
+			members = Collections.unmodifiableList(members);
+		}
+		
+		super.makeReadOnly();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Relation getWriteableInstance() {
+		if (isReadOnly()) {
+			return new Relation(this);
+		} else {
+			return this;
+		}
+	}
+	
+	
+	/**
+	 * Returns the attached list of relation members. The returned list is
+	 * read-only.
+	 * 
+	 * @return The member list.
+	 */
+	public List<RelationMember> getMembers() {
+		return members;
+	}
+
+    /** 
+     * ${@inheritDoc}.
+     */
+    @Override
+    public String toString() {
+        String type = null;
+        Collection<Tag> tags = getTags();
+        for (Tag tag : tags) {
+            if (tag.getKey() != null && tag.getKey().equalsIgnoreCase("type")) {
+                type = tag.getValue();
+                break;
+            }
+        }
+        if (type != null) {
+            return "Relation(id=" + getId() + ", #tags=" +  getTags().size() + ", type='" + type + "')";
+        }
+        return "Relation(id=" + getId() + ", #tags=" +  getTags().size() + ")";
+    }
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationBuilder.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationBuilder.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationBuilder.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationBuilder.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationMember.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationMember.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationMember.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/RelationMember.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Tag.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Tag.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Tag.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Tag.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollection.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollection.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollection.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollection.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollectionImpl.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollectionImpl.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollectionImpl.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/TagCollectionImpl.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/UnmodifiableTagCollection.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/UnmodifiableTagCollection.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/UnmodifiableTagCollection.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/UnmodifiableTagCollection.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java
new file mode 100644
index 0000000..4140caf
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/Way.java
@@ -0,0 +1,395 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.domain.v0_6;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+import org.openstreetmap.osmosis.core.util.IntAsChar;
+
+
+/**
+ * A data class representing a single OSM way.
+ * 
+ * @author Brett Henderson
+ */
+public class Way extends Entity implements Comparable<Way> {
+	
+	private List<WayNode> wayNodes;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestamp
+	 *            The last updated timestamp.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @deprecated As of 0.40, replaced by Way(entityData).
+	 */
+	public Way(long id, int version, Date timestamp, OsmUser user, long changesetId) {
+		// Chain to the more specific constructor
+		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestampContainer
+	 *            The container holding the timestamp in an alternative
+	 *            timestamp representation.
+	 * @param user
+	 *            The name of the user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @deprecated As of 0.40, replaced by Way(entityData).
+	 */
+	public Way(long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId) {
+		super(id, version, timestampContainer, user, changesetId);
+		
+		this.wayNodes = new ArrayList<WayNode>();
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param entityData
+	 *            The common entity data.
+	 */
+	public Way(CommonEntityData entityData) {
+		super(entityData);
+		
+		this.wayNodes = new ArrayList<WayNode>();
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestamp
+	 *            The last updated timestamp.
+	 * @param user
+	 *            The user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @param tags
+	 *            The tags to apply to the object.
+	 * @param wayNodes
+	 *            The way nodes to apply to the object
+	 * @deprecated As of 0.40, replaced by Way(entityData, wayNodes).
+	 */
+	public Way(long id, int version, Date timestamp, OsmUser user, long changesetId, Collection<Tag> tags,
+			List<WayNode> wayNodes) {
+		// Chain to the more specific constructor
+		this(id, version, new SimpleTimestampContainer(timestamp), user, changesetId, tags, wayNodes);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param id
+	 *            The unique identifier.
+	 * @param version
+	 *            The version of the entity.
+	 * @param timestampContainer
+	 *            The container holding the timestamp in an alternative
+	 *            timestamp representation.
+	 * @param user
+	 *            The name of the user that last modified this entity.
+	 * @param changesetId
+	 *            The id of the changeset that this version of the entity was created by.
+	 * @param tags
+	 *            The tags to apply to the object.
+	 * @param wayNodes
+	 *            The way nodes to apply to the object
+	 * @deprecated As of 0.40, replaced by Way(entityData, wayNodes).
+	 */
+	public Way(
+			long id, int version, TimestampContainer timestampContainer, OsmUser user, long changesetId,
+			Collection<Tag> tags, List<WayNode> wayNodes) {
+		super(id, version, timestampContainer, user, changesetId, tags);
+		
+		this.wayNodes = new ArrayList<WayNode>(wayNodes);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param entityData
+	 *            The common entity data.
+	 * @param wayNodes
+	 *            The way nodes to apply to the object
+	 */
+	public Way(
+			CommonEntityData entityData, List<WayNode> wayNodes) {
+		super(entityData);
+		
+		this.wayNodes = new ArrayList<WayNode>(wayNodes);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param originalWay
+	 *            The way to clone from.
+	 */
+	private Way(Way originalWay) {
+		super(originalWay);
+		
+		this.wayNodes = new ArrayList<WayNode>(originalWay.wayNodes);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers
+	 *            within the store.
+	 */
+	public Way(StoreReader sr, StoreClassRegister scr) {
+		super(sr, scr);
+		
+		int featureCount;
+		
+		featureCount = sr.readCharacter();
+		
+		wayNodes = new ArrayList<WayNode>();
+		for (int i = 0; i < featureCount; i++) {
+			wayNodes.add(new WayNode(sr, scr));
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void store(StoreWriter sw, StoreClassRegister scr) {
+		super.store(sw, scr);
+		
+		sw.writeCharacter(IntAsChar.intToChar(wayNodes.size()));
+		for (WayNode wayNode : wayNodes) {
+			wayNode.store(sw, scr);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityType getType() {
+		return EntityType.Way;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object o) {
+		if (o instanceof Way) {
+			return compareTo((Way) o) == 0;
+		} else {
+			return false;
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		/*
+		 * As per the hashCode definition, this doesn't have to be unique it
+		 * just has to return the same value for any two objects that compare
+		 * equal. Using both id and version will provide a good distribution of
+		 * values but is simple to calculate.
+		 */
+		return (int) getId() + getVersion();
+	}
+
+
+	/**
+	 * Compares this node list to the specified node list. The comparison is
+	 * based on a direct comparison of the node ids.
+	 * 
+	 * @param comparisonWayNodes
+	 *            The node list to compare to.
+	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
+	 *         "bigger".
+	 */
+	protected int compareWayNodes(List<WayNode> comparisonWayNodes) {
+		Iterator<WayNode> i;
+		Iterator<WayNode> j;
+		
+		// The list with the most entities is considered bigger.
+		if (wayNodes.size() != comparisonWayNodes.size()) {
+			return wayNodes.size() - comparisonWayNodes.size();
+		}
+		
+		// Check the individual way nodes.
+		i = wayNodes.iterator();
+		j = comparisonWayNodes.iterator();
+		while (i.hasNext()) {
+			int result = i.next().compareTo(j.next());
+			
+			if (result != 0) {
+				return result;
+			}
+		}
+		
+		// There are no differences.
+		return 0;
+	}
+
+
+	/**
+	 * Compares this way to the specified way. The way comparison is based on a
+	 * comparison of id, version, timestamp, wayNodeList and tags in that order.
+	 * 
+	 * @param comparisonWay
+	 *            The way to compare to.
+	 * @return 0 if equal, < 0 if considered "smaller", and > 0 if considered
+	 *         "bigger".
+	 */
+	public int compareTo(Way comparisonWay) {
+		int wayNodeListResult;
+		
+		if (this.getId() < comparisonWay.getId()) {
+			return -1;
+		}
+		if (this.getId() > comparisonWay.getId()) {
+			return 1;
+		}
+		
+		if (this.getVersion() < comparisonWay.getVersion()) {
+			return -1;
+		}
+		if (this.getVersion() > comparisonWay.getVersion()) {
+			return 1;
+		}
+		
+		if (this.getTimestamp() == null && comparisonWay.getTimestamp() != null) {
+			return -1;
+		}
+		if (this.getTimestamp() != null && comparisonWay.getTimestamp() == null) {
+			return 1;
+		}
+		if (this.getTimestamp() != null && comparisonWay.getTimestamp() != null) {
+			int result;
+			
+			result = this.getTimestamp().compareTo(comparisonWay.getTimestamp());
+			
+			if (result != 0) {
+				return result;
+			}
+		}
+		
+		wayNodeListResult = compareWayNodes(
+			comparisonWay.getWayNodes()
+		);
+		
+		if (wayNodeListResult != 0) {
+			return wayNodeListResult;
+		}
+		
+		return compareTags(comparisonWay.getTags());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void makeReadOnly() {
+		if (!isReadOnly()) {
+			wayNodes = Collections.unmodifiableList(wayNodes);
+		}
+		
+		super.makeReadOnly();
+	}
+
+
+	/**
+	 * Returns the attached list of way nodes. The returned list is read-only.
+	 * 
+	 * @return The wayNodeList.
+	 */
+	public List<WayNode> getWayNodes() {
+		return wayNodes;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Way getWriteableInstance() {
+		if (isReadOnly()) {
+			return new Way(this);
+		} else {
+			return this;
+		}
+	}
+
+
+    /**
+     * Is this way closed? (A way is closed if the first node id equals the last node id.)
+     *
+     * @return True or false
+     */
+    public boolean isClosed() {
+        return wayNodes.get(0).getNodeId() == wayNodes.get(wayNodes.size() - 1).getNodeId();
+    }
+
+    /** 
+     * ${@inheritDoc}.
+     */
+    @Override
+    public String toString() {
+        String name = null;
+        Collection<Tag> tags = getTags();
+        for (Tag tag : tags) {
+            if (tag.getKey() != null && tag.getKey().equalsIgnoreCase("name")) {
+                name = tag.getValue();
+                break;
+            }
+        }
+        if (name != null) {
+            return "Way(id=" + getId() + ", #tags=" +  getTags().size() + ", name='" + name + "')";
+        }
+        return "Way(id=" + getId() + ", #tags=" +  getTags().size() + ")";
+    }
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayBuilder.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayBuilder.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayBuilder.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayBuilder.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayNode.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayNode.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayNode.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/domain/v0_6/WayNode.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTracker.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTracker.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTracker.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTracker.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTracker.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTracker.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTracker.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTracker.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerSegment.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerSegment.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerSegment.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerSegment.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTracker.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTracker.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTracker.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTracker.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerType.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerType.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerType.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerType.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/filter/common/ListIdTracker.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/ListIdTracker.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/filter/common/ListIdTracker.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/filter/common/ListIdTracker.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Completable.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Completable.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Completable.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Completable.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/CompletableContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/CompletableContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/CompletableContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/CompletableContainer.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java
new file mode 100644
index 0000000..eb9284a
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/Releasable.java
@@ -0,0 +1,25 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.lifecycle;
+
+
+/**
+ * Classes that hold heavyweight resources that can't wait for garbage
+ * collection should implement this interface. It provides a release method that
+ * should be called by all clients when the class is no longer required. This
+ * release method is guaranteed not to throw exceptions and should always be
+ * called within a finally clause.
+ * 
+ * @author Brett Henderson
+ */
+public interface Releasable {
+	/**
+	 * Performs resource cleanup tasks such as closing files, or database
+	 * connections. This must be called after all processing is complete.
+	 * Implementations should support calling release multiple times, however
+	 * this is not mandatory and cannot be relied on by clients. Implementations
+	 * must call release on any nested Releasable objects. It does not throw
+	 * exceptions and should be called within a finally block to ensure it is
+	 * called in exception scenarios.
+	 */
+	void release();
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableContainer.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableContainer.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableContainer.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableContainer.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/lifecycle/ReleasableIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/merge/common/ConflictResolutionMethod.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/merge/common/ConflictResolutionMethod.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/merge/common/ConflictResolutionMethod.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/merge/common/ConflictResolutionMethod.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java
new file mode 100644
index 0000000..bb76386
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReader.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.misc.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+
+
+/**
+ * An OSM data source that produces an empty change stream.
+ * 
+ * @author Brett Henderson
+ */
+public class EmptyChangeReader implements RunnableChangeSource {
+	private ChangeSink changeSink;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		try {
+			changeSink.initialize(Collections.<String, Object>emptyMap());
+			changeSink.complete();
+		} finally {
+			changeSink.release();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReaderFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReaderFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReaderFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyChangeReaderFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java
new file mode 100644
index 0000000..b8b1ebd
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReader.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.misc.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data source that produces an empty entity stream.
+ * 
+ * @author Brett Henderson
+ */
+public class EmptyReader implements RunnableSource {
+	private Sink sink;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		try {
+			sink.initialize(Collections.<String, Object>emptyMap());
+			sink.complete();
+		} finally {
+			sink.release();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReaderFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReaderFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReaderFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/EmptyReaderFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java
new file mode 100644
index 0000000..17ba5f2
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriter.java
@@ -0,0 +1,49 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.misc.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * An OSM change sink that discards all data sent to it. This is primarily
+ * intended for benchmarking purposes.
+ * 
+ * @author Brett Henderson
+ */
+public class NullChangeWriter implements ChangeSink {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void initialize(Map<String, Object> metaTags) {
+		// Nothing to do.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer change) {
+		// Discard the data.
+	}
+
+
+	/**
+	 * Flushes all changes to file.
+	 */
+	public void complete() {
+		// Nothing to do.
+	}
+
+
+	/**
+	 * Cleans up any open file handles.
+	 */
+	public void release() {
+		// Nothing to do.
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullChangeWriterFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java
new file mode 100644
index 0000000..c75505e
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriter.java
@@ -0,0 +1,49 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.misc.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink that discards all data sent to it. This is primarily
+ * intended for benchmarking purposes.
+ * 
+ * @author Brett Henderson
+ */
+public class NullWriter implements Sink {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void initialize(Map<String, Object> metaTags) {
+		// Nothing to do.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Discard the data.
+	}
+
+
+	/**
+	 * Flushes all changes to file.
+	 */
+	public void complete() {
+		// Nothing to do.
+	}
+
+
+	/**
+	 * Cleans up any open file handles.
+	 */
+	public void release() {
+		// Nothing to do.
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/misc/v0_6/NullWriterFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/ActiveTaskManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/ActiveTaskManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/ActiveTaskManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/ActiveTaskManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PassiveTaskManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PassiveTaskManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PassiveTaskManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PassiveTaskManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipeTasks.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipeTasks.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipeTasks.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipeTasks.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/Pipeline.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/Pipeline.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/Pipeline.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/Pipeline.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipelineConstants.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipelineConstants.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipelineConstants.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/PipelineConstants.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/RunnableTaskManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/RunnableTaskManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/RunnableTaskManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/RunnableTaskManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskConfiguration.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskConfiguration.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskConfiguration.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskConfiguration.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactoryRegister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactoryRegister.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactoryRegister.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskManagerFactoryRegister.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskRunner.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskRunner.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskRunner.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/common/TaskRunner.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkChangeSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkMultiChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkMultiChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkMultiChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkMultiChangeSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkRunnableChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkRunnableChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkRunnableChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkRunnableChangeSourceManager.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkSourceManager.java
new file mode 100644
index 0000000..5bc9774
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/ChangeSinkSourceManager.java
@@ -0,0 +1,60 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.pipeline.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.PassiveTaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.PipeTasks;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkSource;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSource;
+
+
+/**
+ * A task manager implementation for task performing change sink and source
+ * functionality.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeSinkSourceManager extends PassiveTaskManager {
+	private ChangeSinkSource task;
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param taskId
+	 *            A unique identifier for the task. This is used to produce
+	 *            meaningful errors when errors occur.
+	 * @param task
+	 *            The task instance to be managed.
+	 * @param pipeArgs
+	 *            The arguments defining input and output pipes for the task,
+	 *            pipes are a logical concept for identifying how the tasks are
+	 *            connected together.
+	 */
+	public ChangeSinkSourceManager(String taskId,
+			ChangeSinkSource task, Map<String, String> pipeArgs) {
+		super(taskId, pipeArgs);
+
+		this.task = task;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void connect(PipeTasks pipeTasks) {
+		ChangeSource source;
+
+		// Get the input task. A sink only has one input, this corresponds to
+		// pipe index 0.
+		source = (ChangeSource) getInputTask(pipeTasks, 0, ChangeSource.class);
+
+		// Cast the input feed to the correct type.
+		// Connect the tasks.
+		source.setChangeSink(task);
+
+		// Register the task as an output. A source only has one output, this
+		// corresponds to pipe index 0.
+		setOutputTask(pipeTasks, task, 0);
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/DatasetSinkSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiChangeSinkRunnableChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiChangeSinkRunnableChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiChangeSinkRunnableChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiChangeSinkRunnableChangeSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkMultiChangeSinkRunnableSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkMultiChangeSinkRunnableSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkMultiChangeSinkRunnableSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkMultiChangeSinkRunnableSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableChangeSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/MultiSinkRunnableSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableChangeSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableChangeSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableChangeSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableChangeSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableDatasetSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableDatasetSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableDatasetSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableDatasetSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/RunnableSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkDatasetSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkDatasetSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkDatasetSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkDatasetSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkMultiSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkMultiSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkMultiSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkMultiSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkRunnableSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkRunnableSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkRunnableSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkRunnableSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkSourceManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkSourceManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkSourceManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/pipeline/v0_6/SinkSourceManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/plugin/CorePlugin.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/plugin/CorePlugin.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/plugin/CorePlugin.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/plugin/CorePlugin.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/plugin/PluginLoader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/plugin/PluginLoader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/plugin/PluginLoader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/plugin/PluginLoader.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java
new file mode 100644
index 0000000..178779f
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLogger.java
@@ -0,0 +1,108 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.progress.v0_6;
+
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.progress.v0_6.impl.ProgressTracker;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * Logs progress information using jdk logging at info level at regular intervals.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeProgressLogger implements ChangeSinkChangeSource {
+	
+	private static final Logger LOG = Logger.getLogger(ChangeProgressLogger.class.getName());
+	
+	private ChangeSink changeSink;
+	private ProgressTracker progressTracker;
+	
+	private String prefix;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param interval
+	 *            The interval between logging progress reports in milliseconds.
+	 * @param label
+	 *            a label to prefix the logger with; may be null.
+	 */
+	public ChangeProgressLogger(int interval, String label) {
+		progressTracker = new ProgressTracker(interval);
+
+		if (label != null && !label.equals("")) {
+			prefix = "[" + label + "] ";
+		} else {
+			prefix = "";
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		progressTracker.initialize();
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		Entity entity;
+		ChangeAction action;
+		
+		entity = changeContainer.getEntityContainer().getEntity();
+		action = changeContainer.getAction();
+		
+		if (progressTracker.updateRequired()) {
+			LOG.info(
+					prefix 
+					+ "Processing " + entity.getType() + " " + entity.getId() + " with action " + action + ", "
+					+ progressTracker.getObjectsPerSecond() + " objects/second.");
+		}
+		
+		changeSink.process(changeContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		LOG.info("Processing completion steps.");
+		
+		long start = System.currentTimeMillis();
+		changeSink.complete();
+		long duration = System.currentTimeMillis() - start;
+		
+		LOG.info("Completion steps took " + duration / 1000d + " seconds.");
+		LOG.info("Processing complete.");
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeSink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java
new file mode 100644
index 0000000..5d4e660
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/ChangeProgressLoggerFactory.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.progress.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkChangeSourceManager;
+
+
+/**
+ * The task manager factory for a change progress logger.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeProgressLoggerFactory extends TaskManagerFactory {
+	private static final String ARG_LOG_INTERVAL = "interval";
+	private static final int DEFAULT_LOG_INTERVAL = 5;
+
+	private static final String ARG_LABEL = "label";
+	private static final String DEFAULT_LABEL = "";
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		ChangeProgressLogger task;
+		int interval;
+		String label;
+		
+		// Get the task arguments.
+		interval = getIntegerArgument(taskConfig, ARG_LOG_INTERVAL, DEFAULT_LOG_INTERVAL);
+		label = getStringArgument(taskConfig, ARG_LABEL, DEFAULT_LABEL);
+
+		// Build the task object.
+		task = new ChangeProgressLogger(interval * 1000, label);
+		
+		return new ChangeSinkChangeSourceManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java
new file mode 100644
index 0000000..b531e14
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLogger.java
@@ -0,0 +1,104 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.progress.v0_6;
+
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.progress.v0_6.impl.ProgressTracker;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Logs progress information using jdk logging at info level at regular intervals.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityProgressLogger implements SinkSource {
+	
+	private static final Logger LOG = Logger.getLogger(EntityProgressLogger.class.getName());
+	
+	private Sink sink;
+	private ProgressTracker progressTracker;
+	
+	private String prefix;
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param interval
+	 *            The interval between logging progress reports in milliseconds.
+	 * @param label
+	 *            a label to prefix the logger with; may be null.
+	 */
+	public EntityProgressLogger(int interval, String label) {
+		progressTracker = new ProgressTracker(interval);
+
+		if (label != null && !label.equals("")) {
+			prefix = "[" + label + "] ";
+		} else {
+			prefix = "";
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		progressTracker.initialize();
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		Entity entity;
+		
+		entity = entityContainer.getEntity();
+		
+		if (progressTracker.updateRequired()) {
+			LOG.info(
+					prefix
+					+ "Processing " + entity.getType() + " " + entity.getId() + ", "
+					+ progressTracker.getObjectsPerSecond() + " objects/second.");
+		}
+		
+		sink.process(entityContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		LOG.info("Processing completion steps.");
+		
+		long start = System.currentTimeMillis();
+		sink.complete();
+		long duration = System.currentTimeMillis() - start;
+		
+		LOG.info("Completion steps took " + duration / 1000d + " seconds.");
+		LOG.info("Processing complete.");
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java
new file mode 100644
index 0000000..2188513
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/EntityProgressLoggerFactory.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.progress.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+
+/**
+ * The task manager factory for an entity progress logger.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityProgressLoggerFactory extends TaskManagerFactory {
+	private static final String ARG_LOG_INTERVAL = "interval";
+	private static final int DEFAULT_LOG_INTERVAL = 5;
+	
+	private static final String ARG_LABEL = "label";
+	private static final String DEFAULT_LABEL = "";
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		EntityProgressLogger task;
+		int interval;
+		String label;
+		
+		// Get the task arguments.
+		interval = getIntegerArgument(taskConfig, ARG_LOG_INTERVAL, DEFAULT_LOG_INTERVAL);
+		label = getStringArgument(taskConfig, ARG_LABEL, DEFAULT_LABEL);
+		
+		// Build the task object.
+		task = new EntityProgressLogger(interval * 1000, label);
+		
+		return new SinkSourceManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java
new file mode 100644
index 0000000..a6addf3
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/progress/v0_6/impl/ProgressTracker.java
@@ -0,0 +1,95 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.progress.v0_6.impl;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+/**
+ * Maintains state about execution progress. It calculates when the next update
+ * is due, and provides statistics on execution.
+ * 
+ * @author Brett Henderson
+ */
+public class ProgressTracker {
+	
+	private int interval;
+	private boolean initialized;
+	private long lastUpdateTimestamp;
+	private long objectCount;
+	private double objectsPerSecond;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param interval
+	 *            The interval between logging progress reports in milliseconds.
+	 */
+	public ProgressTracker(int interval) {
+		this.interval = interval;
+		
+		initialized = false;
+	}
+	
+	
+	/**
+	 * Initializes interval measurement from now.
+	 */
+	public void initialize() {
+		lastUpdateTimestamp = System.currentTimeMillis();
+		objectCount = 0;
+		objectsPerSecond = 0;
+		
+		initialized = true;
+	}
+	
+	
+	/**
+	 * Indicates if an update is due. This should be called once per object that
+	 * is processed.
+	 * 
+	 * @return True if an update is due.
+	 */
+	public boolean updateRequired() {
+		long currentTimestamp;
+		long duration;
+		
+		if (!initialized) {
+			throw new OsmosisRuntimeException("initialize has not been called");
+			
+		}
+		
+		// Calculate the time since the last update.
+		currentTimestamp = System.currentTimeMillis();
+		duration = currentTimestamp - lastUpdateTimestamp;
+		
+		// Increment the processed object count.
+		objectCount++;
+		
+		if (duration > interval || duration < 0) {
+			lastUpdateTimestamp = currentTimestamp;
+			
+			// Calculate the number of objects processed per second.
+			objectsPerSecond = (double) objectCount * 1000 / duration;
+			
+			// Reset the object count.
+			objectCount = 0;
+			
+			return true;
+			
+		} else {
+			return false;
+		}
+	}
+	
+	
+	/**
+	 * Provides the number of objects processed per second. This only becomes
+	 * valid after updateRequired returns true for the first time.
+	 * 
+	 * @return The number of objects processed per second in the last timing
+	 *         interval.
+	 */
+	public double getObjectsPerSecond() {
+		return objectsPerSecond;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java
new file mode 100644
index 0000000..a764dd4
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporter.java
@@ -0,0 +1,324 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.report.v0_6;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink that analyses the data sent to it and provides a simple
+ * report.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityReporter implements Sink {
+	
+	private static final int COLUMN_WIDTH_USER_NAME = 50;
+	private static final int COLUMN_WIDTH_NODE_COUNT = 7;
+	private static final int COLUMN_WIDTH_WAY_COUNT = 7;
+	private static final int COLUMN_WIDTH_RELATION_COUNT = 7;
+	
+	private Logger log = Logger.getLogger(EntityReporter.class.getName());
+	
+	private File file;
+	private FileWriter fileWriter;
+	private Map<String, UserStatistics> userMap;
+	private UserStatistics anonymousUser;
+	private UserStatistics totalUser;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 */
+	public EntityReporter(File file) {
+		this.file = file;
+		
+		userMap = new HashMap<String, UserStatistics>();
+		anonymousUser = new UserStatistics("anonymous");
+		totalUser = new UserStatistics("Total");
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		String userName;
+		final UserStatistics user;
+		
+		// Obtain the user statistics object.
+		userName = entityContainer.getEntity().getUser().getName();
+		if (userName != null && userName.length() > 0) {
+			if (userMap.containsKey(userName)) {
+				user = userMap.get(userName);
+			} else {
+				user = new UserStatistics(userName);
+				userMap.put(userName, user);
+			}
+		} else {
+			user = anonymousUser;
+		}
+		
+		// Increment the relevant user statistic.
+		entityContainer.process(
+			new EntityProcessor() {
+				private UserStatistics processorUser = user;
+				
+				public void process(BoundContainer bound) {
+					// Do nothing.
+				}
+				
+				public void process(NodeContainer node) {
+					processorUser.incrementNodeCount();
+					totalUser.incrementNodeCount();
+				}
+				
+				public void process(WayContainer way) {
+					processorUser.incrementWayCount();
+					totalUser.incrementWayCount();
+				}
+				
+				public void process(RelationContainer relation) {
+					processorUser.incrementRelationCount();
+					totalUser.incrementRelationCount();
+				}
+			}
+		);
+	}
+	
+	
+	/**
+	 * Writes a single value and pads it out to the correct column width.
+	 * 
+	 * @param writer
+	 *            The report destination.
+	 * @param data
+	 *            The data to be written.
+	 * @param columnWidth
+	 *            The width of the column.
+	 */
+	private void writeColumnValue(BufferedWriter writer, String data, int columnWidth) throws IOException {
+		int padLength;
+		
+		// Calculate the required data padding. The total column width is the
+		// specified column width plus one space.
+		padLength = columnWidth - data.length() + 1;
+		if (padLength < 1) {
+			padLength = 1;
+		}
+		
+		writer.write(data);
+		for (int i = 0; i < padLength; i++) {
+			writer.write(' ');
+		}
+	}
+	
+	
+	/**
+	 * Writes a single line summary of the user statistics.
+	 * 
+	 * @param writer
+	 *            The report destination.
+	 * @param userStatistics
+	 *            The user to report on.
+	 */
+	private void writeUserLine(BufferedWriter writer, UserStatistics userStatistics) throws IOException {
+		writeColumnValue(writer, userStatistics.getUserName(), COLUMN_WIDTH_USER_NAME);
+		writeColumnValue(writer, Integer.toString(userStatistics.getNodeCount()), COLUMN_WIDTH_NODE_COUNT);
+		writeColumnValue(writer, Integer.toString(userStatistics.getWayCount()), COLUMN_WIDTH_WAY_COUNT);
+		writeColumnValue(writer, Integer.toString(userStatistics.getRelationCount()), COLUMN_WIDTH_RELATION_COUNT);
+		writer.newLine();
+	}
+	
+	
+	/**
+	 * Add user information to the report file.
+	 * 
+	 * @param writer
+	 *            The report destination.
+	 */
+	private void writeUserReport(BufferedWriter writer) throws IOException {
+		List<UserStatistics> userList;
+		
+		// Sort the user statistics by user id.
+		userList = new ArrayList<UserStatistics>(userMap.values());
+		Collections.sort(
+			userList,
+			new Comparator<UserStatistics>() {
+				public int compare(UserStatistics o1, UserStatistics o2) {
+					return o1.getUserName().compareTo(o2.getUserName());
+				}
+			}
+		);
+		
+		writer.write("********** User Report **********");
+		writer.newLine();
+		writeColumnValue(writer, "USER NAME", COLUMN_WIDTH_USER_NAME);
+		writeColumnValue(writer, "NODES", COLUMN_WIDTH_NODE_COUNT);
+		writeColumnValue(writer, "WAYS", COLUMN_WIDTH_WAY_COUNT);
+		writeColumnValue(writer, "RELNS", COLUMN_WIDTH_RELATION_COUNT);
+		writer.newLine();
+		writeUserLine(writer, anonymousUser);
+		for (UserStatistics userStatistics : userList) {
+			writeUserLine(writer, userStatistics);
+		}
+		writer.newLine();
+		writeUserLine(writer, totalUser);
+	}
+	
+	
+	/**
+	 * Flushes all changes to file.
+	 */
+	public void complete() {
+		try {
+			BufferedWriter writer;
+			
+			fileWriter = new FileWriter(file);
+			writer = new BufferedWriter(fileWriter);
+			
+			// Produce a report on the user statistics.
+			writeUserReport(writer);
+			
+			writer.close();
+			fileWriter = null;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write report to file " + file + ".");
+		}
+	}
+	
+	
+	/**
+	 * Cleans up any open file handles.
+	 */
+	public void release() {
+		if (fileWriter != null) {
+			try {
+				fileWriter.close();
+			} catch (IOException e) {
+				log.log(Level.SEVERE, "Unable to close file writer for file " + file + ".", e);
+			} finally {
+				fileWriter = null;
+			}
+		}
+	}
+	
+	
+	/**
+	 * A class holding the summary information for a single user.
+	 */
+	private static class UserStatistics {
+		
+		private String userName;
+		private int nodeCount;
+		private int wayCount;
+		private int relationCount;
+		
+		
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param userName
+		 *            The name of the user that this statistics record relates
+		 *            to.
+		 */
+		public UserStatistics(String userName) {
+			this.userName = userName;
+		}
+		
+		
+		/**
+		 * Increments the node count by one.
+		 */
+		public void incrementNodeCount() {
+			nodeCount++;
+		}
+		
+		
+		/**
+		 * Increments the way count by one.
+		 */
+		public void incrementWayCount() {
+			wayCount++;
+		}
+		
+		
+		/**
+		 * Increments the relation count by one.
+		 */
+		public void incrementRelationCount() {
+			relationCount++;
+		}
+		
+		
+		/**
+		 * Returns the name of the user for which this object contains data.
+		 * 
+		 * @return The user name.
+		 */
+		public String getUserName() {
+			return userName;
+		}
+		
+		
+		/**
+		 * Returns the node count.
+		 * 
+		 * @return The node count.
+		 */
+		public int getNodeCount() {
+			return nodeCount;
+		}
+		
+		
+		/**
+		 * Returns the way count.
+		 * 
+		 * @return The way count.
+		 */
+		public int getWayCount() {
+			return wayCount;
+		}
+		
+		
+		/**
+		 * Returns the relation count.
+		 * 
+		 * @return The relation count.
+		 */
+		public int getRelationCount() {
+			return relationCount;
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/EntityReporterFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java
new file mode 100644
index 0000000..6f932b3
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporter.java
@@ -0,0 +1,250 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.report.v0_6;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.filter.common.BitSetIdTracker;
+import org.openstreetmap.osmosis.core.filter.common.IdTracker;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * A sink that verifies the referential integrity of all data passing through
+ * it.
+ * 
+ * @author Brett Henderson
+ */
+public class IntegrityReporter implements Sink, EntityProcessor {
+	
+	private static final Logger LOG = Logger.getLogger(IntegrityReporter.class.getName());
+	
+	private File file;
+	private boolean initialized;
+	private BufferedWriter writer;
+	private IdTracker nodeBitSet;
+	private IdTracker wayBitSet;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 */
+	public IntegrityReporter(File file) {
+		this.file = file;
+		
+		initialized = false;
+		nodeBitSet = new BitSetIdTracker();
+		wayBitSet = new BitSetIdTracker();
+	}
+	
+	
+	/**
+	 * Writes data to the output file.
+	 * 
+	 * @param data
+	 *            The data to be written.
+	 */
+	private void write(String data) {
+		try {
+			writer.write(data);
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write data.", e);
+		}
+	}
+	
+	
+	/**
+	 * Writes a new line in the output file.
+	 */
+	private void writeNewLine() {
+		try {
+			writer.newLine();
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write data.", e);
+		}
+	}
+	
+	
+	/**
+	 * Initialises the output file for writing. This must be called by
+	 * sub-classes before any writing is performed. This method may be called
+	 * multiple times without adverse affect allowing sub-classes to invoke it
+	 * every time they perform processing.
+	 */
+	protected void initialize() {
+		if (!initialized) {
+			OutputStream outStream = null;
+			
+			try {
+				outStream = new FileOutputStream(file);
+				
+				writer = new BufferedWriter(new OutputStreamWriter(outStream, "UTF-8"));
+				
+				outStream = null;
+				
+			} catch (IOException e) {
+				throw new OsmosisRuntimeException("Unable to open file " + file + " for writing.", e);
+			} finally {
+				if (outStream != null) {
+					try {
+						outStream.close();
+					} catch (Exception e) {
+						LOG.log(Level.SEVERE, "Unable to close output stream for file " + file + ".", e);
+					}
+					outStream = null;
+				}
+			}
+			
+			initialized = true;
+			
+			write("Entity Type, Entity Id, Referred Type, Referred Id");
+			writeNewLine();
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+    public void process(BoundContainer bound) {
+	    // Do nothing.
+    }
+
+
+    /**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer node) {
+		nodeBitSet.set(node.getEntity().getId());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		Way way;
+		
+		way = wayContainer.getEntity();
+		
+		wayBitSet.set(way.getId());
+		
+		for (WayNode wayNode : way.getWayNodes()) {
+			if (!nodeBitSet.get(wayNode.getNodeId())) {
+				initialize();
+				
+				write("Way," + way.getId() + ",Node," + wayNode.getNodeId());
+				writeNewLine();
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		Relation relation;
+		
+		relation = relationContainer.getEntity();
+		
+		for (RelationMember relationMember : relation.getMembers()) {
+			EntityType memberType;
+			
+			memberType = relationMember.getMemberType();
+			
+			if (EntityType.Node.equals(memberType)) {
+				if (!nodeBitSet.get(relationMember.getMemberId())) {
+					initialize();
+					
+					write("Relation," + relation.getId() + ",Node," + relationMember.getMemberId());
+					writeNewLine();
+				}
+			} else if (EntityType.Way.equals(memberType)) {
+				if (!wayBitSet.get(relationMember.getMemberId())) {
+					initialize();
+					
+					write("Relation," + relation.getId() + ",Way," + relationMember.getMemberId());
+					writeNewLine();
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes all changes to file.
+	 */
+	public void complete() {
+		try {
+			if (writer != null) {
+				writer.close();
+			}
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to complete writing to the file " + file + ".", e);
+		} finally {
+			initialized = false;
+			writer = null;
+		}
+	}
+	
+	
+	/**
+	 * Cleans up any open file handles.
+	 */
+	public void release() {
+		try {
+			try {
+				if (writer != null) {
+					writer.close();
+				}
+			} catch (IOException e) {
+				LOG.log(Level.SEVERE, "Unable to close writer.", e);
+			}
+		} finally {
+			initialized = false;
+			writer = null;
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/report/v0_6/IntegrityReporterFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java
new file mode 100644
index 0000000..0a40e0a
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSort.java
@@ -0,0 +1,291 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.common;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.lifecycle.Releasable;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.ChunkedObjectStore;
+import org.openstreetmap.osmosis.core.store.ObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.PersistentIterator;
+import org.openstreetmap.osmosis.core.store.Storeable;
+
+
+/**
+ * Allows a large number of objects to be sorted by writing them all to disk
+ * then sorting using a merge sort algorithm.
+ * 
+ * @param <T>
+ *            The object type to be sorted.
+ * @author Brett Henderson
+ */
+public class FileBasedSort<T extends Storeable> implements Releasable {
+	/**
+	 * The maximum number of entities to perform memory-based sorting on,
+	 * amounts larger than this will be split into chunks of this size, the
+	 * chunks sorted in memory before writing to file, and all the results
+	 * merged using the merge sort algorithm.
+	 */
+	private static final int MAX_MEMORY_SORT_COUNT = 16384;
+	
+	/**
+	 * The maximum number of sources to merge together at a single level of the
+	 * merge sort hierarchy. Must be 2 or higher. A standard merge sort is 2.
+	 */
+	private static final int MAX_MERGE_SOURCE_COUNT = 2;
+	
+	
+	/**
+	 * The number of levels in the merge sort hierarchy to perform in memory
+	 * before persisting to a file. By persisting to file at regular hierarchy
+	 * levels, the number of file handles is minimised. File handle count is
+	 * likely to be an issue before memory usage due to the small number of
+	 * in-flight objects at any point in time.
+	 * <p>
+	 * The number of file handles will be MAX_MERGE_SOURCE_COUNT raised to the
+	 * power of MAX_MEMORY_SORT_DEPTH.
+	 */
+	private static final int MAX_MEMORY_SORT_DEPTH = 8;
+	
+
+	private ObjectSerializationFactory serializationFactory;
+	private Comparator<T> comparator;
+	private ChunkedObjectStore<T> chunkedEntityStore;
+	private List<T> addBuffer;
+	private boolean useCompression;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param serializationFactory
+	 *            The factory defining the object serialisation implementation.
+	 * @param comparator
+	 *            The comparator to be used for sorting the results.
+	 * @param useCompression
+	 *            If true, the storage files will be compressed.
+	 */
+	public FileBasedSort(
+			ObjectSerializationFactory serializationFactory, Comparator<T> comparator, boolean useCompression) {
+		this.serializationFactory = serializationFactory;
+		this.comparator = comparator;
+		this.useCompression = useCompression;
+		
+		chunkedEntityStore = new ChunkedObjectStore<T>(serializationFactory, "emta", "idx", useCompression);
+		addBuffer = new ArrayList<T>(MAX_MEMORY_SORT_COUNT);
+	}
+	
+	
+	/**
+	 * Sorts the data currently in the add buffer, writes it to the object
+	 * store, and clears the buffer.
+	 */
+	private void flushAddBuffer() {
+		if (addBuffer.size() >= 0) {
+			// Sort the chunk prior to writing.
+			Collections.sort(addBuffer, comparator);
+			
+			// Write all entities in the buffer to entity storage.
+			for (T entity : addBuffer) {
+				chunkedEntityStore.add(entity);
+			}
+			
+			addBuffer.clear();
+			
+			// Close the chunk in the underlying data store so that it can be
+			// read separately.
+			chunkedEntityStore.closeChunk();
+		}
+	}
+	
+	
+	/**
+	 * Adds a new object to be sorted.
+	 * 
+	 * @param value
+	 *            The data object.
+	 */
+	public void add(T value) {
+		// Add the new data entity to the add buffer.
+		addBuffer.add(value);
+		
+		// If the add buffer is full, it must be sorted and written to entity
+		// storage.
+		if (addBuffer.size() >= MAX_MEMORY_SORT_COUNT) {
+			flushAddBuffer();
+		}
+	}
+	
+	
+	/**
+	 * This is a wrapper method around the iterate method with the same argument
+	 * list that persists the sort results prior to returning. This forces all
+	 * sorting by nested recursive method calls to be performed allowing all
+	 * associated memory can be freed.
+	 * 
+	 * @param nestLevel
+	 *            The current recursive nesting level of the merge sort
+	 *            operation.
+	 * @param beginChunkIndex
+	 *            The initial chunk to begin sorting from.
+	 * @param chunkCount
+	 *            The number of chunks to sort.
+	 * @return An iterator providing access to the sort result.
+	 */
+	private ReleasableIterator<T> iteratePersisted(int nestLevel, long beginChunkIndex, long chunkCount) {
+		ReleasableIterator<T> persistentIterator;
+		
+		// Create a persistent iterator based on the requested underlying chunk
+		// iterator.
+		persistentIterator = new PersistentIterator<T>(
+			serializationFactory,
+			iterate(nestLevel, beginChunkIndex, chunkCount),
+			"emtb",
+			useCompression
+		);
+		
+		// Prime the persistent iterator so that all underlying iterator data is
+		// written to file.
+		try {
+			ReleasableIterator<T> result;
+			
+			result = persistentIterator;
+			
+			// This will cause all data to be read from the underlying iterator
+			// into the persistent store.
+			persistentIterator.hasNext();
+			
+			persistentIterator = null;
+			
+			return result;
+			
+		} finally {
+			// This will release the persistent iterator and its underlying
+			// source iterator if the persistence operations failed.
+			if (persistentIterator != null) {
+				persistentIterator.release();
+			}
+		}
+	}
+	
+	
+	/**
+	 * Sorts the specified sub-section of the overall storage contents. This
+	 * result list is not backed by a file and should be persisted prior to
+	 * being incorporated into a higher level merge operation.
+	 * 
+	 * @param nestLevel
+	 *            The current recursive nesting level of the merge sort
+	 *            operation.
+	 * @param beginChunkIndex
+	 *            The initial chunk to begin sorting from.
+	 * @param chunkCount
+	 *            The number of chunks to sort.
+	 * @return An iterator providing access to the sort result.
+	 */
+	private ReleasableIterator<T> iterate(int nestLevel, long beginChunkIndex, long chunkCount) {
+		List<ReleasableIterator<T>> sources;
+		
+		sources = new ArrayList<ReleasableIterator<T>>();
+		
+		try {
+			MergingIterator<T> mergingIterator;
+			
+			// If we are down to a small number of entities, we retrieve each source from file.
+			// Otherwise we recurse and split the number of entities down into smaller chunks.
+			if (chunkCount <= MAX_MERGE_SOURCE_COUNT) {
+				for (int i = 0; i < chunkCount; i++) {
+					sources.add(
+						chunkedEntityStore.iterate(beginChunkIndex + i)
+					);
+				}
+				
+			} else {
+				long maxChunkIndex;
+				long subChunkCount;
+				
+				/*
+				 * The current chunk count must be divided by
+				 * MAX_MERGE_SOURCE_COUNT and we must recurse for each of those
+				 * sub chunk counts. Where the result isn't exact, we round up
+				 * to the nearest multiple of MAX_MERGE_SOURCE_COUNT to ensure
+				 * we don't end up with more than MAX_MERGE_SOURCE_COUNT
+				 * sources.
+				 */
+				subChunkCount = chunkCount / MAX_MERGE_SOURCE_COUNT;
+				subChunkCount += chunkCount % MAX_MERGE_SOURCE_COUNT;
+				
+				// We can never pass beyond the chunk boundaries specified for
+				// this function.
+				maxChunkIndex = beginChunkIndex + chunkCount;
+				
+				for (
+						long subFirstChunk = beginChunkIndex;
+						subFirstChunk < maxChunkIndex;
+						subFirstChunk += subChunkCount) {
+					
+					// The chunk count passed to the nested function should not
+					// make the nested function exceed this function's boundaries.
+					if (subFirstChunk + subChunkCount > maxChunkIndex) {
+						subChunkCount = maxChunkIndex - subFirstChunk;
+					}
+					
+					/*
+					 * Either call the persistent or standard version of the
+					 * recursive iterate based on whether this nesting level
+					 * requires persistence. If we only have one chunk left at
+					 * this point we make an exception and skip persistence
+					 * because it will only result in a single file being opened
+					 * anyway.
+					 */
+					if (((nestLevel + 1) % MAX_MEMORY_SORT_DEPTH) == 0 && subChunkCount > 1) {
+						sources.add(
+							iteratePersisted(nestLevel + 1, subFirstChunk, subChunkCount)
+						);
+					} else {
+						sources.add(
+							iterate(nestLevel + 1, subFirstChunk, subChunkCount)
+						);
+					}
+				}
+			}
+			
+			// Create a merging iterator to merge all of the sources.
+			mergingIterator = new MergingIterator<T>(sources, comparator);
+			
+			// The merging iterator owns the sources now, so we clear our copy
+			// of them to prevent them being released on method exit.
+			sources.clear();
+			
+			return mergingIterator;
+			
+		} finally {
+			for (ReleasableIterator<T> source : sources) {
+				source.release();
+			}
+		}
+	}
+	
+	
+	/**
+	 * Sorts and returns the contents of the sorter.
+	 * 
+	 * @return An iterator providing access to the sorted entities.
+	 */
+	public ReleasableIterator<T> iterate() {
+		flushAddBuffer();
+		
+		return iterate(0, 0, chunkedEntityStore.getChunkCount());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		chunkedEntityStore.release();
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java
new file mode 100644
index 0000000..747b8cb
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/common/MergingIterator.java
@@ -0,0 +1,131 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.common;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+
+
+/**
+ * This iterator examines a list of sorted input sources and merges them into a
+ * single sorted list.
+ * 
+ * @param <DataType>
+ *            The object type to be sorted.
+ * @author Brett Henderson
+ */
+public class MergingIterator<DataType> implements ReleasableIterator<DataType> {
+	private List<ReleasableIterator<DataType>> sources;
+	private Comparator<DataType> comparator;
+	private List<DataType> sourceData;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sources
+	 *            The list of data sources.
+	 * @param comparator
+	 *            The comparator to be used for sorting.
+	 */
+	public MergingIterator(List<ReleasableIterator<DataType>> sources, Comparator<DataType> comparator) {
+		this.sources = new ArrayList<ReleasableIterator<DataType>>(sources);
+		this.comparator = comparator;
+	}
+	
+	
+	/**
+	 * Primes the sorting collections.
+	 */
+	private void initialize() {
+		if (sourceData == null) {
+			// Get the first entity from each source.  Delete any empty sources.
+			sourceData = new ArrayList<DataType>(sources.size());
+			for (int sourceIndex = 0; sourceIndex < sources.size();) {
+				ReleasableIterator<DataType> source;
+				
+				source = sources.get(sourceIndex);
+				
+				if (source.hasNext()) {
+					sourceData.add(source.next());
+					sourceIndex++;
+				} else {
+					sources.remove(sourceIndex).release();
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean hasNext() {
+		initialize();
+		
+		return sourceData.size() > 0;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public DataType next() {
+		DataType dataMinimum;
+		int indexMinimum;
+		ReleasableIterator<DataType> source;
+		
+		initialize();
+		
+		if (!hasNext()) {
+			throw new NoSuchElementException();
+		}
+		
+		dataMinimum = sourceData.get(0);
+		indexMinimum = 0;
+		
+		// Find the minimum entity.
+		for (int indexCurrent = 1; indexCurrent < sources.size(); indexCurrent++) {
+			DataType dataCurrent = sourceData.get(indexCurrent);
+			
+			// Check if the current data entity is less than the existing minimum.
+			if (comparator.compare(dataMinimum, dataCurrent) > 0) {
+				dataMinimum = dataCurrent;
+				indexMinimum = indexCurrent;
+			}
+		}
+		
+		// Get the next entity from the source if available.
+		// Otherwise remove the source and its current data.
+		source = sources.get(indexMinimum);
+		if (source.hasNext()) {
+			sourceData.set(indexMinimum, source.next());
+		} else {
+			sources.remove(indexMinimum).release();
+			sourceData.remove(indexMinimum);
+		}
+		
+		return dataMinimum;
+	}
+	
+	
+	/**
+	 * Not supported. An UnsupportedOperationException is always thrown.
+	 */
+	public void remove() {
+		throw new UnsupportedOperationException();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		for (ReleasableIterator<DataType> source : sources) {
+			source.release();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeAsEntityComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeAsEntityComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeAsEntityComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeAsEntityComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForSeekableApplierComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForSeekableApplierComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForSeekableApplierComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForSeekableApplierComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForStreamableApplierComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForStreamableApplierComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForStreamableApplierComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeForStreamableApplierComparator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java
new file mode 100644
index 0000000..3e4c8a5
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorter.java
@@ -0,0 +1,92 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * A change stream filter that sorts changes. The sort order is specified by
+ * comparator provided during instantiation.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeSorter implements ChangeSinkChangeSource {
+	private FileBasedSort<ChangeContainer> fileBasedSort;
+	private ChangeSink changeSink;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param comparator
+	 *            The comparator to use for sorting.
+	 */
+	public ChangeSorter(Comparator<ChangeContainer> comparator) {
+		fileBasedSort =
+			new FileBasedSort<ChangeContainer>(
+					new SingleClassObjectSerializationFactory(ChangeContainer.class), comparator, true);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer change) {
+		fileBasedSort.add(change);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		ReleasableIterator<ChangeContainer> iterator = null;
+		
+		try {
+			iterator = fileBasedSort.iterate();
+			
+			while (iterator.hasNext()) {
+				changeSink.process(iterator.next());
+			}
+			
+			changeSink.complete();
+		} finally {
+			if (iterator != null) {
+				iterator.release();
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		fileBasedSort.release();
+		changeSink.release();
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeSorterFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java
new file mode 100644
index 0000000..5006ed3
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorter.java
@@ -0,0 +1,96 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * A data stream filter that sorts tags on changes. This is useful for testing
+ * to allow two sets of data to be compared for equality.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeTagSorter implements ChangeSinkChangeSource {
+	private ChangeSink changeSink;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		EntityContainer readOnlyContainer;
+		EntityContainer writeableContainer;
+		Entity entity;
+		Collection<Tag> sortedTags;
+		
+		readOnlyContainer = changeContainer.getEntityContainer();
+		writeableContainer = readOnlyContainer.getWriteableInstance();
+		
+		entity = writeableContainer.getEntity();
+		sortedTags = sortTags(entity.getTags());
+		entity.getTags().clear();
+		entity.getTags().addAll(sortedTags);
+		
+		changeSink.process(new ChangeContainer(writeableContainer, changeContainer.getAction()));
+	}
+
+
+	/**
+	 * Sorts the specified tag list.
+	 * 
+	 * @param tagList
+	 *            The tag list to be sorted.
+	 * @return A new list containing the sorted tags.
+	 */
+	private List<Tag> sortTags(Collection<Tag> tagList) {
+		List<Tag> sortedTagList;
+		
+		sortedTagList = new ArrayList<Tag>(tagList);
+		Collections.sort(sortedTagList);
+		
+		return sortedTagList;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		changeSink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeSink.release();
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/ChangeTagSorterFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByIdComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByIdComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByIdComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByIdComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdThenVersionComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdThenVersionComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdThenVersionComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByTypeThenIdThenVersionComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByVersionComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByVersionComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByVersionComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityByVersionComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityContainerComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityContainerComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityContainerComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntityContainerComparator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java
new file mode 100644
index 0000000..f56f794
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorter.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
+import org.openstreetmap.osmosis.core.store.GenericObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * A data stream filter that sorts entities. The sort order is specified by
+ * comparator provided during instantiation.
+ * 
+ * @author Brett Henderson
+ */
+public class EntitySorter implements SinkSource {
+	private FileBasedSort<EntityContainer> fileBasedSort;
+	private Sink sink;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param comparator
+	 *            The comparator to use for sorting.
+	 */
+	public EntitySorter(Comparator<EntityContainer> comparator) {
+		fileBasedSort = new FileBasedSort<EntityContainer>(new GenericObjectSerializationFactory(), comparator, true);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		fileBasedSort.add(entityContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		ReleasableIterator<EntityContainer> iterator = null;
+		
+		try {
+			iterator = fileBasedSort.iterate();
+			
+			while (iterator.hasNext()) {
+				sink.process(iterator.next());
+			}
+			
+			sink.complete();
+		} finally {
+			if (iterator != null) {
+				iterator.release();
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		fileBasedSort.release();
+		sink.release();
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySorterFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySubClassComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySubClassComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySubClassComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/EntitySubClassComparator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java
new file mode 100644
index 0000000..c809a8c
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDeltaChangePipeValidator.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * Validates that change data in a pipeline is sorted by entity type then id thus only allowing
+ * delta style changes (ie. not full history). It accepts input data from a Source and passes all
+ * data to a downstream Sink.
+ * 
+ * @author Brett Henderson
+ */
+public class SortedDeltaChangePipeValidator implements ChangeSinkChangeSource {
+	private ChangeSink changeSink;
+	private Comparator<ChangeContainer> comparator;
+	private ChangeContainer previousChangeContainer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SortedDeltaChangePipeValidator() {
+		comparator = new ChangeAsEntityComparator(new EntityContainerComparator(new EntityByTypeThenIdComparator()));
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		changeSink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		// If this is not the first entity in the pipeline, make sure this
+		// entity is greater than the previous.
+		if (previousChangeContainer != null) {
+			if (comparator.compare(previousChangeContainer, changeContainer) >= 0) {
+				throw new OsmosisRuntimeException(
+					"Pipeline entities are not sorted or contain multiple versions of a single entity"
+					+ ", previous entity type=" + previousChangeContainer.getEntityContainer().getEntity().getType()
+					+ ", id=" + previousChangeContainer.getEntityContainer().getEntity().getId()
+					+ ", version=" + previousChangeContainer.getEntityContainer().getEntity().getVersion()
+					+ " current entity type=" + changeContainer.getEntityContainer().getEntity().getType()
+					+ ", id=" + changeContainer.getEntityContainer().getEntity().getId() 
+					+ ", version=" + changeContainer.getEntityContainer().getEntity().getVersion() + "."
+				);
+			}
+		}
+		
+		changeSink.process(changeContainer);
+		
+		previousChangeContainer = changeContainer;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeSink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java
new file mode 100644
index 0000000..fade67f
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedDuplicateEntityPipeValidator.java
@@ -0,0 +1,89 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Validates that entity data in a pipeline is sorted by entity type then id. It
+ * allows duplicates. It accepts input data from a Source and passes all data to
+ * a downstream Sink.
+ * 
+ * @author Brett Henderson
+ */
+public class SortedDuplicateEntityPipeValidator implements SinkSource {
+	private Sink sink;
+	private EntityContainerComparator comparator;
+	private EntityContainer previousEntityContainer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SortedDuplicateEntityPipeValidator() {
+		comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// If this is not the first entity in the pipeline, make sure this
+		// entity is greater than the previous.
+		if (previousEntityContainer != null) {
+			if (comparator.compare(previousEntityContainer,	entityContainer) > 0) {
+				throw new OsmosisRuntimeException(
+					"Pipeline entities are not sorted, previous entity type="
+					+ previousEntityContainer.getEntity().getType() + ", id="
+					+ previousEntityContainer.getEntity().getId() + ", version="
+					+ previousEntityContainer.getEntity().getVersion() + " current entity type="
+					+ entityContainer.getEntity().getType() + ", id=" 
+                    + entityContainer.getEntity().getId() + ", version="
+                    + entityContainer.getEntity().getVersion() + "."
+				);
+			}
+		}
+		
+		sink.process(entityContainer);
+		
+		previousEntityContainer = entityContainer;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java
new file mode 100644
index 0000000..6e5b8d7
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedEntityPipeValidator.java
@@ -0,0 +1,88 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Validates that entity data in a pipeline is sorted by entity type then id. It
+ * accepts input data from a Source and passes all data to a downstream Sink.
+ * 
+ * @author Brett Henderson
+ */
+public class SortedEntityPipeValidator implements SinkSource {
+	private Sink sink;
+	private EntityContainerComparator comparator;
+	private EntityContainer previousEntityContainer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SortedEntityPipeValidator() {
+		comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// If this is not the first entity in the pipeline, make sure this
+		// entity is greater than the previous.
+		if (previousEntityContainer != null) {
+			if (comparator.compare(previousEntityContainer,	entityContainer) >= 0) {
+				throw new OsmosisRuntimeException(
+					"Pipeline entities are not sorted, previous entity type="
+					+ previousEntityContainer.getEntity().getType() + ", id="
+					+ previousEntityContainer.getEntity().getId() + ", version="
+					+ previousEntityContainer.getEntity().getVersion() + " current entity type="
+					+ entityContainer.getEntity().getType() + ", id=" 
+                    + entityContainer.getEntity().getId() + ", version="
+                    + entityContainer.getEntity().getVersion() + "."
+				);
+			}
+		}
+		
+		sink.process(entityContainer);
+		
+		previousEntityContainer = entityContainer;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java
new file mode 100644
index 0000000..98b673b
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/SortedHistoryChangePipeValidator.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.Comparator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * Validates that change data in a pipeline is sorted by entity type, then id, then version allowing
+ * full history changes. It accepts input data from a Source and passes all data to a downstream
+ * Sink.
+ * 
+ * @author Brett Henderson
+ */
+public class SortedHistoryChangePipeValidator implements ChangeSinkChangeSource {
+	private ChangeSink changeSink;
+	private Comparator<ChangeContainer> comparator;
+	private ChangeContainer previousChangeContainer;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SortedHistoryChangePipeValidator() {
+		comparator = new ChangeAsEntityComparator(new EntityContainerComparator(
+				new EntityByTypeThenIdThenVersionComparator()));
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		changeSink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		// If this is not the first entity in the pipeline, make sure this
+		// entity is greater than the previous.
+		if (previousChangeContainer != null) {
+			if (comparator.compare(previousChangeContainer, changeContainer) >= 0) {
+				throw new OsmosisRuntimeException(
+					"Pipeline entities are not sorted, previous entity type="
+					+ previousChangeContainer.getEntityContainer().getEntity().getType()
+					+ ", id=" + previousChangeContainer.getEntityContainer().getEntity().getId()
+					+ ", version=" + previousChangeContainer.getEntityContainer().getEntity().getVersion()
+					+ " current entity type=" + changeContainer.getEntityContainer().getEntity().getType()
+					+ ", id=" + changeContainer.getEntityContainer().getEntity().getId() 
+					+ ", version=" + changeContainer.getEntityContainer().getEntity().getVersion() + "."
+				);
+			}
+		}
+		
+		changeSink.process(changeContainer);
+		
+		previousChangeContainer = changeContainer;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeSink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/StackableComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/StackableComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/StackableComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/StackableComparator.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java
new file mode 100644
index 0000000..f3bae18
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorter.java
@@ -0,0 +1,93 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.v0_6;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * A data stream filter that sorts tags on entities. This is useful for testing
+ * to allow two sets of data to be compared for equality.
+ * 
+ * @author Brett Henderson
+ */
+public class TagSorter implements SinkSource {
+	private Sink sink;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		EntityContainer writeableContainer;
+		Entity entity;
+		Collection<Tag> sortedTags;
+		
+		writeableContainer = entityContainer.getWriteableInstance();
+		
+		entity = writeableContainer.getEntity();
+		sortedTags = sortTags(entity.getTags());
+		entity.getTags().clear();
+		entity.getTags().addAll(sortedTags);
+		
+		sink.process(writeableContainer);
+	}
+
+
+	/**
+	 * Sorts the specified tag list.
+	 * 
+	 * @param tagList
+	 *            The tag list to be sorted.
+	 * @return A new list containing the sorted tags.
+	 */
+	private List<Tag> sortTags(Collection<Tag> tagList) {
+		List<Tag> sortedTagList;
+		
+		sortedTagList = new ArrayList<Tag>(tagList);
+		Collections.sort(sortedTagList);
+		
+		return sortedTagList;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorterFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorterFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorterFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/sort/v0_6/TagSorterFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseObjectWriter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/BaseStoreClassRegister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseStoreClassRegister.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/BaseStoreClassRegister.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BaseStoreClassRegister.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/BufferedRandomAccessFileInputStream.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BufferedRandomAccessFileInputStream.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/BufferedRandomAccessFileInputStream.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/BufferedRandomAccessFileInputStream.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ChunkedObjectStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ChunkedObjectStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ChunkedObjectStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ChunkedObjectStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ComparableComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ComparableComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ComparableComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ComparableComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataInputStoreReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataInputStoreReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/DataInputStoreReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataInputStoreReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/DataOutputStoreWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataOutputStoreWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/DataOutputStoreWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataOutputStoreWriter.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java
new file mode 100644
index 0000000..86c1670
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostbox.java
@@ -0,0 +1,544 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.store;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.Initializable;
+
+
+/**
+ * <p>
+ * This class provides a mechanism for a thread to pass data to another thread.
+ * Both threads will block until the other is ready. It supports a single
+ * writing thread, and a single reading thread. Multiple reading or writing
+ * threads are NOT supported.
+ * </p>
+ * <p>
+ * The input thread must call methods in the following sequence:
+ * <ul>
+ * <li>initialize - Called during successful startup, can be skipped in failure
+ * condition</li>
+ * <li>put - Called from zero to N times until input data is exhausted</li>
+ * <li>complete - Only called if input processing completed successfully</li>
+ * <li>release - Called once at the end of processing regardless of success or
+ * failure</li>
+ * </ul>
+ * The output thread must call methods in the following sequence:
+ * <ul>
+ * <li>outputInitialize - Called during successful startup, can be skipped in
+ * failure condition</li>
+ * <li>hasNext/getNext - Both called until hasNext returns false in which case
+ * no more data is available</li>
+ * <li>outputComplete - Only called if output processing completed successfully</li>
+ * <li>outputRelease - Called once at the end of processing regardless of
+ * success or failure</li>
+ * </ul>
+ * </p>
+ * <p>
+ * The input thread will block in the following situations:
+ * <ul>
+ * <li>initialize has been called, but outputInitialize has not yet been called</li>
+ * <li>put has been called, and the buffer is full</li>
+ * <li>The complete method has been called, and outputComplete has not yet been
+ * called</li>
+ * <li>The release method has been called, and outputRelease has not yet been
+ * called. This wait must occur to support the scenario where both threads
+ * subsequently wish to initialize again.</li>
+ * </ul>
+ * The output thread will block in the following situations:
+ * <ul>
+ * <li>The outputInitialize method has been called, and initialize has not yet
+ * been called</li>
+ * <li>hasNext has been called, the buffer is empty, and complete has not yet
+ * been called</li>
+ * <li>The outputComplete method has been called, but complete has not yet been
+ * called (Should never happen because hasNext won't return false until complete
+ * has been called).</li>
+ * <li>The outputRelease method has been called, but release has not yet been
+ * called.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class may be re-used multiple times. For this to work, both input and
+ * output methods must be called an equal number of times or deadlock will
+ * occur. Re-use may occur after input or output threads fail, however in all
+ * cases calls to release and outputRelease must be matched.
+ * </p>
+ * 
+ * @param <T>
+ *            The type of data held in the postbox.
+ */
+public class DataPostbox<T> implements Initializable {
+	private int bufferCapacity;
+	private int chunkSize;
+	private Lock lock;
+	private Condition dataWaitCondition;
+	private Map<String, Object> processingMetaData;
+	private Collection<T> centralQueue;
+	private Collection<T> inboundQueue;
+	private Queue<T> outboundQueue;
+	private boolean inputInitialized;
+	private boolean outputInitialized;
+	private boolean inputComplete;
+	private boolean outputComplete;
+	private boolean inputReleased;
+	private boolean outputReleased;
+	private boolean inputExit;
+	private boolean outputExit;
+	private boolean inputOkay;
+	private boolean outputOkay;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param capacity
+	 *            The maximum number of objects to hold in the postbox before
+	 *            blocking.
+	 */
+	public DataPostbox(int capacity) {
+		if (capacity <= 0) {
+			throw new OsmosisRuntimeException("A capacity of " + capacity + " is invalid, must be greater than 0.");
+		}
+
+		this.bufferCapacity = capacity;
+
+		// Use a chunk size one quarter of total buffer size. This is a magic
+		// number but performance isn't highly sensitive to this parameter.
+		chunkSize = bufferCapacity / 4;
+		if (chunkSize <= 0) {
+			chunkSize = 1;
+		}
+
+		// Create the thread synchronisation primitives.
+		lock = new ReentrantLock();
+		dataWaitCondition = lock.newCondition();
+
+		// Thread synchronisation flags. Each thread moves in lockstep through
+		// each of these phases. Only initialize and or complete flags may be
+		// skipped which trigger error conditions and the setting of the okay
+		// flags.
+		inputInitialized = false;
+		outputInitialized = false;
+		inputComplete = false;
+		outputComplete = false;
+		inputReleased = false;
+		outputReleased = false;
+		inputExit = true;
+		outputExit = true;
+		inputOkay = true;
+		outputOkay = true;
+
+		// Create the inter-thread data transfer queues.
+		initializeQueues();
+	}
+
+
+	private void initializeQueues() {
+		// Create buffer objects.
+		centralQueue = new ArrayList<T>();
+		inboundQueue = new ArrayList<T>();
+		outboundQueue = new ArrayDeque<T>();
+	}
+
+
+	/**
+	 * This is called by the input thread to validate that no errors have
+	 * occurred on the output thread.
+	 */
+	private void checkForOutputErrors() {
+		// Check for reading thread error.
+		if (!outputOkay) {
+			throw new OsmosisRuntimeException("An output error has occurred, aborting.");
+		}
+	}
+
+
+	/**
+	 * This is called by the output thread to validate that no errors have
+	 * occurred on the input thread.
+	 */
+	private void checkForInputErrors() {
+		// Check for writing thread error.
+		if (!inputOkay) {
+			throw new OsmosisRuntimeException("An input error has occurred, aborting.");
+		}
+	}
+
+
+	/**
+	 * Either thread can call this method when they wish to wait until an update
+	 * has been performed by the other thread.
+	 */
+	private void waitForUpdate() {
+		try {
+			dataWaitCondition.await();
+
+		} catch (InterruptedException e) {
+			throw new OsmosisRuntimeException("Thread was interrupted.", e);
+		}
+	}
+
+
+	/**
+	 * Either thread can call this method when they wish to signal the other
+	 * thread that an update has occurred.
+	 */
+	private void signalUpdate() {
+		dataWaitCondition.signal();
+	}
+
+
+	/**
+	 * Adds a group of objects to the central queue ready for consumption by the
+	 * receiver.
+	 * 
+	 * @param o
+	 *            The objects to be added.
+	 */
+	private void populateCentralQueue() {
+		lock.lock();
+
+		try {
+			checkForOutputErrors();
+
+			// Wait until the currently posted data is cleared.
+			while (centralQueue.size() >= bufferCapacity) {
+				waitForUpdate();
+				checkForOutputErrors();
+			}
+
+			// Post the new data.
+			centralQueue.addAll(inboundQueue);
+			inboundQueue.clear();
+			signalUpdate();
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * Empties the contents of the central queue into the outbound queue.
+	 */
+	private void consumeCentralQueue() {
+		lock.lock();
+
+		try {
+			checkForInputErrors();
+
+			// Wait until data is available.
+			while (!((centralQueue.size() > 0) || inputComplete)) {
+				waitForUpdate();
+				checkForInputErrors();
+			}
+
+			outboundQueue.addAll(centralQueue);
+			centralQueue.clear();
+
+			signalUpdate();
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		if (inputInitialized) {
+			throw new OsmosisRuntimeException("initialize has already been called");
+		}
+		
+		lock.lock();
+
+		try {
+			checkForOutputErrors();
+
+			// Set the processing metadata, and flag that we have initialized.
+			processingMetaData = metaData;
+			inputInitialized = true;
+
+			signalUpdate();
+
+			// Now we must wait until the output thread initializes or
+			// encounters an error.
+			while (!outputInitialized) {
+				waitForUpdate();
+				checkForOutputErrors();
+			}
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * Adds a new object to the postbox.
+	 * 
+	 * @param o
+	 *            The object to be added.
+	 */
+	public void put(T o) {
+		if (!inputInitialized) {
+			throw new OsmosisRuntimeException("initialize has not been called");
+		}
+
+		inboundQueue.add(o);
+
+		if (inboundQueue.size() >= chunkSize) {
+			populateCentralQueue();
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		if (!inputInitialized) {
+			throw new OsmosisRuntimeException("initialize has not been called");
+		}
+
+		lock.lock();
+
+		try {
+			populateCentralQueue();
+
+			inputComplete = true;
+
+			signalUpdate();
+
+			// Now we must wait until the output thread completes or
+			// encounters an error.
+			while (!outputComplete) {
+				waitForUpdate();
+				checkForOutputErrors();
+			}
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * This method conforms to the
+	 * {@link org.openstreetmap.osmosis.core.lifecycle.Releasable} contract,
+	 * however there are limitations around calling it multiple times. Each call
+	 * to this method must be matched by a call to the outputRelease method in a
+	 * separate thread or deadlock will occur.
+	 */
+	@Override
+	public void release() {
+		lock.lock();
+
+		try {
+			// If release is being called without having completed successfully,
+			// it is an error condition.
+			if (!inputComplete) {
+				inputOkay = false;
+			}
+
+			inputReleased = true;
+			inputExit = false;
+			signalUpdate();
+
+			// Wait until the output thread releases.
+			while (!outputReleased) {
+				waitForUpdate();
+			}
+
+			// At this point both threads have reached a release state so we can
+			// reset our state.
+			initializeQueues();
+			inputInitialized = false;
+			inputComplete = false;
+			inputReleased = false;
+			inputExit = true;
+			inputOkay = true;
+			signalUpdate();
+
+			// Wait for the output thread to exit.
+			while (!outputExit) {
+				waitForUpdate();
+			}
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * Notifies that the output thread has begun processing, and gets the
+	 * initialization data set by the input thread. This will block until either
+	 * the input thread has called initialize, or an input error occurs.
+	 * 
+	 * @return The initialization data.
+	 */
+	public Map<String, Object> outputInitialize() {
+		if (outputInitialized) {
+			throw new OsmosisRuntimeException("outputInitialize has already been called");
+		}
+		
+		lock.lock();
+
+		try {
+			checkForInputErrors();
+
+			// We must wait until the input thread initializes or
+			// encounters an error.
+			while (!inputInitialized) {
+				waitForUpdate();
+				checkForInputErrors();
+			}
+
+			outputInitialized = true;
+			signalUpdate();
+
+			return processingMetaData;
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * Indicates if data is available for output. This will block until either
+	 * data is available, input processing has completed, or an input error
+	 * occurs.
+	 * 
+	 * @return True if data is available.
+	 */
+	public boolean hasNext() {
+		int queueSize;
+
+		if (!outputInitialized) {
+			throw new OsmosisRuntimeException("outputInitialize has not been called");
+		}
+
+		queueSize = outboundQueue.size();
+
+		if (queueSize <= 0) {
+			consumeCentralQueue();
+			queueSize = outboundQueue.size();
+		}
+
+		return queueSize > 0;
+	}
+
+
+	/**
+	 * Returns the next available object from the postbox. This should be
+	 * preceeded by a call to hasNext.
+	 * 
+	 * @return The next available object.
+	 */
+	public T getNext() {
+		if (hasNext()) {
+			T result;
+
+			result = outboundQueue.remove();
+
+			return result;
+
+		} else {
+			throw new OsmosisRuntimeException("No data is available, should call hasNext first.");
+		}
+	}
+
+
+	/**
+	 * Notifies that the output thread has completed processing. This will block
+	 * until either the input thread has called complete, or an input error
+	 * occurs.
+	 */
+	public void outputComplete() {
+		if (!outputInitialized) {
+			throw new OsmosisRuntimeException("outputInitialize has not been called");
+		}
+		
+		lock.lock();
+
+		try {
+			checkForInputErrors();
+
+			// We must wait until the input thread completes or encounters an
+			// error.
+			while (!inputComplete) {
+				waitForUpdate();
+				checkForInputErrors();
+			}
+
+			outputComplete = true;
+			signalUpdate();
+
+		} finally {
+			lock.unlock();
+		}
+	}
+
+
+	/**
+	 * Notifies that the output thread has released. This will block until the
+	 * input thread has also released and the object has been reset.
+	 */
+	public void outputRelease() {
+		lock.lock();
+
+		try {
+			// If release is being called without having completed successfully,
+			// it is an error condition.
+			if (!outputComplete) {
+				outputOkay = false;
+				signalUpdate();
+			}
+			
+			// Wait until the input thread is released.
+			while (!inputReleased) {
+				waitForUpdate();
+			}
+
+			// At this point both threads have reached a release state so we can
+			// set out state as released but waiting for exit.
+			outputInitialized = false;
+			outputComplete = false;
+			outputReleased = true;
+			outputExit = false;
+			outputOkay = true;
+			signalUpdate();
+
+			// Wait until the input thread has reached the exit point.
+			while (!inputExit) {
+				waitForUpdate();
+			}
+			
+			// The input thread has reached exit, so now we can clear the
+			// release flag (required so that subsequent iterations if they
+			// exist must go through the same handshake sequence) and flag that
+			// we've reached exit.
+			outputReleased = false;
+			outputExit = true;
+			signalUpdate();
+
+		} finally {
+			lock.unlock();
+		}
+	}
+}
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java
new file mode 100644
index 0000000..df6628f
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DataPostboxLoadInjector.java
@@ -0,0 +1,87 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.store;
+
+import java.util.Date;
+
+import org.openstreetmap.osmosis.core.buffer.v0_6.EntityBuffer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.misc.v0_6.NullWriter;
+import org.openstreetmap.osmosis.core.progress.v0_6.EntityProgressLogger;
+
+/**
+ * Very simple class for applying load to the DataPostbox class and measuring
+ * performance.
+ * 
+ * @author Brett Henderson
+ */
+public final class DataPostboxLoadInjector implements Runnable {
+
+	private EntityBuffer buffer;
+	private EntityProgressLogger progressLogger;
+	private NullWriter nullWriter;
+
+	/**
+	 * Launches the application.
+	 * 
+	 * @param args
+	 *            The program arguments.
+	 */
+	public static void main(String[] args) {
+		new DataPostboxLoadInjector().run();
+	}
+
+	
+	private DataPostboxLoadInjector() {
+		buffer = new EntityBuffer(10000);
+		progressLogger = new EntityProgressLogger(5000, null);
+		buffer.setSink(progressLogger);
+		nullWriter = new NullWriter();
+		progressLogger.setSink(nullWriter);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void run() {
+		Thread t1;
+		Thread t2;
+
+		t1 = new Thread(new Writer(), "input");
+		t2 = new Thread(buffer, "output");
+
+		t1.start();
+		t2.start();
+
+		try {
+			t1.join();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		try {
+			t2.join();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+	}
+
+	private class Writer implements Runnable {
+		private Node node;
+		private NodeContainer nodeContainer;
+
+		public Writer() {
+			node = new Node(new CommonEntityData(1, 2, new Date(), OsmUser.NONE, 3), 10, 10);
+			nodeContainer = new NodeContainer(node);
+		}
+
+		public void run() {
+			while (true) {
+				buffer.process(nodeContainer);
+			}
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/DynamicStoreClassRegister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DynamicStoreClassRegister.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/DynamicStoreClassRegister.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/DynamicStoreClassRegister.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/EmptyIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/EmptyIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/EmptyIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/EmptyIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/EndOfStoreException.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/EndOfStoreException.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/EndOfStoreException.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/EndOfStoreException.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectSerializationFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectSerializationFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectSerializationFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectSerializationFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/GenericObjectWriter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexElement.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexElement.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexElement.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexElement.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexRangeIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexRangeIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexRangeIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexRangeIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStoreReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStoreReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStoreReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexStoreReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStoreReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStoreReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStoreReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IndexedObjectStoreReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/IntegerLongIndexElement.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IntegerLongIndexElement.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/IntegerLongIndexElement.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/IntegerLongIndexElement.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/LongLongIndexElement.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/LongLongIndexElement.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/LongLongIndexElement.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/LongLongIndexElement.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/MultipleSourceIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/MultipleSourceIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/MultipleSourceIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/MultipleSourceIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/NoSuchIndexElementException.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/NoSuchIndexElementException.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/NoSuchIndexElementException.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/NoSuchIndexElementException.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectDataInputIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectDataInputIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectDataInputIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectDataInputIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectSerializationFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectSerializationFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectSerializationFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectSerializationFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectStreamIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectStreamIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectStreamIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectStreamIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ObjectWriter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/OffsetTrackingOutputStream.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/OffsetTrackingOutputStream.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/OffsetTrackingOutputStream.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/OffsetTrackingOutputStream.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/PeekableIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/PeekableIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/PeekableIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/PeekableIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/PersistentIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/PersistentIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/PersistentIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/PersistentIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStoreReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStoreReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStoreReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/RandomAccessObjectStoreReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/ReleasableAdaptorForIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ReleasableAdaptorForIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/ReleasableAdaptorForIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/ReleasableAdaptorForIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SegmentedObjectStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SegmentedObjectStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SegmentedObjectStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SegmentedObjectStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SimpleObjectStore.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SimpleObjectStore.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SimpleObjectStore.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SimpleObjectStore.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectSerializationFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectSerializationFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectSerializationFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectSerializationFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SingleClassObjectWriter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StaticStoreClassRegister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StaticStoreClassRegister.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StaticStoreClassRegister.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StaticStoreClassRegister.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StorageStage.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StorageStage.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StorageStage.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StorageStage.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StoreClassRegister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreClassRegister.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StoreClassRegister.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreClassRegister.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReader.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReader.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReader.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReader.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReleasingIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReleasingIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReleasingIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreReleasingIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StoreWriter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreWriter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StoreWriter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreWriter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/Storeable.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/Storeable.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/Storeable.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/Storeable.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/StoreableConstructorCache.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreableConstructorCache.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/StoreableConstructorCache.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/StoreableConstructorCache.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/SubObjectStreamIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SubObjectStreamIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/SubObjectStreamIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/SubObjectStreamIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/UnsignedIntegerComparator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/UnsignedIntegerComparator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/UnsignedIntegerComparator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/UnsignedIntegerComparator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/store/UpcastIterator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/UpcastIterator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/store/UpcastIterator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/store/UpcastIterator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/common/ChangeAction.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/ChangeAction.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/common/ChangeAction.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/ChangeAction.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/common/RunnableTask.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/RunnableTask.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/common/RunnableTask.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/RunnableTask.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/common/Task.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/Task.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/common/Task.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/common/Task.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java
new file mode 100644
index 0000000..650f7fe
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSink.java
@@ -0,0 +1,22 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.task.v0_6;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.common.Task;
+
+
+/**
+ * Defines the interface for all tasks consuming OSM changes to data.
+ * 
+ * @author Brett Henderson
+ */
+public interface ChangeSink extends Task, Initializable {
+
+	/**
+	 * Process the change.
+	 * 
+	 * @param change
+	 *            The change to be processed.
+	 */
+	void process(ChangeContainer change);
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkMultiChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkMultiChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkMultiChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkMultiChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkRunnableChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkRunnableChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkRunnableChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkRunnableChangeSource.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkSource.java
new file mode 100644
index 0000000..d692e52
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSinkSource.java
@@ -0,0 +1,13 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.task.v0_6;
+
+
+/**
+ * Defines the interface for combining change sink and source functionality.
+ * This is used by classes converting a change stream into an entity stream.
+ * 
+ * @author Brett Henderson
+ */
+public interface ChangeSinkSource extends ChangeSink, Source {
+	// Interface only combines functionality of its extended interfaces.
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/ChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSink.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSink.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSink.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSink.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSinkSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSinkSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSinkSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSinkSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/DatasetSource.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Initializable.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Initializable.java
new file mode 100644
index 0000000..590b9bd
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Initializable.java
@@ -0,0 +1,30 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.task.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.lifecycle.Completable;
+
+
+/**
+ * This interface defines methods required to manage class lifecycles. All
+ * clients must first call the initialize method, then the implementation
+ * specific processing methods, then complete, and finally the release method.
+ * It may be possible to call initialize multiple times, but each call must be
+ * matched by a call to complete. Release must be called at the completion of
+ * all processing. It may be possible to call release multiple times, but
+ * initialize must be called again before processing can proceed.
+ * 
+ * @author Brett Henderson
+ */
+public interface Initializable extends Completable {
+
+	/**
+	 * Initialize the object. Any global information applicable to this
+	 * processing phase can be generically set as meta data.
+	 * 
+	 * @param metaData
+	 *            Meta data applicable to this pipeline invocation.
+	 */
+	void initialize(Map<String, Object> metaData);
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSink.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSink.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSink.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSink.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSinkRunnableChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSinkRunnableChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSinkRunnableChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSinkRunnableChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSink.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSink.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSink.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSink.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkMultiChangeSinkRunnableSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkMultiChangeSinkRunnableSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkMultiChangeSinkRunnableSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkMultiChangeSinkRunnableSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSinkRunnableSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/MultiSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableChangeSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableChangeSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableChangeSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableChangeSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableDatasetSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableDatasetSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableDatasetSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableDatasetSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/RunnableSource.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java
new file mode 100644
index 0000000..f675dc8
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Sink.java
@@ -0,0 +1,22 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.task.v0_6;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.common.Task;
+
+
+/**
+ * Defines the interface for tasks consuming OSM data types.
+ * 
+ * @author Brett Henderson
+ */
+public interface Sink extends Task, Initializable {
+
+	/**
+	 * Process the entity.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 */
+	void process(EntityContainer entityContainer);
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkDatasetSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkDatasetSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkDatasetSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkDatasetSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkMultiSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkMultiSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkMultiSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkMultiSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkRunnableSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkRunnableSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkRunnableSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkRunnableSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkSource.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkSource.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkSource.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/SinkSource.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Source.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Source.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Source.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/task/v0_6/Source.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java
new file mode 100644
index 0000000..2b8376c
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTee.java
@@ -0,0 +1,162 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.tee.v0_6;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkMultiChangeSource;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSource;
+
+
+/**
+ * Sends input change data to two output destinations.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeTee implements ChangeSinkMultiChangeSource {
+	
+	private List<ProxyChangeSinkChangeSource> sinkList;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param outputCount
+	 *            The number of output destinations to write to.
+	 */
+	public ChangeTee(int outputCount) {
+		sinkList = new ArrayList<ProxyChangeSinkChangeSource>();
+		
+		for (int i = 0; i < outputCount; i++) {
+			sinkList.add(new ProxyChangeSinkChangeSource());
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public ChangeSource getChangeSource(int index) {
+		if (index < 0 || index >= sinkList.size()) {
+			throw new OsmosisRuntimeException("Source index " + index
+					+ " is in the range 0 to " + (sinkList.size() - 1) + ".");
+		}
+		
+		return sinkList.get(index);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getChangeSourceCount() {
+		return sinkList.size();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		for (ProxyChangeSinkChangeSource sink : sinkList) {
+			sink.initialize(metaData);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer change) {
+		for (ProxyChangeSinkChangeSource sink : sinkList) {
+			// We're passing the data to multiple downstream tasks therefore should make the entity
+			// read-only to prevent multiple threads impacting each other.
+			change.getEntityContainer().getEntity().makeReadOnly();
+			
+			sink.process(change);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		for (ProxyChangeSinkChangeSource sink : sinkList) {
+			sink.complete();
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		for (ProxyChangeSinkChangeSource sink : sinkList) {
+			sink.release();
+		}
+	}
+	
+	
+	/**
+	 * Instances of this class are returned via the parent class getSource method.
+	 * 
+	 * @author Brett Henderson
+	 */
+	private static class ProxyChangeSinkChangeSource implements ChangeSinkChangeSource {
+		private ChangeSink changeSink;
+		
+		
+		/**
+		 * Creates a new instance.
+		 */
+		public ProxyChangeSinkChangeSource() {
+			// Nothing to do.
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void setChangeSink(ChangeSink changeSink) {
+			this.changeSink = changeSink;
+		}
+
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public void initialize(Map<String, Object> metaData) {
+			changeSink.initialize(metaData);
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(ChangeContainer change) {
+			changeSink.process(change);
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void complete() {
+			changeSink.complete();
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void release() {
+			changeSink.release();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTeeFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTeeFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTeeFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/ChangeTeeFactory.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java
new file mode 100644
index 0000000..0be8f9a
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTee.java
@@ -0,0 +1,162 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.tee.v0_6;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkMultiSource;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Source;
+
+
+/**
+ * Sends input data to two output destinations.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityTee implements SinkMultiSource {
+	
+	private List<ProxySinkSource> sinkList;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param outputCount
+	 *            The number of output destinations to write to.
+	 */
+	public EntityTee(int outputCount) {
+		sinkList = new ArrayList<ProxySinkSource>();
+		
+		for (int i = 0; i < outputCount; i++) {
+			sinkList.add(new ProxySinkSource());
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public Source getSource(int index) {
+		if (index < 0 || index >= sinkList.size()) {
+			throw new OsmosisRuntimeException("Source index " + index
+					+ " is in the range 0 to " + (sinkList.size() - 1) + ".");
+		}
+		
+		return sinkList.get(index);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public int getSourceCount() {
+		return sinkList.size();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		for (ProxySinkSource sink : sinkList) {
+			sink.initialize(metaData);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		for (ProxySinkSource sink : sinkList) {
+			// We're passing the data to multiple downstream tasks therefore should make the entity
+			// read-only to prevent multiple threads impacting each other.
+			entityContainer.getEntity().makeReadOnly();
+			
+			sink.process(entityContainer);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		for (ProxySinkSource sink : sinkList) {
+			sink.complete();
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		for (ProxySinkSource sink : sinkList) {
+			sink.release();
+		}
+	}
+	
+	
+	/**
+	 * Instances of this class are returned via the parent class getSource method.
+	 * 
+	 * @author Brett Henderson
+	 */
+	private static class ProxySinkSource implements SinkSource {
+		private Sink sink;
+		
+		
+		/**
+		 * Creates a new instance.
+		 */
+		public ProxySinkSource() {
+			// Nothing to do.
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void setSink(Sink sink) {
+			this.sink = sink;
+		}
+
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public void initialize(Map<String, Object> metaData) {
+			sink.initialize(metaData);
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(EntityContainer entityContainer) {
+			sink.process(entityContainer);
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void complete() {
+			sink.complete();
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void release() {
+			sink.release();
+		}		
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTeeFactory.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTeeFactory.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTeeFactory.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/tee/v0_6/EntityTeeFactory.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/time/DateFormatter.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/DateFormatter.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/time/DateFormatter.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/DateFormatter.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/time/DateParser.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/DateParser.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/time/DateParser.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/DateParser.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/time/FallbackDateParser.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/FallbackDateParser.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/time/FallbackDateParser.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/time/FallbackDateParser.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/AtomicFileCreator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/AtomicFileCreator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/AtomicFileCreator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/AtomicFileCreator.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/CollectionWrapper.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/CollectionWrapper.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/CollectionWrapper.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/CollectionWrapper.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java
new file mode 100644
index 0000000..251c9ee
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/FileBasedLock.java
@@ -0,0 +1,120 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.lifecycle.Releasable;
+
+
+/**
+ * This class provides a mechanism to use simple files as locks to prevent
+ * multiple threads or processes from updating common files.
+ * 
+ * @author Brett Henderson
+ */
+public class FileBasedLock implements Releasable {
+	
+	private static final Logger LOG = Logger.getLogger(FileBasedLock.class.getName());
+	
+	private File lockFile;
+	private FileOutputStream outputStream;
+	private FileChannel fileChannel;
+	private FileLock fileLock;
+	private boolean initialized;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param lockFile
+	 *            The file to use for locking.
+	 */
+	public FileBasedLock(File lockFile) {
+		this.lockFile = lockFile;
+		
+		initialized = false;
+	}
+	
+	
+	/**
+	 * Creates the file resources used internally for implementing the lock.
+	 */
+	private void initialize() {
+		if (!initialized) {
+			try {
+				outputStream = new FileOutputStream(lockFile);
+			} catch (IOException e) {
+				throw new OsmosisRuntimeException("Unable to open lock file " + lockFile + ".");
+			}
+			
+			fileChannel = outputStream.getChannel();
+			
+			initialized = true;
+		}
+	}
+	
+	
+	/**
+	 * Obtain an exclusive lock. This will fail if another thread or process
+	 * already has a lock.
+	 */
+	public void lock() {
+		initialize();
+		
+		if (fileLock != null) {
+			throw new OsmosisRuntimeException("A lock has already been obtained on file " + lockFile + ".");
+		}
+		
+		try {
+			fileLock = fileChannel.tryLock();
+			
+			if (fileLock == null) {
+				throw new OsmosisRuntimeException("A exclusive lock already exists on file " + lockFile + ".");
+			}
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException(
+					"An error occurred while trying to obtain an exclusive lock on file " + lockFile + ".");
+		}
+	}
+	
+	
+	/**
+	 * Release the lock.
+	 */
+	public void unlock() {
+		initialize();
+		
+		try {
+			fileLock.release();
+			fileLock = null;
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to release lock on file " + lockFile + ".");
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		if (outputStream != null) {
+			try {
+				outputStream.close();
+			} catch (Exception e) {
+				LOG.warning("Unable to close lock stream on file " + lockFile + ".");
+			} finally {
+				outputStream = null;
+				fileChannel = null;
+				fileLock = null;
+				initialized = false;
+			}
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/FixedPrecisionCoordinateConvertor.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/FixedPrecisionCoordinateConvertor.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/FixedPrecisionCoordinateConvertor.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/FixedPrecisionCoordinateConvertor.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/IntAsChar.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/IntAsChar.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/IntAsChar.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/IntAsChar.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java
new file mode 100644
index 0000000..9034039
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/LazyHashMap.java
@@ -0,0 +1,180 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This map uses a HashMap internally, but only instantiates it after values are
+ * added. This is intended for cases where most instances of the map will remain
+ * empty and avoids the instantiation overhead of creating the full map.
+ * 
+ * @author Brett Henderson
+ * 
+ * @param <K>
+ *            the type of keys maintained by this map
+ * @param <V>
+ *            the type of mapped values
+ */
+public class LazyHashMap<K, V> implements Map<K, V> {
+
+	private Map<K, V> internalMap;
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void clear() {
+		if (internalMap != null) {
+			internalMap.clear();
+		}
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean containsKey(Object key) {
+		if (internalMap != null) {
+			return internalMap.containsKey(key);
+		} else {
+			return false;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean containsValue(Object value) {
+		if (internalMap != null) {
+			return internalMap.containsValue(value);
+		} else {
+			return false;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Set<Map.Entry<K, V>> entrySet() {
+		if (internalMap != null) {
+			return internalMap.entrySet();
+		} else {
+			return Collections.emptySet();
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public V get(Object key) {
+		if (internalMap != null) {
+			return internalMap.get(key);
+		} else {
+			return null;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean isEmpty() {
+		if (internalMap != null) {
+			return internalMap.isEmpty();
+		} else {
+			return true;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Set<K> keySet() {
+		if (internalMap != null) {
+			return internalMap.keySet();
+		} else {
+			return Collections.emptySet();
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public V put(K key, V value) {
+		if (internalMap == null) {
+			internalMap = new HashMap<K, V>();
+		}
+		
+		return internalMap.put(key, value);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void putAll(Map<? extends K, ? extends V> m) {
+		if (internalMap == null) {
+			internalMap = new HashMap<K, V>();
+		}
+		
+		internalMap.putAll(m);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public V remove(Object key) {
+		if (internalMap != null) {
+			return internalMap.remove(key);
+		} else {
+			return null;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int size() {
+		if (internalMap != null) {
+			return internalMap.size();
+		} else {
+			return 0;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Collection<V> values() {
+		if (internalMap != null) {
+			return internalMap.values();
+		} else {
+			return Collections.<K, V>emptyMap().values();
+		}
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/LongAsInt.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/LongAsInt.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/LongAsInt.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/LongAsInt.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/MultiMemberGZIPInputStream.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/MultiMemberGZIPInputStream.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/MultiMemberGZIPInputStream.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/MultiMemberGZIPInputStream.java
diff --git a/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java
new file mode 100644
index 0000000..aa6aab4
--- /dev/null
+++ b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/PropertiesPersister.java
@@ -0,0 +1,154 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Allows Properties objects to be loaded and stored to file.
+ */
+public class PropertiesPersister {
+	
+	private static final Logger LOG = Logger.getLogger(PropertiesPersister.class.getName());
+	
+	
+	private AtomicFileCreator atomicFileCreator;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param propertiesFile
+	 *            The location of the file containing the persisted data.
+	 */
+	public PropertiesPersister(File propertiesFile) {
+		atomicFileCreator = new AtomicFileCreator(propertiesFile);
+	}
+	
+	
+	/**
+	 * Loads the properties from the file.
+	 * 
+	 * @return The properties.
+	 */
+	public Properties load() {
+
+		FileInputStream fileInputStream = null;
+		
+		try {
+			Reader reader;
+			Properties properties;
+			
+			fileInputStream = new FileInputStream(atomicFileCreator.getFile());
+			reader = new InputStreamReader(new BufferedInputStream(fileInputStream), Charset.forName("UTF-8"));
+			
+			properties = new Properties();
+			properties.load(reader);
+			
+			fileInputStream.close();
+			fileInputStream = null;
+			
+			return properties;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read the properties from file " + atomicFileCreator.getFile()
+					+ ".", e);
+		} finally {
+			if (fileInputStream != null) {
+				try {
+					fileInputStream.close();
+				} catch (Exception e) {
+					LOG.log(Level.WARNING, "Unable to close properties file " + atomicFileCreator.getFile() + ".", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Load the properties from a file as a strongly typed Map.
+	 * 
+	 * @return The properties.
+	 */
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	public Map<String, String> loadMap() {
+		return new HashMap<String, String>((Map) load());
+	}
+	
+	
+	/**
+	 * Stores the properties to the file overwriting any existing file contents.
+	 * 
+	 * @param properties
+	 *            The properties.
+	 */
+	public void store(Properties properties) {
+		FileOutputStream fileOutputStream = null;
+		
+		try {
+			Writer writer;
+			
+			fileOutputStream = new FileOutputStream(atomicFileCreator.getTmpFile());
+			writer = new OutputStreamWriter(new BufferedOutputStream(fileOutputStream));
+			
+			properties.store(writer, null);
+			
+			writer.close();
+			
+			atomicFileCreator.renameTmpFileToCurrent();
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException(
+					"Unable to write the properties to temporary file " + atomicFileCreator.getTmpFile() + ".", e);
+		} finally {
+			if (fileOutputStream != null) {
+				try {
+					fileOutputStream.close();
+				} catch (Exception e) {
+					LOG.log(Level.WARNING, "Unable to close temporary state file " + atomicFileCreator.getTmpFile()
+							+ ".", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Stores the properties to the file overwriting any existing file contents.
+	 * 
+	 * @param propertiesMap
+	 *            The properties.
+	 */
+	public void store(Map<String, String> propertiesMap) {
+		Properties properties = new Properties();
+		properties.putAll(propertiesMap);
+		store(properties);
+	}
+
+
+	/**
+	 * Checks if the properties file exists.
+	 * 
+	 * @return True if a file exists, false otherwise.
+	 */
+	public boolean exists() {
+		return atomicFileCreator.exists();
+	}
+}
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/ResourceFileManager.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/ResourceFileManager.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/ResourceFileManager.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/ResourceFileManager.java
diff --git a/core/src/main/java/org/openstreetmap/osmosis/core/util/TileCalculator.java b/osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/TileCalculator.java
similarity index 100%
rename from core/src/main/java/org/openstreetmap/osmosis/core/util/TileCalculator.java
rename to osmosis-core/src/main/java/org/openstreetmap/osmosis/core/util/TileCalculator.java
diff --git a/core/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml.template b/osmosis-core/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml.template
similarity index 100%
rename from core/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml.template
rename to osmosis-core/src/main/resources/org/openstreetmap/osmosis/core/plugin/plugin.xml.template
diff --git a/core/src/main/resources/osmosis-plugins.conf b/osmosis-core/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from core/src/main/resources/osmosis-plugins.conf
rename to osmosis-core/src/main/resources/osmosis-plugins.conf
diff --git a/core/src/site/site.xml b/osmosis-core/src/site/site.xml
similarity index 100%
rename from core/src/site/site.xml
rename to osmosis-core/src/site/site.xml
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/MyPluginLoader.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/MyPluginLoader.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/MyPluginLoader.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/MyPluginLoader.java
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java
new file mode 100644
index 0000000..91887e8
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundComputerTest.java
@@ -0,0 +1,99 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.bound.v0_6;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+
+/**
+ * Unit tests for the bound computer.
+ * 
+ * @author Igor Podolskiy
+ */
+public class BoundComputerTest {
+
+	/**
+	 * Tests the bound computation if no nodes are upstream.
+	 */
+	@Test
+	public void computeBoundNoNodes() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundComputer bc = new BoundComputer("NewBound");
+		bc.setSink(inspector);
+		bc.complete();
+		bc.release();
+
+		Assert.assertNull(inspector.getLastEntityContainer());
+	}
+
+
+	/**
+	 * Tests the bound computation if no nodes but a bound entity is upstream.
+	 */
+	@Test
+	public void computeBoundNoNodesWithBound() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundComputer bc = new BoundComputer("NewBound");
+		bc.setSink(inspector);
+		bc.process(new BoundContainer(new Bound("Test")));
+		bc.complete();
+		bc.release();
+
+		Assert.assertNull(inspector.getLastEntityContainer());
+	}
+
+
+	/**
+	 * Tests the bound computation when no bound entity is upstream.
+	 */
+	@Test
+	public void computeBoundNoUpstreamBound() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundComputer bc = new BoundComputer("NewBound");
+		bc.setSink(inspector);
+		bc.process(new NodeContainer(new Node(new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		bc.process(new NodeContainer(new Node(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 1), 2, 2)));
+		bc.complete();
+		bc.release();
+
+		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
+		Assert.assertEquals(new Bound(2, 1, 2, 1, "NewBound"), ec.getEntity());
+	}
+
+
+	/**
+	 * Tests the bound computation when there is bound entity is upstream.
+	 */
+	@Test
+	public void computeBoundWithUpstreamBound() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundComputer bc = new BoundComputer("NewBound");
+		bc.setSink(inspector);
+		bc.process(new NodeContainer(new Node(new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		bc.process(new NodeContainer(new Node(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 1), 2, 2)));
+		bc.complete();
+		bc.release();
+
+		Iterator<EntityContainer> iterator = inspector.getProcessedEntities().iterator();
+		EntityContainer ec = iterator.next();
+		Assert.assertEquals(new Bound(2, 1, 2, 1, "NewBound"), ec.getEntity());
+
+		// Ensure there is no second bound.
+		ec = iterator.next();
+		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
+	}
+
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java
new file mode 100644
index 0000000..ba7f13a
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/bound/v0_6/BoundSetterTest.java
@@ -0,0 +1,108 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.bound.v0_6;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+/**
+ * Test for the bound setter task.
+ *
+ * @author Igor Podolskiy
+ */
+public class BoundSetterTest {
+
+	/**
+	 * Tests the bound removal.
+	 */
+	@Test
+	public void removeExistingBoundTest() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundSetter setter = new BoundSetter(null);
+		setter.setSink(inspector);
+		setter.process(new BoundContainer(new Bound("Test")));
+		setter.process(new NodeContainer(new Node(
+				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		setter.complete();
+		setter.release();
+		
+		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
+		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
+	}
+	
+	/**
+	 * Tests the bound removal when there is no bound upstream.
+	 */
+	@Test
+	public void removeNoBoundTest() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		BoundSetter setter = new BoundSetter(null);
+		setter.setSink(inspector);
+		setter.process(new NodeContainer(new Node(
+				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		setter.complete();
+		setter.release();
+		
+		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
+		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
+	}
+	
+	/**
+	 * Tests the bound setting.
+	 */
+	@Test
+	public void overwriteBoundTest() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		Bound newBound = new Bound(2, 1, 4, 3, "NewBound");
+		BoundSetter setter = new BoundSetter(newBound);
+		setter.setSink(inspector);
+		setter.process(new BoundContainer(new Bound("Test")));
+		setter.process(new NodeContainer(new Node(
+				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		setter.complete();
+		setter.release();
+		
+		Iterator<EntityContainer> iterator = inspector.getProcessedEntities().iterator();
+		EntityContainer ec = iterator.next();
+		Assert.assertEquals(EntityType.Bound, ec.getEntity().getType());
+		Bound bound = (Bound) ec.getEntity();
+		Assert.assertEquals(bound, newBound);
+		
+		// Ensure there is no second bound
+		ec = iterator.next();
+		Assert.assertEquals(EntityType.Node, ec.getEntity().getType());
+	}
+	
+	/**
+	 * Tests the bound setting when there is no bound upstream.
+	 */
+	@Test
+	public void setNewBoundTest() {
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		Bound newBound = new Bound(2, 1, 4, 3, "NewBound");
+		BoundSetter setter = new BoundSetter(newBound);
+		setter.setSink(inspector);
+		setter.process(new NodeContainer(new Node(
+				new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1)));
+		setter.complete();
+		setter.release();
+		
+		EntityContainer ec = inspector.getProcessedEntities().iterator().next();
+		Assert.assertEquals(EntityType.Bound, ec.getEntity().getType());
+		Bound bound = (Bound) ec.getEntity();
+		Assert.assertEquals(bound, newBound);
+	}
+
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java
new file mode 100644
index 0000000..7d9757c
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/cli/CommandLineParserTest.java
@@ -0,0 +1,140 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.cli;
+
+import java.util.Arrays;
+import java.util.logging.Level;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.openstreetmap.osmosis.core.LogLevels;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Tests the CommandLineParser class.
+ * 
+ * @author Brett Henderson
+ */
+public class CommandLineParserTest {
+	/**
+	 * Validates the quiet option.
+	 */
+	@Test
+	public void testQuietOption() {
+		CommandLineParser commandLineParser;
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {});
+		Assert.assertEquals("Incorrect default log level.", Level.INFO,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-q"});
+		Assert.assertEquals("Incorrect quiet log level.", Level.WARNING,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-q", "1"});
+		Assert.assertEquals("Incorrect very quiet log level.", Level.SEVERE,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-q", "2"});
+		Assert.assertEquals("Incorrect very very quiet log level.", Level.OFF,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+	}
+	
+	
+	/**
+	 * Validates the verbose option.
+	 */
+	@Test
+	public void testVerboseOption() {
+		CommandLineParser commandLineParser;
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {});
+		Assert.assertEquals("Incorrect default log level.", Level.INFO,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v"});
+		Assert.assertEquals("Incorrect verbose log level.", Level.FINE,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "1"});
+		Assert.assertEquals("Incorrect very verbose log level.", Level.FINER,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "2"});
+		Assert.assertEquals("Incorrect very very verbose log level.", Level.FINEST,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "3"});
+		Assert.assertEquals(
+				"Incorrect very very very verbose log level.",
+				Level.FINEST,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+	}
+	
+	
+	/**
+	 * Validates the quiet and verbose options in combination.
+	 */
+	@Test
+	public void testQuietAndVerboseOption() {
+		CommandLineParser commandLineParser;
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {});
+		Assert.assertEquals("Incorrect default log level.", Level.INFO,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "-q"});
+		Assert.assertEquals("Incorrect default log level.", Level.INFO,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "1", "-q", "1"});
+		Assert.assertEquals("Incorrect default log level.", Level.INFO,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-v", "1", "-q", "2"});
+		Assert.assertEquals("Incorrect quiet log level.", Level.WARNING,
+				LogLevels.getLogLevel(commandLineParser.getLogLevelIndex()));
+	}
+	
+	
+	/**
+	 * Validates the quiet and verbose options in combination.
+	 */
+	@Test
+	public void testPluginOption() {
+		CommandLineParser commandLineParser;
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-p", "plugin1", "-p", "plugin2"});
+		Assert.assertEquals(
+				"Incorrect plugin list.",
+				Arrays.asList("plugin1", "plugin2"),
+				commandLineParser.getPlugins());
+	}
+	
+	
+	/**
+	 * Validates failure when an unknown option is specified.
+	 */
+	@Test (expected = OsmosisRuntimeException.class)
+	public void testUnknownOption() {
+		CommandLineParser commandLineParser;
+		
+		commandLineParser = new CommandLineParser();
+		commandLineParser.parse(new String [] {"-a"});
+	}
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java
new file mode 100644
index 0000000..bd7c6de
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/BoundTest.java
@@ -0,0 +1,587 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.domain.v0_6;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+
+/**
+ * Tests the Bound entity class.
+ * 
+ * @author Karl Newman
+ */
+public class BoundTest {
+
+	/**
+	 * Test the constructor with right > 180.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor1() {
+		new Bound(181.0000000000001, -20, 20, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with right < -180.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor2() {
+		new Bound(-181.0000000000001, -20, 20, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with left > 180.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor3() {
+		new Bound(20, 181.0000000000001, 20, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with left < -180.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor4() {
+		new Bound(20, -181.0000000000001, 20, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with top > 90.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor5() {
+		new Bound(20, -20, 91.0000000000001, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with top < -90.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor6() {
+		new Bound(20, -20, -91.0000000000001, -20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with bottom > 90.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor7() {
+		new Bound(20, -20, 20, 91.0000000000001, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with bottom < -90.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor8() {
+		new Bound(20, -20, 20, -91.0000000000001, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test the constructor with top < bottom.
+	 */
+	@Test(expected = IllegalArgumentException.class)
+	public final void testConstructor9() {
+		new Bound(20, -20, -20, 20, "not null");
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a valid constructor with only the origin string provided (covers full planet).
+	 */
+	@Test
+	public final void testConstructor11() {
+		Bound b = new Bound("not null");
+		assertTrue(Double.compare(b.getRight(), 180) == 0
+		        && Double.compare(b.getLeft(), -180) == 0
+		        && Double.compare(b.getTop(), 90) == 0
+		        && Double.compare(b.getBottom(), -90) == 0
+		        && b.getOrigin().equals("not null"));
+	}
+
+
+	/**
+	 * Test a valid constructor with all values provided.
+	 */
+	@Test
+	public final void testConstructor12() {
+		Bound b = new Bound(20, -20, 21, -21, "not null");
+		assertTrue(Double.compare(b.getRight(), 20) == 0
+		        && Double.compare(b.getLeft(), -20) == 0
+		        && Double.compare(b.getTop(), 21) == 0
+		        && Double.compare(b.getBottom(), -21) == 0
+		        && b.getOrigin().equals("not null"));
+	}
+
+
+	/**
+	 * Test a simple intersection.
+	 */
+	@Test
+	public final void testIntersect1() {
+		final double right1 = 20.0;
+		final double left1 = 10.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 30.0;
+		final double left2 = 15.0;
+		final double top2 = 45.0;
+		final double bottom2 = 20.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "this");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
+		Bound b = b1.intersect(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("this"));
+		// Test it with arguments swapped
+		b = b2.intersect(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+	}
+
+
+	/**
+	 * Test an intersect with no top-bottom overlapping areas.
+	 */
+	@Test
+	public final void testIntersect2() {
+		final double right1 = 20.0;
+		final double left1 = 10.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 30.0;
+		final double left2 = 15.0;
+		final double top2 = 25.0;
+		final double bottom2 = 20.0;
+		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
+		        right2,
+		        left2,
+		        top2,
+		        bottom2,
+		        ""));
+		assertNull(b);
+	}
+
+
+	/**
+	 * Test an intersect with no left-right overlapping areas.
+	 */
+	@Test
+	public final void testIntersect3() {
+		final double right1 = 20.0;
+		final double left1 = 10.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 30.0;
+		final double left2 = 21.0;
+		final double top2 = 45.0;
+		final double bottom2 = 20.0;
+		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
+		        right2,
+		        left2,
+		        top2,
+		        bottom2,
+		        ""));
+		assertNull(b);
+	}
+
+
+	/**
+	 * Test an intersect with 1 Bound crossing the antimeridian.
+	 */
+	@Test
+	public final void testIntersect4() {
+		final double right1 = 20.0;
+		final double left1 = 60.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 30.0;
+		final double left2 = 15.0;
+		final double top2 = 45.0;
+		final double bottom2 = 20.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
+		Bound b = b1.intersect(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+		// Test it with arguments swapped
+		b = b2.intersect(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+	}
+
+
+	/**
+	 * Test an intersection where one Bound crosses the antimeridian (but doesn't cover the planet)
+	 * and both ends overlap with the intersecting Bound. A strict intersection would result in two
+	 * Bound areas, so just expect the smaller (longitudinally) of the Bound as the result.
+	 */
+	@Test
+	public final void testIntersect5() {
+		final double right1 = 150.0;
+		final double left1 = 170.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 175.0;
+		final double left2 = 145.0;
+		final double top2 = 45.0;
+		final double bottom2 = 25.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.intersect(b2);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+		// Test it with arguments swapped
+		b = b2.intersect(b1);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+	}
+
+
+	/**
+	 * Test an intersect with both Bound crossing the antimeridian.
+	 */
+	@Test
+	public final void testIntersect6() {
+		final double right1 = 20.0;
+		final double left1 = 60.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 30.0;
+		final double left2 = 50.0;
+		final double top2 = 45.0;
+		final double bottom2 = 35.0;
+		Bound b = new Bound(right1, left1, top1, bottom1, "").intersect(new Bound(
+		        right2,
+		        left2,
+		        top2,
+		        bottom2,
+		        ""));
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom2) == 0);
+	}
+
+
+	/**
+	 * Test a simple union on opposite sides of the planet with exactly the same distance between
+	 * them on both sides. The smallest resulting union could wrap around the planet either way, so
+	 * expect a simple Bound which does not cross the antimeridian.
+	 */
+	@Test
+	public final void testUnion1() {
+		final double right1 = 90.0;
+		final double left1 = 80.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = -90.0;
+		final double left2 = -100.0;
+		final double top2 = 45.0;
+		final double bottom2 = 35.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "this");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("this"));
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+	}
+
+
+	/**
+	 * Test a union where one Bound is entirely contained by another.
+	 */
+	@Test
+	public final void testUnion2() {
+		final double right1 = 20.0;
+		final double left1 = 10.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 15.0;
+		final double left2 = 12.0;
+		final double top2 = 35.0;
+		final double bottom2 = 32.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "that");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0
+		        && b.getOrigin().equals("that"));
+	}
+
+
+	/**
+	 * Test a union of two simple Bound where the resulting Bound crosses the antimeridian.
+	 */
+	@Test
+	public final void testUnion3() {
+		final double right1 = 91.0;
+		final double left1 = 80.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = -90.0;
+		final double left2 = -100.0;
+		final double top2 = 45.0;
+		final double bottom2 = 35.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+	}
+
+
+	/**
+	 * Test a union where one Bound crosses the antimeridian but there is still a gap such that the
+	 * union does not cover the planet.
+	 */
+	@Test
+	public final void testUnion4() {
+		final double right1 = 10.0;
+		final double left1 = 20.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 15.0;
+		final double left2 = 12.0;
+		final double top2 = 35.0;
+		final double bottom2 = 32.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right2) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+	}
+
+
+	/**
+	 * Test a union where both Bound cross the antimeridian but do not cover the planet.
+	 */
+	@Test
+	public final void testUnion5() {
+		final double right1 = -170.0;
+		final double left1 = 175.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = -175.0;
+		final double left2 = 170.0;
+		final double top2 = 35.0;
+		final double bottom2 = 25.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom2) == 0);
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left2) == 0
+		        && Double.compare(b.getTop(), top1) == 0
+		        && Double.compare(b.getBottom(), bottom2) == 0);
+	}
+
+
+	/**
+	 * Test a union where one Bound covers the planet left-right.
+	 */
+	@Test
+	public final void testUnion6() {
+		final double right1 = 180.0;
+		final double left1 = -180.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 15.0;
+		final double left2 = 12.0;
+		final double top2 = 45.0;
+		final double bottom2 = 32.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), right1) == 0
+		        && Double.compare(b.getLeft(), left1) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom1) == 0);
+	}
+
+
+	/**
+	 * Test a union where the Bound overlap and the resulting union covers the planet left-right.
+	 */
+	@Test
+	public final void testUnion7() {
+		final double right1 = 150.0;
+		final double left1 = 170.0;
+		final double top1 = 40.0;
+		final double bottom1 = 30.0;
+		final double right2 = 175.0;
+		final double left2 = 145.0;
+		final double top2 = 45.0;
+		final double bottom2 = 25.0;
+		final double minLongitude = -180.0;
+		final double maxLongitude = 180.0;
+
+		Bound b1 = new Bound(right1, left1, top1, bottom1, "");
+		Bound b2 = new Bound(right2, left2, top2, bottom2, "");
+		Bound b = b1.union(b2);
+		assertTrue(Double.compare(b.getRight(), maxLongitude) == 0
+		        && Double.compare(b.getLeft(), minLongitude) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom2) == 0);
+		// Test it with arguments swapped
+		b = b2.union(b1);
+		assertTrue(Double.compare(b.getRight(), maxLongitude) == 0
+		        && Double.compare(b.getLeft(), minLongitude) == 0
+		        && Double.compare(b.getTop(), top2) == 0
+		        && Double.compare(b.getBottom(), bottom2) == 0);
+	}
+
+
+	/**
+	 * Test the case where the Bound is already "simple" (i.e., doesn't cross the antimeridian).
+	 */
+	@Test
+	public final void testToSimpleBound1() {
+		final double left = -179.0;
+		final double right = 179.0;
+		final double top = 1.0;
+		final double bottom = -1.0;
+		boolean expected1found = false;
+		int cnt = 0;
+		Bound expected1 = new Bound(right, left, top, bottom, "");
+
+		for (Bound b : new Bound(right, left, top, bottom, "").toSimpleBound()) {
+			cnt++;
+			if (Double.compare(b.getRight(), expected1.getRight()) == 0
+			        && Double.compare(b.getLeft(), expected1.getLeft()) == 0
+			        && Double.compare(b.getTop(), expected1.getTop()) == 0
+			        && Double.compare(b.getBottom(), expected1.getBottom()) == 0) {
+				expected1found = true;
+			}
+		}
+		assertTrue(cnt == 1);
+		assertTrue(expected1found);
+	}
+
+
+	/**
+	 * Test the case where the Bound is split into two simple Bound elements, one on either side
+	 * of the antimeridian.
+	 */
+	@Test
+	public final void testToSimpleBound2() {
+		final double left = 179.0;
+		final double right = -179.0;
+		final double top = 1.0;
+		final double bottom = -1.0;
+		final double minLongitude = -180.0;
+		final double maxLongitude = 180.0;
+		boolean expected1found = false, expected2found = false;
+		int cnt = 0;
+		Bound expected1 = new Bound(maxLongitude, left, top, bottom, "");
+		Bound expected2 = new Bound(right, minLongitude, top, bottom, "");
+
+		for (Bound b : new Bound(right, left, top, bottom, "").toSimpleBound()) {
+			cnt++;
+			if (Double.compare(b.getRight(), expected1.getRight()) == 0
+			        && Double.compare(b.getLeft(), expected1.getLeft()) == 0
+			        && Double.compare(b.getTop(), expected1.getTop()) == 0
+			        && Double.compare(b.getBottom(), expected1.getBottom()) == 0) {
+				expected1found = true;
+			}
+			if (Double.compare(b.getRight(), expected2.getRight()) == 0
+			        && Double.compare(b.getLeft(), expected2.getLeft()) == 0
+			        && Double.compare(b.getTop(), expected2.getTop()) == 0
+			        && Double.compare(b.getBottom(), expected2.getBottom()) == 0) {
+				expected2found = true;
+			}
+		}
+
+		assertTrue(cnt == 2);
+		assertTrue(expected1found);
+		assertTrue(expected2found);
+	}
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/CloneTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/CloneTest.java
new file mode 100644
index 0000000..7b6a596
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/CloneTest.java
@@ -0,0 +1,87 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.domain.v0_6;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ * Verifies that read-only entities can be cloned.
+ * 
+ * @author Brett Henderson
+ * 
+ */
+public class CloneTest {
+	/**
+	 * Node cloning test.
+	 */
+	@Test
+	public void testNodeClone() {
+		// Build the original entity.
+		List<Tag> tags = new ArrayList<Tag>();
+		tags.add(new Tag("myKey", "myValue"));
+		Node entity = new Node(new CommonEntityData(1, 2, new Date(0), OsmUser.NONE, 3, tags), 4, 5);
+
+		// Cloning a writeable object should return the original object.
+		Assert.assertSame("Entity was cloned", entity, entity.getWriteableInstance());
+
+		// Get a cloned entity.
+		entity.makeReadOnly();
+		Node clonedEntity = entity.getWriteableInstance();
+
+		// Make sure we weren't assigned the original entity.
+		Assert.assertNotSame("Entity was not cloned", entity, clonedEntity);
+	}
+
+
+	/**
+	 * Way cloning test.
+	 */
+	@Test
+	public void testWayClone() {
+		// Build the original entity.
+		List<Tag> tags = new ArrayList<Tag>();
+		tags.add(new Tag("myKey", "myValue"));
+		List<WayNode> wayNodes = new ArrayList<WayNode>();
+		wayNodes.add(new WayNode(1));
+		Way entity = new Way(new CommonEntityData(1, 2, new Date(0), OsmUser.NONE, 3, tags), wayNodes);
+
+		// Cloning a writeable object should return the original object.
+		Assert.assertSame("Entity was cloned", entity, entity.getWriteableInstance());
+
+		// Get a cloned entity.
+		entity.makeReadOnly();
+		Way clonedEntity = entity.getWriteableInstance();
+
+		// Make sure we weren't assigned the original entity.
+		Assert.assertNotSame("Entity was not cloned", entity, clonedEntity);
+	}
+
+
+	/**
+	 * Relation cloning test.
+	 */
+	@Test
+	public void testRelationClone() {
+		// Build the original entity.
+		List<Tag> tags = new ArrayList<Tag>();
+		tags.add(new Tag("myKey", "myValue"));
+		List<RelationMember> members = new ArrayList<RelationMember>();
+		members.add(new RelationMember(1, EntityType.Node, "myRole"));
+		Relation entity = new Relation(new CommonEntityData(1, 2, new Date(0), OsmUser.NONE, 3, tags), members);
+
+		// Cloning a writeable object should return the original object.
+		Assert.assertSame("Entity was cloned", entity, entity.getWriteableInstance());
+
+		// Get a cloned entity.
+		entity.makeReadOnly();
+		Relation clonedEntity = entity.getWriteableInstance();
+
+		// Make sure we weren't assigned the original entity.
+		Assert.assertNotSame("Entity was not cloned", entity, clonedEntity);
+	}
+}
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUserTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUserTest.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUserTest.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/domain/v0_6/OsmUserTest.java
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTrackerTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTrackerTest.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTrackerTest.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/BitSetIdTrackerTest.java
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerTest.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerTest.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/DynamicIdTrackerTest.java
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerBase.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerBase.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerBase.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/IdTrackerBase.java
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/filter/common/ListIdTrackerTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/ListIdTrackerTest.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/filter/common/ListIdTrackerTest.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/filter/common/ListIdTrackerTest.java
diff --git a/core/src/test/java/org/openstreetmap/osmosis/core/mysql/common/TileCalculatorTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/mysql/common/TileCalculatorTest.java
similarity index 100%
rename from core/src/test/java/org/openstreetmap/osmosis/core/mysql/common/TileCalculatorTest.java
rename to osmosis-core/src/test/java/org/openstreetmap/osmosis/core/mysql/common/TileCalculatorTest.java
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSortTest.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSortTest.java
new file mode 100644
index 0000000..3c7777f
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/FileBasedSortTest.java
@@ -0,0 +1,77 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.common;
+
+import java.util.Comparator;
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+
+
+/**
+ * Tests the {@link FileBasedSort} class.
+ */
+public class FileBasedSortTest {
+
+	/**
+	 * Stores a large number of items into the file-based sorter and verifies
+	 * that they are returned in the correct sequence. It exceeds the in-memory
+	 * sorting limit, but doesn't trigger additional persistence at sub-levels
+	 * in the merge sort to minimise file handles as this would take too much
+	 * time for a unit test. The item count must be greater than
+	 * (MAX_MEMORY_SORT_COUNT * (MAX_MERGE_SOURCE_COUNT ^
+	 * MAX_MEMORY_SORT_DEPTH)) to trigger intermediate persistence.
+	 */
+	@Test
+	public void test() {
+		final long itemCount = 10000;
+
+		// Create a new file-based sorter for the TestStoreable type.
+		SingleClassObjectSerializationFactory objectFactory = new SingleClassObjectSerializationFactory(
+				SampleStoreable.class);
+		Comparator<SampleStoreable> comparator = new Comparator<SampleStoreable>() {
+			@Override
+			public int compare(SampleStoreable o1, SampleStoreable o2) {
+				long value1 = o1.getValue();
+				long value2 = o2.getValue();
+
+				if (value1 > value2) {
+					return 1;
+				} else if (value1 < value2) {
+					return -1;
+				} else {
+					return 0;
+				}
+			}
+		};
+		FileBasedSort<SampleStoreable> fileBasedSort =
+				new FileBasedSort<SampleStoreable>(objectFactory, comparator, true);
+
+		try {
+			// Add randomly generated test values into the sorter.
+			Random random = new Random();
+			for (long i = 0; i < itemCount; i++) {
+				fileBasedSort.add(new SampleStoreable(random.nextInt()));
+			}
+
+			// Read back all values in the sorter and verify that they are
+			// sorted correctly.
+			ReleasableIterator<SampleStoreable> resultIterator = fileBasedSort.iterate();
+			try {
+				int lastValue = Integer.MIN_VALUE;
+				while (resultIterator.hasNext()) {
+					int currentValue = resultIterator.next().getValue();
+					Assert.assertTrue(currentValue >= lastValue);
+					lastValue = currentValue;
+				}
+			} finally {
+				resultIterator.release();
+			}
+
+		} finally {
+			fileBasedSort.release();
+		}
+	}
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/SampleStoreable.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/SampleStoreable.java
new file mode 100644
index 0000000..e23488f
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/core/sort/common/SampleStoreable.java
@@ -0,0 +1,57 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.core.sort.common;
+
+import org.openstreetmap.osmosis.core.store.StoreClassRegister;
+import org.openstreetmap.osmosis.core.store.StoreReader;
+import org.openstreetmap.osmosis.core.store.StoreWriter;
+import org.openstreetmap.osmosis.core.store.Storeable;
+
+
+/**
+ * A simple storeable class for testing {@link FileBasedSort}.
+ */
+public class SampleStoreable implements Storeable {
+
+	private int value;
+
+
+	/**
+	 * Constructs a new instance.
+	 * 
+	 * @param value
+	 *            See {@link #getValue()}.
+	 */
+	public SampleStoreable(int value) {
+		this.value = value;
+	}
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sr
+	 *            The store to read state from.
+	 * @param scr
+	 *            Maintains the mapping between classes and their identifiers
+	 *            within the store.
+	 */
+	public SampleStoreable(StoreReader sr, StoreClassRegister scr) {
+		this.value = sr.readInteger();
+	}
+
+
+	@Override
+	public void store(StoreWriter writer, StoreClassRegister storeClassRegister) {
+		writer.writeInteger(value);
+	}
+
+
+	/**
+	 * The value used for sorting.
+	 * 
+	 * @return The sort value.
+	 */
+	public int getValue() {
+		return value;
+	}
+}
diff --git a/osmosis-core/src/test/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java b/osmosis-core/src/test/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java
new file mode 100644
index 0000000..616ebc1
--- /dev/null
+++ b/osmosis-core/src/test/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.testutil.v0_6;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+/**
+ * Mock object for inspecting the resulting entities after passing through a pipeline task.
+ * 
+ * @author Karl Newman
+ */
+public class SinkEntityInspector implements Sink {
+
+	private List<EntityContainer> processedEntities;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SinkEntityInspector() {
+		processedEntities = new LinkedList<EntityContainer>();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * Catch all passed entities and save them for later inspection.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 */
+	@Override
+	public void process(EntityContainer entityContainer) {
+		processedEntities.add(entityContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * Shortcut method if you only care about the most recent EntityContainer.
+	 * 
+	 * @return the lastEntityContainer
+	 */
+	public EntityContainer getLastEntityContainer() {
+		if (processedEntities.isEmpty()) {
+			return null;
+		} else {
+			return processedEntities.get(processedEntities.size() - 1);
+		}
+	}
+
+
+	/**
+	 * Retrieve an Iterable of all the processed EntityContainers.
+	 * 
+	 * @return the processedEntities
+	 */
+	public Iterable<EntityContainer> getProcessedEntities() {
+		return Collections.unmodifiableList(processedEntities);
+	}
+
+}
diff --git a/core/src/test/resources/data/template/readme.txt b/osmosis-core/src/test/resources/data/template/readme.txt
similarity index 100%
rename from core/src/test/resources/data/template/readme.txt
rename to osmosis-core/src/test/resources/data/template/readme.txt
diff --git a/core/src/test/resources/data/template/v0_6/compressor-bzip2-test.osm.bz2 b/osmosis-core/src/test/resources/data/template/v0_6/compressor-bzip2-test.osm.bz2
similarity index 100%
rename from core/src/test/resources/data/template/v0_6/compressor-bzip2-test.osm.bz2
rename to osmosis-core/src/test/resources/data/template/v0_6/compressor-bzip2-test.osm.bz2
diff --git a/core/src/test/resources/data/template/v0_6/migration-expected.osc b/osmosis-core/src/test/resources/data/template/v0_6/migration-expected.osc
similarity index 100%
rename from core/src/test/resources/data/template/v0_6/migration-expected.osc
rename to osmosis-core/src/test/resources/data/template/v0_6/migration-expected.osc
diff --git a/core/src/test/resources/data/template/v0_6/migration-expected.osm b/osmosis-core/src/test/resources/data/template/v0_6/migration-expected.osm
similarity index 100%
rename from core/src/test/resources/data/template/v0_6/migration-expected.osm
rename to osmosis-core/src/test/resources/data/template/v0_6/migration-expected.osm
diff --git a/core/src/test/resources/data/template/v0_6/rep-changeset.osc b/osmosis-core/src/test/resources/data/template/v0_6/rep-changeset.osc
similarity index 100%
rename from core/src/test/resources/data/template/v0_6/rep-changeset.osc
rename to osmosis-core/src/test/resources/data/template/v0_6/rep-changeset.osc
diff --git a/core/src/test/resources/data/template/v0_6/repdb-authfile.txt b/osmosis-core/src/test/resources/data/template/v0_6/repdb-authfile.txt
similarity index 100%
rename from core/src/test/resources/data/template/v0_6/repdb-authfile.txt
rename to osmosis-core/src/test/resources/data/template/v0_6/repdb-authfile.txt
diff --git a/osmosis-dataset/.checkstyle b/osmosis-dataset/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-dataset/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-dataset/.gitignore b/osmosis-dataset/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-dataset/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-dataset/build.gradle b/osmosis-dataset/build.gradle
new file mode 100644
index 0000000..1a7b210
--- /dev/null
+++ b/osmosis-dataset/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    testCompile project(':osmosis-xml')
+    testCompile project(':osmosis-testutil')
+}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/DatasetPluginLoader.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/DatasetPluginLoader.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/DatasetPluginLoader.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/DatasetPluginLoader.java
diff --git a/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java
new file mode 100644
index 0000000..a00a94d
--- /dev/null
+++ b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilter.java
@@ -0,0 +1,107 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.dataset.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
+import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.DatasetSinkSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * Provides a filter utilising a dataset to extract all entities that lie within
+ * a specific geographical box identified by latitude and longitude coordinates.
+ * 
+ * @author Brett Henderson
+ */
+public class DatasetBoundingBoxFilter implements DatasetSinkSource {
+	private Sink sink;
+	private double left;
+	private double right;
+	private double top;
+	private double bottom;
+	private boolean completeWays;
+	private DatasetContext datasetReader;
+	
+	
+	/**
+	 * Creates a new instance with the specified geographical coordinates. When
+	 * filtering, nodes right on the left and bottom edges of the box will be
+	 * included, nodes on the top and right edges will be excluded.
+	 * 
+	 * @param left
+	 *            The longitude marking the left edge of the bounding box.
+	 * @param right
+	 *            The longitude marking the right edge of the bounding box.
+	 * @param top
+	 *            The latitude marking the top edge of the bounding box.
+	 * @param bottom
+	 *            The latitude marking the bottom edge of the bounding box.
+	 * @param completeWays
+	 *            Include all nodes for ways which have some portion inside the
+	 *            filtered area.
+	 */
+	public DatasetBoundingBoxFilter(double left, double right, double top, double bottom, boolean completeWays) {
+		this.left = left;
+		this.right = right;
+		this.top = top;
+		this.bottom = bottom;
+		this.completeWays = completeWays;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(Dataset dataset) {
+		ReleasableIterator<EntityContainer> bboxData;
+		
+		if (datasetReader != null) {
+			throw new OsmosisRuntimeException("process may only be invoked once.");
+		}
+		
+		datasetReader = dataset.createReader();
+		
+		// Pass all data within the bounding box to the sink.
+		bboxData = datasetReader.iterateBoundingBox(left, right, top, bottom, completeWays);
+		try {
+			sink.initialize(Collections.<String, Object>emptyMap());
+			
+			while (bboxData.hasNext()) {
+				sink.process(bboxData.next());
+			}
+			
+			sink.complete();
+			
+		} finally {
+			bboxData.release();
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		sink.release();
+		
+		if (datasetReader != null) {
+			datasetReader.release();
+		}
+	}
+}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilterFactory.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilterFactory.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilterFactory.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DatasetBoundingBoxFilterFactory.java
diff --git a/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java
new file mode 100644
index 0000000..f51740e
--- /dev/null
+++ b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDataset.java
@@ -0,0 +1,75 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.dataset.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
+import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.task.v0_6.DatasetSinkSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * Reads all data from a dataset.
+ * 
+ * @author Brett Henderson
+ */
+public class DumpDataset implements DatasetSinkSource {
+	private Sink sink;
+	private DatasetContext datasetReader;
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(Dataset dataset) {
+		ReleasableIterator<EntityContainer> bboxData;
+		
+		if (datasetReader != null) {
+			throw new OsmosisRuntimeException("process may only be invoked once.");
+		}
+		
+		datasetReader = dataset.createReader();
+		
+		// Pass all data within the dataset to the sink.
+		bboxData = datasetReader.iterate();
+		try {
+			sink.initialize(Collections.<String, Object>emptyMap());
+			
+			while (bboxData.hasNext()) {
+				sink.process(bboxData.next());
+			}
+			
+			sink.complete();
+			
+		} finally {
+			bboxData.release();
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		sink.release();
+		
+		if (datasetReader != null) {
+			datasetReader.release();
+		}
+	}
+}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDatasetFactory.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDatasetFactory.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDatasetFactory.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/DumpDatasetFactory.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDataset.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDataset.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDataset.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDataset.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDatasetFactory.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDatasetFactory.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDatasetFactory.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/ReadDatasetFactory.java
diff --git a/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java
new file mode 100644
index 0000000..c69ee5e
--- /dev/null
+++ b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDataset.java
@@ -0,0 +1,77 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.dataset.v0_6;
+
+import java.io.File;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.dataset.v0_6.impl.DatasetStore;
+import org.openstreetmap.osmosis.dataset.v0_6.impl.DatasetStoreFileManager;
+import org.openstreetmap.osmosis.dataset.v0_6.impl.PermanentFileDatasetStoreFileManager;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * Receives input data as a stream and builds a dataset containing all of the
+ * data.
+ * 
+ * @author Brett Henderson
+ */
+public class WriteDataset implements Sink {
+	
+	private DatasetStoreFileManager fileManager;
+	private DatasetStore store;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param directory
+	 *            The directory to store all data files in.
+	 * @param enableWayTileIndex
+	 *            If true a tile index is created for ways, otherwise a node-way
+	 *            index is used.
+	 */
+	public WriteDataset(File directory, boolean enableWayTileIndex) {
+		fileManager = new PermanentFileDatasetStoreFileManager(directory);
+		store = new DatasetStore(fileManager, enableWayTileIndex);
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		store.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		store.process(entityContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		store.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		// We must release the store last because downstream tasks must be able
+		// to release store readers first.
+		store.release();
+		
+		// We must release the file manager after the store to ensure all open
+		// files are closed.
+		fileManager.release();
+	}
+}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDatasetFactory.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDatasetFactory.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDatasetFactory.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/WriteDatasetFactory.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/BoundingBoxContext.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/BoundingBoxContext.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/BoundingBoxContext.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/BoundingBoxContext.java
diff --git a/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java
new file mode 100644
index 0000000..2d5589a
--- /dev/null
+++ b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStore.java
@@ -0,0 +1,451 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.dataset.v0_6.impl;
+
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.Dataset;
+import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
+import org.openstreetmap.osmosis.core.store.ComparableComparator;
+import org.openstreetmap.osmosis.core.store.IndexStore;
+import org.openstreetmap.osmosis.core.store.IndexStoreReader;
+import org.openstreetmap.osmosis.core.store.IntegerLongIndexElement;
+import org.openstreetmap.osmosis.core.store.LongLongIndexElement;
+import org.openstreetmap.osmosis.core.store.NoSuchIndexElementException;
+import org.openstreetmap.osmosis.core.store.RandomAccessObjectStore;
+import org.openstreetmap.osmosis.core.store.RandomAccessObjectStoreReader;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.UnsignedIntegerComparator;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.util.TileCalculator;
+
+
+/**
+ * Provides a file based storage mechanism for implementing a dataset.
+ * 
+ * @author Brett Henderson
+ */
+public class DatasetStore implements Sink, EntityProcessor, Dataset {
+	
+	private static final Logger LOG = Logger.getLogger(DatasetStore.class.getName());
+	
+	
+	private SortedEntityPipeValidator sortedPipeValidator;
+	private TileCalculator tileCalculator;
+	private UnsignedIntegerComparator uintComparator;
+	
+	private boolean enableWayTileIndex;
+	
+	private CompletableContainer storeContainer;
+	private RandomAccessObjectStore<Node> nodeObjectStore;
+	private IndexStore<Long, LongLongIndexElement> nodeObjectOffsetIndexWriter;
+	private IndexStore<Integer, IntegerLongIndexElement> nodeTileIndexWriter;
+	private RandomAccessObjectStore<Way> wayObjectStore;
+	private IndexStore<Long, LongLongIndexElement> wayObjectOffsetIndexWriter;
+	private WayTileAreaIndex wayTileIndexWriter;
+	private IndexStore<Long, LongLongIndexElement> nodeWayIndexWriter;
+	private RandomAccessObjectStore<Relation> relationObjectStore;
+	private IndexStore<Long, LongLongIndexElement> relationObjectOffsetIndexWriter;
+	private IndexStore<Long, LongLongIndexElement> nodeRelationIndexWriter;
+	private IndexStore<Long, LongLongIndexElement> wayRelationIndexWriter;
+	private IndexStore<Long, LongLongIndexElement> relationRelationIndexWriter;
+	
+	private RandomAccessObjectStoreReader<Node> nodeObjectReader;
+	private IndexStoreReader<Long, LongLongIndexElement> nodeObjectOffsetIndexReader;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param fileManager
+	 *            The manager providing access to store files.
+	 * @param enableWayTileIndex
+	 *            If true a tile index is created for ways, otherwise a node-way
+	 *            index is used.
+	 */
+	public DatasetStore(DatasetStoreFileManager fileManager, boolean enableWayTileIndex) {
+		this.enableWayTileIndex = enableWayTileIndex;
+		
+		storeContainer = new CompletableContainer();
+		
+		// Validate all input data to ensure it is sorted.
+		sortedPipeValidator = new SortedEntityPipeValidator();
+		sortedPipeValidator.setSink(new Sink() {
+			@Override
+		    public void initialize(Map<String, Object> metaData) {
+				throw new UnsupportedOperationException();
+			}
+			@Override
+			public void complete() {
+				throw new UnsupportedOperationException();
+			}
+			
+			@Override
+			public void process(EntityContainer entityContainer) {
+				processImpl(entityContainer);
+			}
+			
+			@Override
+			public void release() {
+				throw new UnsupportedOperationException();
+			} });
+		
+		tileCalculator = new TileCalculator();
+		uintComparator = new UnsignedIntegerComparator();
+		
+		// Create node store and indexes.
+		nodeObjectStore = storeContainer.add(
+			new RandomAccessObjectStore<Node>(
+				new SingleClassObjectSerializationFactory(Node.class),
+				fileManager.getNodeObjectFile()
+			)
+		);
+		nodeObjectOffsetIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+			LongLongIndexElement.class,
+			new ComparableComparator<Long>(),
+			fileManager.getNodeObjectOffsetIndexFile()
+			)
+		);
+		nodeTileIndexWriter = storeContainer.add(
+			new IndexStore<Integer, IntegerLongIndexElement>(
+			IntegerLongIndexElement.class,
+			uintComparator,
+			fileManager.getNodeTileIndexFile()
+			)
+		);
+		
+		// Create way store and indexes.
+		wayObjectStore = storeContainer.add(
+			new RandomAccessObjectStore<Way>(
+				new SingleClassObjectSerializationFactory(Way.class),
+				fileManager.getWayObjectFile()
+			)
+		);
+		wayObjectOffsetIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getWayObjectOffsetIndexFile()
+			)
+		);
+		wayTileIndexWriter = storeContainer.add(new WayTileAreaIndex(fileManager));
+		nodeWayIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getNodeWayIndexFile()
+			)
+		);
+		
+		// Create relation store and indexes.
+		relationObjectStore = storeContainer.add(
+			new RandomAccessObjectStore<Relation>(
+				new SingleClassObjectSerializationFactory(Relation.class),
+				fileManager.getRelationObjectFile()
+			)
+		);
+		relationObjectOffsetIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getRelationObjectOffsetIndexFile()
+			)
+		);
+		nodeRelationIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getNodeRelationIndexFile()
+			)
+		);
+		wayRelationIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getWayRelationIndexFile()
+			)
+		);
+		relationRelationIndexWriter = storeContainer.add(
+			new IndexStore<Long, LongLongIndexElement>(
+				LongLongIndexElement.class,
+				new ComparableComparator<Long>(),
+				fileManager.getRelationRelationIndexFile()
+			)
+		);
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		sortedPipeValidator.process(entityContainer);
+	}
+	
+	
+	/**
+	 * The entity processing implementation. This must not be called directly,
+	 * it is called by the internal sorted pipe validator.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 */
+	protected void processImpl(EntityContainer entityContainer) {
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void process(BoundContainer bound) {
+        // Do nothing.
+    }
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer nodeContainer) {
+		Node node;
+		long nodeId;
+		long objectOffset;
+		
+		node = nodeContainer.getEntity();
+		nodeId = node.getId();
+		
+		// Write the node to the object store and save the file offset in an
+		// index keyed by node id.
+		objectOffset = nodeObjectStore.add(node);
+		nodeObjectOffsetIndexWriter.write(
+			new LongLongIndexElement(nodeId, objectOffset)
+		);
+		
+		// Write the node id to an index keyed by tile.
+		nodeTileIndexWriter.write(
+			new IntegerLongIndexElement((int) tileCalculator.calculateTile(node.getLatitude(), node.getLongitude()),
+			nodeId)
+		);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		Way way;
+		long wayId;
+		long objectOffset;
+		int minimumTile;
+		int maximumTile;
+		boolean tilesFound;
+		
+		if (nodeObjectReader == null) {
+			nodeObjectStore.complete();
+			nodeObjectReader = nodeObjectStore.createReader();
+		}
+		if (nodeObjectOffsetIndexReader == null) {
+			nodeObjectOffsetIndexWriter.complete();
+			nodeObjectOffsetIndexReader = nodeObjectOffsetIndexWriter.createReader();
+		}
+		
+		way = wayContainer.getEntity();
+		wayId = way.getId();
+		
+		// Write the way to the object store and save the file offset in an
+		// index keyed by way id.
+		objectOffset = wayObjectStore.add(way);
+		wayObjectOffsetIndexWriter.write(
+			new LongLongIndexElement(wayId, objectOffset)
+		);
+		
+		if (enableWayTileIndex) {
+		// Calculate the minimum and maximum tile indexes for the way.
+		tilesFound = false;
+		minimumTile = 0;
+		maximumTile = 0;
+		for (WayNode wayNode : way.getWayNodes()) {
+			long nodeId;
+			Node node;
+			int tile;
+			
+			nodeId = wayNode.getNodeId();
+			
+			try {
+			node = nodeObjectReader.get(
+				nodeObjectOffsetIndexReader.get(nodeId).getValue()
+			);
+			
+			tile = (int) tileCalculator.calculateTile(node.getLatitude(), node.getLongitude());
+			
+			if (tilesFound) {
+				if (uintComparator.compare(tile, minimumTile) < 0) {
+					minimumTile = tile;
+				}
+				if (uintComparator.compare(maximumTile, tile) < 0) {
+					maximumTile = tile;
+				}
+				
+			} else {
+				minimumTile = tile;
+				maximumTile = tile;
+				
+				tilesFound = true;
+			}
+				
+			} catch (NoSuchIndexElementException e) {
+				// Ignore any referential integrity problems.
+				if (LOG.isLoggable(Level.FINER)) {
+					LOG.finest(
+						"Ignoring referential integrity problem where way " + wayId
+						+ " refers to non-existent node " + nodeId + "."
+					);
+		}
+			}
+		}
+		
+		// Write the way id to an index keyed by tile but only if tiles were
+		// actually found.
+		if (tilesFound) {
+		wayTileIndexWriter.write(wayId, minimumTile, maximumTile);
+			}
+			
+		} else {
+			for (WayNode wayNode : way.getWayNodes()) {
+				long nodeId;
+				
+				nodeId = wayNode.getNodeId();
+				
+				nodeWayIndexWriter.write(new LongLongIndexElement(nodeId, wayId));
+			}
+	}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		Relation relation;
+		long relationId;
+		long objectOffset;
+		
+		relation = relationContainer.getEntity();
+		relationId = relation.getId();
+		
+		// Write the relation to the object store and save the file offset in an
+		// index keyed by relation id.
+		objectOffset = relationObjectStore.add(relation);
+		relationObjectOffsetIndexWriter.write(
+			new LongLongIndexElement(relationId, objectOffset)
+		);
+		
+		// Write the relation id to indexes keyed by each of the relation members.
+		for (RelationMember member : relation.getMembers()) {
+			EntityType memberType;
+			
+			memberType = member.getMemberType();
+			
+			if (memberType.equals(EntityType.Node)) {
+				nodeRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
+			} else if (memberType.equals(EntityType.Way)) {
+				wayRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
+			} else if (memberType.equals(EntityType.Relation)) {
+				relationRelationIndexWriter.write(new LongLongIndexElement(member.getMemberId(), relationId));
+			} else {
+				throw new OsmosisRuntimeException("Member type " + memberType + " is not recognised.");
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		// Complete all the stores to ensure their data is fully persisted.
+		storeContainer.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public DatasetContext createReader() {
+		ReleasableContainer releasableContainer = new ReleasableContainer();
+		
+		try {
+			DatasetContext reader;
+			
+			reader = new DatasetStoreReader(
+					new NodeStorageContainer(
+							releasableContainer.add(nodeObjectStore.createReader()),
+							releasableContainer.add(nodeObjectOffsetIndexWriter.createReader()),
+							releasableContainer.add(nodeTileIndexWriter.createReader()),
+							releasableContainer.add(nodeWayIndexWriter.createReader()),
+							releasableContainer.add(nodeRelationIndexWriter.createReader())),
+					new WayStorageContainer(
+							releasableContainer.add(wayObjectStore.createReader()),
+							releasableContainer.add(wayObjectOffsetIndexWriter.createReader()),
+							releasableContainer.add(wayTileIndexWriter.createReader()),
+							releasableContainer.add(wayRelationIndexWriter.createReader())),
+					new RelationStorageContainer(
+							releasableContainer.add(relationObjectStore.createReader()),
+							releasableContainer.add(relationObjectOffsetIndexWriter.createReader()),
+							releasableContainer.add(relationRelationIndexWriter.createReader())),
+					enableWayTileIndex
+			);
+			
+			// Stop the release of all created objects.
+			releasableContainer.clear();
+			
+			return reader;
+			
+		} finally {
+			releasableContainer.release();
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		if (nodeObjectReader != null) {
+			nodeObjectReader.release();
+		}
+		if (nodeObjectOffsetIndexReader != null) {
+			nodeObjectOffsetIndexReader.release();
+		}
+		
+		storeContainer.release();
+	}
+}
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreFileManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreFileManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreFileManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreFileManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreReader.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreReader.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreReader.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/DatasetStoreReader.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeStorageContainer.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeStorageContainer.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeStorageContainer.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/NodeStorageContainer.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/PermanentFileDatasetStoreFileManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/PermanentFileDatasetStoreFileManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/PermanentFileDatasetStoreFileManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/PermanentFileDatasetStoreFileManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationStorageContainer.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationStorageContainer.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationStorageContainer.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationStorageContainer.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationalIndexValueIdIterator.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationalIndexValueIdIterator.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationalIndexValueIdIterator.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/RelationalIndexValueIdIterator.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TempFileDatasetStoreFileManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TempFileDatasetStoreFileManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TempFileDatasetStoreFileManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TempFileDatasetStoreFileManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TileIndexValueIdIterator.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TileIndexValueIdIterator.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TileIndexValueIdIterator.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/TileIndexValueIdIterator.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayManager.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayManager.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayManager.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayManager.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayStorageContainer.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayStorageContainer.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayStorageContainer.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayStorageContainer.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndex.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndex.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndex.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndex.java
diff --git a/dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndexReader.java b/osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndexReader.java
similarity index 100%
rename from dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndexReader.java
rename to osmosis-dataset/src/main/java/org/openstreetmap/osmosis/dataset/v0_6/impl/WayTileAreaIndexReader.java
diff --git a/dataset/src/main/resources/osmosis-plugins.conf b/osmosis-dataset/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from dataset/src/main/resources/osmosis-plugins.conf
rename to osmosis-dataset/src/main/resources/osmosis-plugins.conf
diff --git a/dataset/src/test/java/org/openstreetmap/osmosis/dataset/v0_6/CustomDbTest.java b/osmosis-dataset/src/test/java/org/openstreetmap/osmosis/dataset/v0_6/CustomDbTest.java
similarity index 100%
rename from dataset/src/test/java/org/openstreetmap/osmosis/dataset/v0_6/CustomDbTest.java
rename to osmosis-dataset/src/test/java/org/openstreetmap/osmosis/dataset/v0_6/CustomDbTest.java
diff --git a/dataset/src/test/resources/data/template/v0_6/customdb-snapshot.osm b/osmosis-dataset/src/test/resources/data/template/v0_6/customdb-snapshot.osm
similarity index 100%
rename from dataset/src/test/resources/data/template/v0_6/customdb-snapshot.osm
rename to osmosis-dataset/src/test/resources/data/template/v0_6/customdb-snapshot.osm
diff --git a/osmosis-extract/.checkstyle b/osmosis-extract/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-extract/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-extract/.gitignore b/osmosis-extract/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-extract/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-extract/build.gradle b/osmosis-extract/build.gradle
new file mode 100644
index 0000000..2285e2b
--- /dev/null
+++ b/osmosis-extract/build.gradle
@@ -0,0 +1,7 @@
+dependencies {
+    compile project(':osmosis-apidb')
+    compile project(':osmosis-core')
+    compile project(':osmosis-replication')
+    compile project(':osmosis-xml')
+    testCompile project(':osmosis-testutil')
+}
diff --git a/extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/common/Configuration.java b/osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/common/Configuration.java
similarity index 100%
rename from extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/common/Configuration.java
rename to osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/common/Configuration.java
diff --git a/extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoader.java b/osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoader.java
similarity index 100%
rename from extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoader.java
rename to osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoader.java
diff --git a/extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/IntervalExtractor.java b/osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/IntervalExtractor.java
similarity index 100%
rename from extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/IntervalExtractor.java
rename to osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/IntervalExtractor.java
diff --git a/extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/OsmosisExtractApiDb.java b/osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/OsmosisExtractApiDb.java
similarity index 100%
rename from extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/OsmosisExtractApiDb.java
rename to osmosis-extract/src/main/java/org/openstreetmap/osmosis/extract/apidb/v0_6/OsmosisExtractApiDb.java
diff --git a/extract/src/main/resources/org/openstreetmap/osmosis/extract/apidb/v0_6/osmosis-extract-apidb.conf b/osmosis-extract/src/main/resources/org/openstreetmap/osmosis/extract/apidb/v0_6/osmosis-extract-apidb.conf
similarity index 100%
rename from extract/src/main/resources/org/openstreetmap/osmosis/extract/apidb/v0_6/osmosis-extract-apidb.conf
rename to osmosis-extract/src/main/resources/org/openstreetmap/osmosis/extract/apidb/v0_6/osmosis-extract-apidb.conf
diff --git a/extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoaderTest.java b/osmosis-extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoaderTest.java
similarity index 100%
rename from extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoaderTest.java
rename to osmosis-extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseTimeLoaderTest.java
diff --git a/extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseUtilities.java b/osmosis-extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseUtilities.java
similarity index 100%
rename from extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseUtilities.java
rename to osmosis-extract/src/test/java/org/openstreetmap/osmosis/extract/apidb/v0_6/DatabaseUtilities.java
diff --git a/extract/src/test/resources/data/template/v0_6/apidb-authfile.txt b/osmosis-extract/src/test/resources/data/template/v0_6/apidb-authfile.txt
similarity index 100%
rename from extract/src/test/resources/data/template/v0_6/apidb-authfile.txt
rename to osmosis-extract/src/test/resources/data/template/v0_6/apidb-authfile.txt
diff --git a/osmosis-hstore-jdbc/.checkstyle b/osmosis-hstore-jdbc/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-hstore-jdbc/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-hstore-jdbc/.gitignore b/osmosis-hstore-jdbc/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-hstore-jdbc/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-hstore-jdbc/build.gradle b/osmosis-hstore-jdbc/build.gradle
new file mode 100644
index 0000000..c8f234b
--- /dev/null
+++ b/osmosis-hstore-jdbc/build.gradle
@@ -0,0 +1,6 @@
+dependencies {
+    compile group: 'postgresql', name: 'postgresql', version: dependencyVersionPostgreSql
+}
+
+// Disable checkstyle because this is external code.
+checkstyleMain.enabled = false
diff --git a/hstore-jdbc/src/main/java/org/openstreetmap/osmosis/hstore/PGHStore.java b/osmosis-hstore-jdbc/src/main/java/org/openstreetmap/osmosis/hstore/PGHStore.java
similarity index 100%
rename from hstore-jdbc/src/main/java/org/openstreetmap/osmosis/hstore/PGHStore.java
rename to osmosis-hstore-jdbc/src/main/java/org/openstreetmap/osmosis/hstore/PGHStore.java
diff --git a/hstore-jdbc/src/main/resources/org/postgresql/driverconfig.properties b/osmosis-hstore-jdbc/src/main/resources/org/postgresql/driverconfig.properties
similarity index 100%
rename from hstore-jdbc/src/main/resources/org/postgresql/driverconfig.properties
rename to osmosis-hstore-jdbc/src/main/resources/org/postgresql/driverconfig.properties
diff --git a/osmosis-osm-binary/.gitignore b/osmosis-osm-binary/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-osm-binary/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-osm-binary/COPYING.osmpbf b/osmosis-osm-binary/COPYING.osmpbf
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/osmosis-osm-binary/COPYING.osmpbf
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/osmosis-osm-binary/ReleaseNotes.txt b/osmosis-osm-binary/ReleaseNotes.txt
new file mode 100644
index 0000000..59ae489
--- /dev/null
+++ b/osmosis-osm-binary/ReleaseNotes.txt
@@ -0,0 +1,37 @@
+----------------------------------------
+Release notes for 1.3.0  -- 2012-12-04
+----------------------------------------
+
+Add osmosis replication fields into the header.
+   -- Changes from Frederick Ramm <frederik at remote.org>
+
+
+----------------------------------------
+Release notes for 1.2.1  -- 2011-10-29
+----------------------------------------
+
+--------------------------------------
+Release notes for 1.2.0  -- NOT RELEASED
+--------------------------------------
+
+* Bad tag, so not releasable.
+
+
+* Switched to the lite (non-introspective) version of protocol buffers 
+  for a smaller library.
+* Includes support for "visible" flag on OSM objects. This allows PBF to
+  handle OSM history files.
+* Namespace and include conventions changes for the C++ library. Everything
+  is in the OSMPBF namespace. You now do:
+ #include <osmpbf/osmpbf.h>
+* Added stuff needed to build Debian/Ubuntu libosmpbf-dev package containing
+  the C++ library.
+* Added osmpbf-outline tool that shows internals of a PBF file for debugging.
+* Added magic file that can recognize OSM PBF files.
+
+   --  Changes from Jochen Topf <jochen at topf.org>
+                    Peter <github at mazdermind.de>
+
+* Added a pom.xml
+
+   --  Changes from Zsombor Welker <flaktack at welker.hu>
diff --git a/osmosis-osm-binary/build.gradle b/osmosis-osm-binary/build.gradle
new file mode 100644
index 0000000..b925f69
--- /dev/null
+++ b/osmosis-osm-binary/build.gradle
@@ -0,0 +1,29 @@
+dependencies {
+    compile group: 'com.google.protobuf', name: 'protobuf-java', version: dependencyVersionProtobuf
+}
+
+// Disable checkstyle because this is external code.
+checkstyleMain.enabled = false
+
+sourceSets {
+	main {
+		// Create a separate source directory to contain protobuf generated classes.
+		java.srcDirs 'gen-src/main/java'
+	}
+}
+
+// Configure the maven plugin to upload artefacts to the Sonatype repository.
+uploadArchives {
+	repositories {
+		mavenDeployer {
+			pom.project {
+				licenses {
+					license {
+						name 'LGPL 3'
+						url 'http://www.gnu.org/licenses/lgpl-3.0.html'
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Fileformat.java b/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Fileformat.java
new file mode 100644
index 0000000..8ce1359
--- /dev/null
+++ b/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Fileformat.java
@@ -0,0 +1,992 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: src/main/protobuf/fileformat.proto
+
+package org.openstreetmap.osmosis.osmbinary;
+
+public final class Fileformat {
+  private Fileformat() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+  public interface BlobOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // optional bytes raw = 1;
+    boolean hasRaw();
+    com.google.protobuf.ByteString getRaw();
+    
+    // optional int32 raw_size = 2;
+    boolean hasRawSize();
+    int getRawSize();
+    
+    // optional bytes zlib_data = 3;
+    boolean hasZlibData();
+    com.google.protobuf.ByteString getZlibData();
+    
+    // optional bytes lzma_data = 4;
+    boolean hasLzmaData();
+    com.google.protobuf.ByteString getLzmaData();
+    
+    // optional bytes OBSOLETE_bzip2_data = 5 [deprecated = true];
+    @java.lang.Deprecated boolean hasOBSOLETEBzip2Data();
+    @java.lang.Deprecated com.google.protobuf.ByteString getOBSOLETEBzip2Data();
+  }
+  public static final class Blob extends
+      com.google.protobuf.GeneratedMessageLite
+      implements BlobOrBuilder {
+    // Use Blob.newBuilder() to construct.
+    private Blob(Builder builder) {
+      super(builder);
+    }
+    private Blob(boolean noInit) {}
+    
+    private static final Blob defaultInstance;
+    public static Blob getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Blob getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // optional bytes raw = 1;
+    public static final int RAW_FIELD_NUMBER = 1;
+    private com.google.protobuf.ByteString raw_;
+    public boolean hasRaw() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public com.google.protobuf.ByteString getRaw() {
+      return raw_;
+    }
+    
+    // optional int32 raw_size = 2;
+    public static final int RAW_SIZE_FIELD_NUMBER = 2;
+    private int rawSize_;
+    public boolean hasRawSize() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public int getRawSize() {
+      return rawSize_;
+    }
+    
+    // optional bytes zlib_data = 3;
+    public static final int ZLIB_DATA_FIELD_NUMBER = 3;
+    private com.google.protobuf.ByteString zlibData_;
+    public boolean hasZlibData() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public com.google.protobuf.ByteString getZlibData() {
+      return zlibData_;
+    }
+    
+    // optional bytes lzma_data = 4;
+    public static final int LZMA_DATA_FIELD_NUMBER = 4;
+    private com.google.protobuf.ByteString lzmaData_;
+    public boolean hasLzmaData() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public com.google.protobuf.ByteString getLzmaData() {
+      return lzmaData_;
+    }
+    
+    // optional bytes OBSOLETE_bzip2_data = 5 [deprecated = true];
+    public static final int OBSOLETE_BZIP2_DATA_FIELD_NUMBER = 5;
+    private com.google.protobuf.ByteString oBSOLETEBzip2Data_;
+    @java.lang.Deprecated public boolean hasOBSOLETEBzip2Data() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    @java.lang.Deprecated public com.google.protobuf.ByteString getOBSOLETEBzip2Data() {
+      return oBSOLETEBzip2Data_;
+    }
+    
+    private void initFields() {
+      raw_ = com.google.protobuf.ByteString.EMPTY;
+      rawSize_ = 0;
+      zlibData_ = com.google.protobuf.ByteString.EMPTY;
+      lzmaData_ = com.google.protobuf.ByteString.EMPTY;
+      oBSOLETEBzip2Data_ = com.google.protobuf.ByteString.EMPTY;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeBytes(1, raw_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt32(2, rawSize_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeBytes(3, zlibData_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeBytes(4, lzmaData_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeBytes(5, oBSOLETEBzip2Data_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(1, raw_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(2, rawSize_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(3, zlibData_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(4, lzmaData_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(5, oBSOLETEBzip2Data_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.Blob parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Fileformat.Blob prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Fileformat.Blob, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Fileformat.BlobOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Fileformat.Blob.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        raw_ = com.google.protobuf.ByteString.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        rawSize_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        zlibData_ = com.google.protobuf.ByteString.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        lzmaData_ = com.google.protobuf.ByteString.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        oBSOLETEBzip2Data_ = com.google.protobuf.ByteString.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.Blob getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Fileformat.Blob.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.Blob build() {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.Blob result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Fileformat.Blob buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.Blob result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.Blob buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.Blob result = new org.openstreetmap.osmosis.osmbinary.Fileformat.Blob(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.raw_ = raw_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.rawSize_ = rawSize_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.zlibData_ = zlibData_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.lzmaData_ = lzmaData_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.oBSOLETEBzip2Data_ = oBSOLETEBzip2Data_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Fileformat.Blob other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Fileformat.Blob.getDefaultInstance()) return this;
+        if (other.hasRaw()) {
+          setRaw(other.getRaw());
+        }
+        if (other.hasRawSize()) {
+          setRawSize(other.getRawSize());
+        }
+        if (other.hasZlibData()) {
+          setZlibData(other.getZlibData());
+        }
+        if (other.hasLzmaData()) {
+          setLzmaData(other.getLzmaData());
+        }
+        if (other.hasOBSOLETEBzip2Data()) {
+          setOBSOLETEBzip2Data(other.getOBSOLETEBzip2Data());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              bitField0_ |= 0x00000001;
+              raw_ = input.readBytes();
+              break;
+            }
+            case 16: {
+              bitField0_ |= 0x00000002;
+              rawSize_ = input.readInt32();
+              break;
+            }
+            case 26: {
+              bitField0_ |= 0x00000004;
+              zlibData_ = input.readBytes();
+              break;
+            }
+            case 34: {
+              bitField0_ |= 0x00000008;
+              lzmaData_ = input.readBytes();
+              break;
+            }
+            case 42: {
+              bitField0_ |= 0x00000010;
+              oBSOLETEBzip2Data_ = input.readBytes();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // optional bytes raw = 1;
+      private com.google.protobuf.ByteString raw_ = com.google.protobuf.ByteString.EMPTY;
+      public boolean hasRaw() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public com.google.protobuf.ByteString getRaw() {
+        return raw_;
+      }
+      public Builder setRaw(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        raw_ = value;
+        
+        return this;
+      }
+      public Builder clearRaw() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        raw_ = getDefaultInstance().getRaw();
+        
+        return this;
+      }
+      
+      // optional int32 raw_size = 2;
+      private int rawSize_ ;
+      public boolean hasRawSize() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public int getRawSize() {
+        return rawSize_;
+      }
+      public Builder setRawSize(int value) {
+        bitField0_ |= 0x00000002;
+        rawSize_ = value;
+        
+        return this;
+      }
+      public Builder clearRawSize() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        rawSize_ = 0;
+        
+        return this;
+      }
+      
+      // optional bytes zlib_data = 3;
+      private com.google.protobuf.ByteString zlibData_ = com.google.protobuf.ByteString.EMPTY;
+      public boolean hasZlibData() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      public com.google.protobuf.ByteString getZlibData() {
+        return zlibData_;
+      }
+      public Builder setZlibData(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000004;
+        zlibData_ = value;
+        
+        return this;
+      }
+      public Builder clearZlibData() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        zlibData_ = getDefaultInstance().getZlibData();
+        
+        return this;
+      }
+      
+      // optional bytes lzma_data = 4;
+      private com.google.protobuf.ByteString lzmaData_ = com.google.protobuf.ByteString.EMPTY;
+      public boolean hasLzmaData() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public com.google.protobuf.ByteString getLzmaData() {
+        return lzmaData_;
+      }
+      public Builder setLzmaData(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000008;
+        lzmaData_ = value;
+        
+        return this;
+      }
+      public Builder clearLzmaData() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        lzmaData_ = getDefaultInstance().getLzmaData();
+        
+        return this;
+      }
+      
+      // optional bytes OBSOLETE_bzip2_data = 5 [deprecated = true];
+      private com.google.protobuf.ByteString oBSOLETEBzip2Data_ = com.google.protobuf.ByteString.EMPTY;
+      @java.lang.Deprecated public boolean hasOBSOLETEBzip2Data() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      @java.lang.Deprecated public com.google.protobuf.ByteString getOBSOLETEBzip2Data() {
+        return oBSOLETEBzip2Data_;
+      }
+      @java.lang.Deprecated public Builder setOBSOLETEBzip2Data(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000010;
+        oBSOLETEBzip2Data_ = value;
+        
+        return this;
+      }
+      @java.lang.Deprecated public Builder clearOBSOLETEBzip2Data() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        oBSOLETEBzip2Data_ = getDefaultInstance().getOBSOLETEBzip2Data();
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.Blob)
+    }
+    
+    static {
+      defaultInstance = new Blob(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.Blob)
+  }
+  
+  public interface BlobHeaderOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required string type = 1;
+    boolean hasType();
+    String getType();
+    
+    // optional bytes indexdata = 2;
+    boolean hasIndexdata();
+    com.google.protobuf.ByteString getIndexdata();
+    
+    // required int32 datasize = 3;
+    boolean hasDatasize();
+    int getDatasize();
+  }
+  public static final class BlobHeader extends
+      com.google.protobuf.GeneratedMessageLite
+      implements BlobHeaderOrBuilder {
+    // Use BlobHeader.newBuilder() to construct.
+    private BlobHeader(Builder builder) {
+      super(builder);
+    }
+    private BlobHeader(boolean noInit) {}
+    
+    private static final BlobHeader defaultInstance;
+    public static BlobHeader getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public BlobHeader getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required string type = 1;
+    public static final int TYPE_FIELD_NUMBER = 1;
+    private java.lang.Object type_;
+    public boolean hasType() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public String getType() {
+      java.lang.Object ref = type_;
+      if (ref instanceof String) {
+        return (String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        String s = bs.toStringUtf8();
+        if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+          type_ = s;
+        }
+        return s;
+      }
+    }
+    private com.google.protobuf.ByteString getTypeBytes() {
+      java.lang.Object ref = type_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+        type_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    
+    // optional bytes indexdata = 2;
+    public static final int INDEXDATA_FIELD_NUMBER = 2;
+    private com.google.protobuf.ByteString indexdata_;
+    public boolean hasIndexdata() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public com.google.protobuf.ByteString getIndexdata() {
+      return indexdata_;
+    }
+    
+    // required int32 datasize = 3;
+    public static final int DATASIZE_FIELD_NUMBER = 3;
+    private int datasize_;
+    public boolean hasDatasize() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public int getDatasize() {
+      return datasize_;
+    }
+    
+    private void initFields() {
+      type_ = "";
+      indexdata_ = com.google.protobuf.ByteString.EMPTY;
+      datasize_ = 0;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasType()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasDatasize()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeBytes(1, getTypeBytes());
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeBytes(2, indexdata_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt32(3, datasize_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(1, getTypeBytes());
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(2, indexdata_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(3, datasize_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeaderOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        type_ = "";
+        bitField0_ = (bitField0_ & ~0x00000001);
+        indexdata_ = com.google.protobuf.ByteString.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        datasize_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader build() {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader result = new org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.type_ = type_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.indexdata_ = indexdata_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.datasize_ = datasize_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader.getDefaultInstance()) return this;
+        if (other.hasType()) {
+          setType(other.getType());
+        }
+        if (other.hasIndexdata()) {
+          setIndexdata(other.getIndexdata());
+        }
+        if (other.hasDatasize()) {
+          setDatasize(other.getDatasize());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasType()) {
+          
+          return false;
+        }
+        if (!hasDatasize()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              bitField0_ |= 0x00000001;
+              type_ = input.readBytes();
+              break;
+            }
+            case 18: {
+              bitField0_ |= 0x00000002;
+              indexdata_ = input.readBytes();
+              break;
+            }
+            case 24: {
+              bitField0_ |= 0x00000004;
+              datasize_ = input.readInt32();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required string type = 1;
+      private java.lang.Object type_ = "";
+      public boolean hasType() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public String getType() {
+        java.lang.Object ref = type_;
+        if (!(ref instanceof String)) {
+          String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+          type_ = s;
+          return s;
+        } else {
+          return (String) ref;
+        }
+      }
+      public Builder setType(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000001;
+        type_ = value;
+        
+        return this;
+      }
+      public Builder clearType() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        type_ = getDefaultInstance().getType();
+        
+        return this;
+      }
+      void setType(com.google.protobuf.ByteString value) {
+        bitField0_ |= 0x00000001;
+        type_ = value;
+        
+      }
+      
+      // optional bytes indexdata = 2;
+      private com.google.protobuf.ByteString indexdata_ = com.google.protobuf.ByteString.EMPTY;
+      public boolean hasIndexdata() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public com.google.protobuf.ByteString getIndexdata() {
+        return indexdata_;
+      }
+      public Builder setIndexdata(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000002;
+        indexdata_ = value;
+        
+        return this;
+      }
+      public Builder clearIndexdata() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        indexdata_ = getDefaultInstance().getIndexdata();
+        
+        return this;
+      }
+      
+      // required int32 datasize = 3;
+      private int datasize_ ;
+      public boolean hasDatasize() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      public int getDatasize() {
+        return datasize_;
+      }
+      public Builder setDatasize(int value) {
+        bitField0_ |= 0x00000004;
+        datasize_ = value;
+        
+        return this;
+      }
+      public Builder clearDatasize() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        datasize_ = 0;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.BlobHeader)
+    }
+    
+    static {
+      defaultInstance = new BlobHeader(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.BlobHeader)
+  }
+  
+  
+  static {
+  }
+  
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Osmformat.java b/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Osmformat.java
new file mode 100644
index 0000000..2e461de
--- /dev/null
+++ b/osmosis-osm-binary/gen-src/main/java/org/openstreetmap/osmosis/osmbinary/Osmformat.java
@@ -0,0 +1,8472 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: src/main/protobuf/osmformat.proto
+
+package org.openstreetmap.osmosis.osmbinary;
+
+public final class Osmformat {
+  private Osmformat() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistryLite registry) {
+  }
+  public interface HeaderBlockOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // optional .OSMPBF.HeaderBBox bbox = 1;
+    boolean hasBbox();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox getBbox();
+    
+    // repeated string required_features = 4;
+    java.util.List<String> getRequiredFeaturesList();
+    int getRequiredFeaturesCount();
+    String getRequiredFeatures(int index);
+    
+    // repeated string optional_features = 5;
+    java.util.List<String> getOptionalFeaturesList();
+    int getOptionalFeaturesCount();
+    String getOptionalFeatures(int index);
+    
+    // optional string writingprogram = 16;
+    boolean hasWritingprogram();
+    String getWritingprogram();
+    
+    // optional string source = 17;
+    boolean hasSource();
+    String getSource();
+    
+    // optional int64 osmosis_replication_timestamp = 32;
+    boolean hasOsmosisReplicationTimestamp();
+    long getOsmosisReplicationTimestamp();
+    
+    // optional int64 osmosis_replication_sequence_number = 33;
+    boolean hasOsmosisReplicationSequenceNumber();
+    long getOsmosisReplicationSequenceNumber();
+    
+    // optional string osmosis_replication_base_url = 34;
+    boolean hasOsmosisReplicationBaseUrl();
+    String getOsmosisReplicationBaseUrl();
+  }
+  public static final class HeaderBlock extends
+      com.google.protobuf.GeneratedMessageLite
+      implements HeaderBlockOrBuilder {
+    // Use HeaderBlock.newBuilder() to construct.
+    private HeaderBlock(Builder builder) {
+      super(builder);
+    }
+    private HeaderBlock(boolean noInit) {}
+    
+    private static final HeaderBlock defaultInstance;
+    public static HeaderBlock getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public HeaderBlock getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // optional .OSMPBF.HeaderBBox bbox = 1;
+    public static final int BBOX_FIELD_NUMBER = 1;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox bbox_;
+    public boolean hasBbox() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox getBbox() {
+      return bbox_;
+    }
+    
+    // repeated string required_features = 4;
+    public static final int REQUIRED_FEATURES_FIELD_NUMBER = 4;
+    private com.google.protobuf.LazyStringList requiredFeatures_;
+    public java.util.List<String>
+        getRequiredFeaturesList() {
+      return requiredFeatures_;
+    }
+    public int getRequiredFeaturesCount() {
+      return requiredFeatures_.size();
+    }
+    public String getRequiredFeatures(int index) {
+      return requiredFeatures_.get(index);
+    }
+    
+    // repeated string optional_features = 5;
+    public static final int OPTIONAL_FEATURES_FIELD_NUMBER = 5;
+    private com.google.protobuf.LazyStringList optionalFeatures_;
+    public java.util.List<String>
+        getOptionalFeaturesList() {
+      return optionalFeatures_;
+    }
+    public int getOptionalFeaturesCount() {
+      return optionalFeatures_.size();
+    }
+    public String getOptionalFeatures(int index) {
+      return optionalFeatures_.get(index);
+    }
+    
+    // optional string writingprogram = 16;
+    public static final int WRITINGPROGRAM_FIELD_NUMBER = 16;
+    private java.lang.Object writingprogram_;
+    public boolean hasWritingprogram() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public String getWritingprogram() {
+      java.lang.Object ref = writingprogram_;
+      if (ref instanceof String) {
+        return (String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        String s = bs.toStringUtf8();
+        if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+          writingprogram_ = s;
+        }
+        return s;
+      }
+    }
+    private com.google.protobuf.ByteString getWritingprogramBytes() {
+      java.lang.Object ref = writingprogram_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+        writingprogram_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    
+    // optional string source = 17;
+    public static final int SOURCE_FIELD_NUMBER = 17;
+    private java.lang.Object source_;
+    public boolean hasSource() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public String getSource() {
+      java.lang.Object ref = source_;
+      if (ref instanceof String) {
+        return (String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        String s = bs.toStringUtf8();
+        if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+          source_ = s;
+        }
+        return s;
+      }
+    }
+    private com.google.protobuf.ByteString getSourceBytes() {
+      java.lang.Object ref = source_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+        source_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    
+    // optional int64 osmosis_replication_timestamp = 32;
+    public static final int OSMOSIS_REPLICATION_TIMESTAMP_FIELD_NUMBER = 32;
+    private long osmosisReplicationTimestamp_;
+    public boolean hasOsmosisReplicationTimestamp() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public long getOsmosisReplicationTimestamp() {
+      return osmosisReplicationTimestamp_;
+    }
+    
+    // optional int64 osmosis_replication_sequence_number = 33;
+    public static final int OSMOSIS_REPLICATION_SEQUENCE_NUMBER_FIELD_NUMBER = 33;
+    private long osmosisReplicationSequenceNumber_;
+    public boolean hasOsmosisReplicationSequenceNumber() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    public long getOsmosisReplicationSequenceNumber() {
+      return osmosisReplicationSequenceNumber_;
+    }
+    
+    // optional string osmosis_replication_base_url = 34;
+    public static final int OSMOSIS_REPLICATION_BASE_URL_FIELD_NUMBER = 34;
+    private java.lang.Object osmosisReplicationBaseUrl_;
+    public boolean hasOsmosisReplicationBaseUrl() {
+      return ((bitField0_ & 0x00000020) == 0x00000020);
+    }
+    public String getOsmosisReplicationBaseUrl() {
+      java.lang.Object ref = osmosisReplicationBaseUrl_;
+      if (ref instanceof String) {
+        return (String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        String s = bs.toStringUtf8();
+        if (com.google.protobuf.Internal.isValidUtf8(bs)) {
+          osmosisReplicationBaseUrl_ = s;
+        }
+        return s;
+      }
+    }
+    private com.google.protobuf.ByteString getOsmosisReplicationBaseUrlBytes() {
+      java.lang.Object ref = osmosisReplicationBaseUrl_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8((String) ref);
+        osmosisReplicationBaseUrl_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    
+    private void initFields() {
+      bbox_ = org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance();
+      requiredFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+      optionalFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+      writingprogram_ = "";
+      source_ = "";
+      osmosisReplicationTimestamp_ = 0L;
+      osmosisReplicationSequenceNumber_ = 0L;
+      osmosisReplicationBaseUrl_ = "";
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (hasBbox()) {
+        if (!getBbox().isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(1, bbox_);
+      }
+      for (int i = 0; i < requiredFeatures_.size(); i++) {
+        output.writeBytes(4, requiredFeatures_.getByteString(i));
+      }
+      for (int i = 0; i < optionalFeatures_.size(); i++) {
+        output.writeBytes(5, optionalFeatures_.getByteString(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeBytes(16, getWritingprogramBytes());
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeBytes(17, getSourceBytes());
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeInt64(32, osmosisReplicationTimestamp_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeInt64(33, osmosisReplicationSequenceNumber_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        output.writeBytes(34, getOsmosisReplicationBaseUrlBytes());
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, bbox_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < requiredFeatures_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeBytesSizeNoTag(requiredFeatures_.getByteString(i));
+        }
+        size += dataSize;
+        size += 1 * getRequiredFeaturesList().size();
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < optionalFeatures_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeBytesSizeNoTag(optionalFeatures_.getByteString(i));
+        }
+        size += dataSize;
+        size += 1 * getOptionalFeaturesList().size();
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(16, getWritingprogramBytes());
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(17, getSourceBytes());
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(32, osmosisReplicationTimestamp_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(33, osmosisReplicationSequenceNumber_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(34, getOsmosisReplicationBaseUrlBytes());
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlockOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        bbox_ = org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        requiredFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        optionalFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        writingprogram_ = "";
+        bitField0_ = (bitField0_ & ~0x00000008);
+        source_ = "";
+        bitField0_ = (bitField0_ & ~0x00000010);
+        osmosisReplicationTimestamp_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        osmosisReplicationSequenceNumber_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000040);
+        osmosisReplicationBaseUrl_ = "";
+        bitField0_ = (bitField0_ & ~0x00000080);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock result = new org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.bbox_ = bbox_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          requiredFeatures_ = new com.google.protobuf.UnmodifiableLazyStringList(
+              requiredFeatures_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.requiredFeatures_ = requiredFeatures_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          optionalFeatures_ = new com.google.protobuf.UnmodifiableLazyStringList(
+              optionalFeatures_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.optionalFeatures_ = optionalFeatures_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.writingprogram_ = writingprogram_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.source_ = source_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.osmosisReplicationTimestamp_ = osmosisReplicationTimestamp_;
+        if (((from_bitField0_ & 0x00000040) == 0x00000040)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.osmosisReplicationSequenceNumber_ = osmosisReplicationSequenceNumber_;
+        if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
+          to_bitField0_ |= 0x00000020;
+        }
+        result.osmosisReplicationBaseUrl_ = osmosisReplicationBaseUrl_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock.getDefaultInstance()) return this;
+        if (other.hasBbox()) {
+          mergeBbox(other.getBbox());
+        }
+        if (!other.requiredFeatures_.isEmpty()) {
+          if (requiredFeatures_.isEmpty()) {
+            requiredFeatures_ = other.requiredFeatures_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensureRequiredFeaturesIsMutable();
+            requiredFeatures_.addAll(other.requiredFeatures_);
+          }
+          
+        }
+        if (!other.optionalFeatures_.isEmpty()) {
+          if (optionalFeatures_.isEmpty()) {
+            optionalFeatures_ = other.optionalFeatures_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureOptionalFeaturesIsMutable();
+            optionalFeatures_.addAll(other.optionalFeatures_);
+          }
+          
+        }
+        if (other.hasWritingprogram()) {
+          setWritingprogram(other.getWritingprogram());
+        }
+        if (other.hasSource()) {
+          setSource(other.getSource());
+        }
+        if (other.hasOsmosisReplicationTimestamp()) {
+          setOsmosisReplicationTimestamp(other.getOsmosisReplicationTimestamp());
+        }
+        if (other.hasOsmosisReplicationSequenceNumber()) {
+          setOsmosisReplicationSequenceNumber(other.getOsmosisReplicationSequenceNumber());
+        }
+        if (other.hasOsmosisReplicationBaseUrl()) {
+          setOsmosisReplicationBaseUrl(other.getOsmosisReplicationBaseUrl());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (hasBbox()) {
+          if (!getBbox().isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.newBuilder();
+              if (hasBbox()) {
+                subBuilder.mergeFrom(getBbox());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setBbox(subBuilder.buildPartial());
+              break;
+            }
+            case 34: {
+              ensureRequiredFeaturesIsMutable();
+              requiredFeatures_.add(input.readBytes());
+              break;
+            }
+            case 42: {
+              ensureOptionalFeaturesIsMutable();
+              optionalFeatures_.add(input.readBytes());
+              break;
+            }
+            case 130: {
+              bitField0_ |= 0x00000008;
+              writingprogram_ = input.readBytes();
+              break;
+            }
+            case 138: {
+              bitField0_ |= 0x00000010;
+              source_ = input.readBytes();
+              break;
+            }
+            case 256: {
+              bitField0_ |= 0x00000020;
+              osmosisReplicationTimestamp_ = input.readInt64();
+              break;
+            }
+            case 264: {
+              bitField0_ |= 0x00000040;
+              osmosisReplicationSequenceNumber_ = input.readInt64();
+              break;
+            }
+            case 274: {
+              bitField0_ |= 0x00000080;
+              osmosisReplicationBaseUrl_ = input.readBytes();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // optional .OSMPBF.HeaderBBox bbox = 1;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox bbox_ = org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance();
+      public boolean hasBbox() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox getBbox() {
+        return bbox_;
+      }
+      public Builder setBbox(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        bbox_ = value;
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder setBbox(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.Builder builderForValue) {
+        bbox_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder mergeBbox(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox value) {
+        if (((bitField0_ & 0x00000001) == 0x00000001) &&
+            bbox_ != org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance()) {
+          bbox_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.newBuilder(bbox_).mergeFrom(value).buildPartial();
+        } else {
+          bbox_ = value;
+        }
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder clearBbox() {
+        bbox_ = org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+      
+      // repeated string required_features = 4;
+      private com.google.protobuf.LazyStringList requiredFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+      private void ensureRequiredFeaturesIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          requiredFeatures_ = new com.google.protobuf.LazyStringArrayList(requiredFeatures_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      public java.util.List<String>
+          getRequiredFeaturesList() {
+        return java.util.Collections.unmodifiableList(requiredFeatures_);
+      }
+      public int getRequiredFeaturesCount() {
+        return requiredFeatures_.size();
+      }
+      public String getRequiredFeatures(int index) {
+        return requiredFeatures_.get(index);
+      }
+      public Builder setRequiredFeatures(
+          int index, String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureRequiredFeaturesIsMutable();
+        requiredFeatures_.set(index, value);
+        
+        return this;
+      }
+      public Builder addRequiredFeatures(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureRequiredFeaturesIsMutable();
+        requiredFeatures_.add(value);
+        
+        return this;
+      }
+      public Builder addAllRequiredFeatures(
+          java.lang.Iterable<String> values) {
+        ensureRequiredFeaturesIsMutable();
+        super.addAll(values, requiredFeatures_);
+        
+        return this;
+      }
+      public Builder clearRequiredFeatures() {
+        requiredFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      void addRequiredFeatures(com.google.protobuf.ByteString value) {
+        ensureRequiredFeaturesIsMutable();
+        requiredFeatures_.add(value);
+        
+      }
+      
+      // repeated string optional_features = 5;
+      private com.google.protobuf.LazyStringList optionalFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+      private void ensureOptionalFeaturesIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          optionalFeatures_ = new com.google.protobuf.LazyStringArrayList(optionalFeatures_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<String>
+          getOptionalFeaturesList() {
+        return java.util.Collections.unmodifiableList(optionalFeatures_);
+      }
+      public int getOptionalFeaturesCount() {
+        return optionalFeatures_.size();
+      }
+      public String getOptionalFeatures(int index) {
+        return optionalFeatures_.get(index);
+      }
+      public Builder setOptionalFeatures(
+          int index, String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureOptionalFeaturesIsMutable();
+        optionalFeatures_.set(index, value);
+        
+        return this;
+      }
+      public Builder addOptionalFeatures(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureOptionalFeaturesIsMutable();
+        optionalFeatures_.add(value);
+        
+        return this;
+      }
+      public Builder addAllOptionalFeatures(
+          java.lang.Iterable<String> values) {
+        ensureOptionalFeaturesIsMutable();
+        super.addAll(values, optionalFeatures_);
+        
+        return this;
+      }
+      public Builder clearOptionalFeatures() {
+        optionalFeatures_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      void addOptionalFeatures(com.google.protobuf.ByteString value) {
+        ensureOptionalFeaturesIsMutable();
+        optionalFeatures_.add(value);
+        
+      }
+      
+      // optional string writingprogram = 16;
+      private java.lang.Object writingprogram_ = "";
+      public boolean hasWritingprogram() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public String getWritingprogram() {
+        java.lang.Object ref = writingprogram_;
+        if (!(ref instanceof String)) {
+          String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+          writingprogram_ = s;
+          return s;
+        } else {
+          return (String) ref;
+        }
+      }
+      public Builder setWritingprogram(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000008;
+        writingprogram_ = value;
+        
+        return this;
+      }
+      public Builder clearWritingprogram() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        writingprogram_ = getDefaultInstance().getWritingprogram();
+        
+        return this;
+      }
+      void setWritingprogram(com.google.protobuf.ByteString value) {
+        bitField0_ |= 0x00000008;
+        writingprogram_ = value;
+        
+      }
+      
+      // optional string source = 17;
+      private java.lang.Object source_ = "";
+      public boolean hasSource() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      public String getSource() {
+        java.lang.Object ref = source_;
+        if (!(ref instanceof String)) {
+          String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+          source_ = s;
+          return s;
+        } else {
+          return (String) ref;
+        }
+      }
+      public Builder setSource(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000010;
+        source_ = value;
+        
+        return this;
+      }
+      public Builder clearSource() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        source_ = getDefaultInstance().getSource();
+        
+        return this;
+      }
+      void setSource(com.google.protobuf.ByteString value) {
+        bitField0_ |= 0x00000010;
+        source_ = value;
+        
+      }
+      
+      // optional int64 osmosis_replication_timestamp = 32;
+      private long osmosisReplicationTimestamp_ ;
+      public boolean hasOsmosisReplicationTimestamp() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      public long getOsmosisReplicationTimestamp() {
+        return osmosisReplicationTimestamp_;
+      }
+      public Builder setOsmosisReplicationTimestamp(long value) {
+        bitField0_ |= 0x00000020;
+        osmosisReplicationTimestamp_ = value;
+        
+        return this;
+      }
+      public Builder clearOsmosisReplicationTimestamp() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        osmosisReplicationTimestamp_ = 0L;
+        
+        return this;
+      }
+      
+      // optional int64 osmosis_replication_sequence_number = 33;
+      private long osmosisReplicationSequenceNumber_ ;
+      public boolean hasOsmosisReplicationSequenceNumber() {
+        return ((bitField0_ & 0x00000040) == 0x00000040);
+      }
+      public long getOsmosisReplicationSequenceNumber() {
+        return osmosisReplicationSequenceNumber_;
+      }
+      public Builder setOsmosisReplicationSequenceNumber(long value) {
+        bitField0_ |= 0x00000040;
+        osmosisReplicationSequenceNumber_ = value;
+        
+        return this;
+      }
+      public Builder clearOsmosisReplicationSequenceNumber() {
+        bitField0_ = (bitField0_ & ~0x00000040);
+        osmosisReplicationSequenceNumber_ = 0L;
+        
+        return this;
+      }
+      
+      // optional string osmosis_replication_base_url = 34;
+      private java.lang.Object osmosisReplicationBaseUrl_ = "";
+      public boolean hasOsmosisReplicationBaseUrl() {
+        return ((bitField0_ & 0x00000080) == 0x00000080);
+      }
+      public String getOsmosisReplicationBaseUrl() {
+        java.lang.Object ref = osmosisReplicationBaseUrl_;
+        if (!(ref instanceof String)) {
+          String s = ((com.google.protobuf.ByteString) ref).toStringUtf8();
+          osmosisReplicationBaseUrl_ = s;
+          return s;
+        } else {
+          return (String) ref;
+        }
+      }
+      public Builder setOsmosisReplicationBaseUrl(String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000080;
+        osmosisReplicationBaseUrl_ = value;
+        
+        return this;
+      }
+      public Builder clearOsmosisReplicationBaseUrl() {
+        bitField0_ = (bitField0_ & ~0x00000080);
+        osmosisReplicationBaseUrl_ = getDefaultInstance().getOsmosisReplicationBaseUrl();
+        
+        return this;
+      }
+      void setOsmosisReplicationBaseUrl(com.google.protobuf.ByteString value) {
+        bitField0_ |= 0x00000080;
+        osmosisReplicationBaseUrl_ = value;
+        
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.HeaderBlock)
+    }
+    
+    static {
+      defaultInstance = new HeaderBlock(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.HeaderBlock)
+  }
+  
+  public interface HeaderBBoxOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required sint64 left = 1;
+    boolean hasLeft();
+    long getLeft();
+    
+    // required sint64 right = 2;
+    boolean hasRight();
+    long getRight();
+    
+    // required sint64 top = 3;
+    boolean hasTop();
+    long getTop();
+    
+    // required sint64 bottom = 4;
+    boolean hasBottom();
+    long getBottom();
+  }
+  public static final class HeaderBBox extends
+      com.google.protobuf.GeneratedMessageLite
+      implements HeaderBBoxOrBuilder {
+    // Use HeaderBBox.newBuilder() to construct.
+    private HeaderBBox(Builder builder) {
+      super(builder);
+    }
+    private HeaderBBox(boolean noInit) {}
+    
+    private static final HeaderBBox defaultInstance;
+    public static HeaderBBox getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public HeaderBBox getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required sint64 left = 1;
+    public static final int LEFT_FIELD_NUMBER = 1;
+    private long left_;
+    public boolean hasLeft() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public long getLeft() {
+      return left_;
+    }
+    
+    // required sint64 right = 2;
+    public static final int RIGHT_FIELD_NUMBER = 2;
+    private long right_;
+    public boolean hasRight() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public long getRight() {
+      return right_;
+    }
+    
+    // required sint64 top = 3;
+    public static final int TOP_FIELD_NUMBER = 3;
+    private long top_;
+    public boolean hasTop() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public long getTop() {
+      return top_;
+    }
+    
+    // required sint64 bottom = 4;
+    public static final int BOTTOM_FIELD_NUMBER = 4;
+    private long bottom_;
+    public boolean hasBottom() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public long getBottom() {
+      return bottom_;
+    }
+    
+    private void initFields() {
+      left_ = 0L;
+      right_ = 0L;
+      top_ = 0L;
+      bottom_ = 0L;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasLeft()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasRight()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasTop()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasBottom()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeSInt64(1, left_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeSInt64(2, right_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeSInt64(3, top_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeSInt64(4, bottom_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(1, left_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(2, right_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(3, top_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(4, bottom_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBoxOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        left_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        right_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        top_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        bottom_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox result = new org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.left_ = left_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.right_ = right_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.top_ = top_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.bottom_ = bottom_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox.getDefaultInstance()) return this;
+        if (other.hasLeft()) {
+          setLeft(other.getLeft());
+        }
+        if (other.hasRight()) {
+          setRight(other.getRight());
+        }
+        if (other.hasTop()) {
+          setTop(other.getTop());
+        }
+        if (other.hasBottom()) {
+          setBottom(other.getBottom());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasLeft()) {
+          
+          return false;
+        }
+        if (!hasRight()) {
+          
+          return false;
+        }
+        if (!hasTop()) {
+          
+          return false;
+        }
+        if (!hasBottom()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              left_ = input.readSInt64();
+              break;
+            }
+            case 16: {
+              bitField0_ |= 0x00000002;
+              right_ = input.readSInt64();
+              break;
+            }
+            case 24: {
+              bitField0_ |= 0x00000004;
+              top_ = input.readSInt64();
+              break;
+            }
+            case 32: {
+              bitField0_ |= 0x00000008;
+              bottom_ = input.readSInt64();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required sint64 left = 1;
+      private long left_ ;
+      public boolean hasLeft() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public long getLeft() {
+        return left_;
+      }
+      public Builder setLeft(long value) {
+        bitField0_ |= 0x00000001;
+        left_ = value;
+        
+        return this;
+      }
+      public Builder clearLeft() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        left_ = 0L;
+        
+        return this;
+      }
+      
+      // required sint64 right = 2;
+      private long right_ ;
+      public boolean hasRight() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public long getRight() {
+        return right_;
+      }
+      public Builder setRight(long value) {
+        bitField0_ |= 0x00000002;
+        right_ = value;
+        
+        return this;
+      }
+      public Builder clearRight() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        right_ = 0L;
+        
+        return this;
+      }
+      
+      // required sint64 top = 3;
+      private long top_ ;
+      public boolean hasTop() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      public long getTop() {
+        return top_;
+      }
+      public Builder setTop(long value) {
+        bitField0_ |= 0x00000004;
+        top_ = value;
+        
+        return this;
+      }
+      public Builder clearTop() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        top_ = 0L;
+        
+        return this;
+      }
+      
+      // required sint64 bottom = 4;
+      private long bottom_ ;
+      public boolean hasBottom() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public long getBottom() {
+        return bottom_;
+      }
+      public Builder setBottom(long value) {
+        bitField0_ |= 0x00000008;
+        bottom_ = value;
+        
+        return this;
+      }
+      public Builder clearBottom() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        bottom_ = 0L;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.HeaderBBox)
+    }
+    
+    static {
+      defaultInstance = new HeaderBBox(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.HeaderBBox)
+  }
+  
+  public interface PrimitiveBlockOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required .OSMPBF.StringTable stringtable = 1;
+    boolean hasStringtable();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable getStringtable();
+    
+    // repeated .OSMPBF.PrimitiveGroup primitivegroup = 2;
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> 
+        getPrimitivegroupList();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup getPrimitivegroup(int index);
+    int getPrimitivegroupCount();
+    
+    // optional int32 granularity = 17 [default = 100];
+    boolean hasGranularity();
+    int getGranularity();
+    
+    // optional int64 lat_offset = 19 [default = 0];
+    boolean hasLatOffset();
+    long getLatOffset();
+    
+    // optional int64 lon_offset = 20 [default = 0];
+    boolean hasLonOffset();
+    long getLonOffset();
+    
+    // optional int32 date_granularity = 18 [default = 1000];
+    boolean hasDateGranularity();
+    int getDateGranularity();
+  }
+  public static final class PrimitiveBlock extends
+      com.google.protobuf.GeneratedMessageLite
+      implements PrimitiveBlockOrBuilder {
+    // Use PrimitiveBlock.newBuilder() to construct.
+    private PrimitiveBlock(Builder builder) {
+      super(builder);
+    }
+    private PrimitiveBlock(boolean noInit) {}
+    
+    private static final PrimitiveBlock defaultInstance;
+    public static PrimitiveBlock getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public PrimitiveBlock getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required .OSMPBF.StringTable stringtable = 1;
+    public static final int STRINGTABLE_FIELD_NUMBER = 1;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable stringtable_;
+    public boolean hasStringtable() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable getStringtable() {
+      return stringtable_;
+    }
+    
+    // repeated .OSMPBF.PrimitiveGroup primitivegroup = 2;
+    public static final int PRIMITIVEGROUP_FIELD_NUMBER = 2;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> primitivegroup_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> getPrimitivegroupList() {
+      return primitivegroup_;
+    }
+    public java.util.List<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroupOrBuilder> 
+        getPrimitivegroupOrBuilderList() {
+      return primitivegroup_;
+    }
+    public int getPrimitivegroupCount() {
+      return primitivegroup_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup getPrimitivegroup(int index) {
+      return primitivegroup_.get(index);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroupOrBuilder getPrimitivegroupOrBuilder(
+        int index) {
+      return primitivegroup_.get(index);
+    }
+    
+    // optional int32 granularity = 17 [default = 100];
+    public static final int GRANULARITY_FIELD_NUMBER = 17;
+    private int granularity_;
+    public boolean hasGranularity() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public int getGranularity() {
+      return granularity_;
+    }
+    
+    // optional int64 lat_offset = 19 [default = 0];
+    public static final int LAT_OFFSET_FIELD_NUMBER = 19;
+    private long latOffset_;
+    public boolean hasLatOffset() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public long getLatOffset() {
+      return latOffset_;
+    }
+    
+    // optional int64 lon_offset = 20 [default = 0];
+    public static final int LON_OFFSET_FIELD_NUMBER = 20;
+    private long lonOffset_;
+    public boolean hasLonOffset() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public long getLonOffset() {
+      return lonOffset_;
+    }
+    
+    // optional int32 date_granularity = 18 [default = 1000];
+    public static final int DATE_GRANULARITY_FIELD_NUMBER = 18;
+    private int dateGranularity_;
+    public boolean hasDateGranularity() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    public int getDateGranularity() {
+      return dateGranularity_;
+    }
+    
+    private void initFields() {
+      stringtable_ = org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance();
+      primitivegroup_ = java.util.Collections.emptyList();
+      granularity_ = 100;
+      latOffset_ = 0L;
+      lonOffset_ = 0L;
+      dateGranularity_ = 1000;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasStringtable()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      for (int i = 0; i < getPrimitivegroupCount(); i++) {
+        if (!getPrimitivegroup(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(1, stringtable_);
+      }
+      for (int i = 0; i < primitivegroup_.size(); i++) {
+        output.writeMessage(2, primitivegroup_.get(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt32(17, granularity_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeInt32(18, dateGranularity_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt64(19, latOffset_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeInt64(20, lonOffset_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, stringtable_);
+      }
+      for (int i = 0; i < primitivegroup_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, primitivegroup_.get(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(17, granularity_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(18, dateGranularity_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(19, latOffset_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(20, lonOffset_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlockOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        stringtable_ = org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        primitivegroup_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000002);
+        granularity_ = 100;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        latOffset_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        lonOffset_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        dateGranularity_ = 1000;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock result = new org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.stringtable_ = stringtable_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          primitivegroup_ = java.util.Collections.unmodifiableList(primitivegroup_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.primitivegroup_ = primitivegroup_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.granularity_ = granularity_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.latOffset_ = latOffset_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.lonOffset_ = lonOffset_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.dateGranularity_ = dateGranularity_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock.getDefaultInstance()) return this;
+        if (other.hasStringtable()) {
+          mergeStringtable(other.getStringtable());
+        }
+        if (!other.primitivegroup_.isEmpty()) {
+          if (primitivegroup_.isEmpty()) {
+            primitivegroup_ = other.primitivegroup_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensurePrimitivegroupIsMutable();
+            primitivegroup_.addAll(other.primitivegroup_);
+          }
+          
+        }
+        if (other.hasGranularity()) {
+          setGranularity(other.getGranularity());
+        }
+        if (other.hasLatOffset()) {
+          setLatOffset(other.getLatOffset());
+        }
+        if (other.hasLonOffset()) {
+          setLonOffset(other.getLonOffset());
+        }
+        if (other.hasDateGranularity()) {
+          setDateGranularity(other.getDateGranularity());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasStringtable()) {
+          
+          return false;
+        }
+        for (int i = 0; i < getPrimitivegroupCount(); i++) {
+          if (!getPrimitivegroup(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.newBuilder();
+              if (hasStringtable()) {
+                subBuilder.mergeFrom(getStringtable());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setStringtable(subBuilder.buildPartial());
+              break;
+            }
+            case 18: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.newBuilder();
+              input.readMessage(subBuilder, extensionRegistry);
+              addPrimitivegroup(subBuilder.buildPartial());
+              break;
+            }
+            case 136: {
+              bitField0_ |= 0x00000004;
+              granularity_ = input.readInt32();
+              break;
+            }
+            case 144: {
+              bitField0_ |= 0x00000020;
+              dateGranularity_ = input.readInt32();
+              break;
+            }
+            case 152: {
+              bitField0_ |= 0x00000008;
+              latOffset_ = input.readInt64();
+              break;
+            }
+            case 160: {
+              bitField0_ |= 0x00000010;
+              lonOffset_ = input.readInt64();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required .OSMPBF.StringTable stringtable = 1;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable stringtable_ = org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance();
+      public boolean hasStringtable() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable getStringtable() {
+        return stringtable_;
+      }
+      public Builder setStringtable(org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        stringtable_ = value;
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder setStringtable(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.Builder builderForValue) {
+        stringtable_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder mergeStringtable(org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable value) {
+        if (((bitField0_ & 0x00000001) == 0x00000001) &&
+            stringtable_ != org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance()) {
+          stringtable_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.newBuilder(stringtable_).mergeFrom(value).buildPartial();
+        } else {
+          stringtable_ = value;
+        }
+        
+        bitField0_ |= 0x00000001;
+        return this;
+      }
+      public Builder clearStringtable() {
+        stringtable_ = org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+      
+      // repeated .OSMPBF.PrimitiveGroup primitivegroup = 2;
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> primitivegroup_ =
+        java.util.Collections.emptyList();
+      private void ensurePrimitivegroupIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          primitivegroup_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup>(primitivegroup_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> getPrimitivegroupList() {
+        return java.util.Collections.unmodifiableList(primitivegroup_);
+      }
+      public int getPrimitivegroupCount() {
+        return primitivegroup_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup getPrimitivegroup(int index) {
+        return primitivegroup_.get(index);
+      }
+      public Builder setPrimitivegroup(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.set(index, value);
+        
+        return this;
+      }
+      public Builder setPrimitivegroup(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.Builder builderForValue) {
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.set(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addPrimitivegroup(org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.add(value);
+        
+        return this;
+      }
+      public Builder addPrimitivegroup(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.add(index, value);
+        
+        return this;
+      }
+      public Builder addPrimitivegroup(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.Builder builderForValue) {
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.add(builderForValue.build());
+        
+        return this;
+      }
+      public Builder addPrimitivegroup(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.Builder builderForValue) {
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.add(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addAllPrimitivegroup(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup> values) {
+        ensurePrimitivegroupIsMutable();
+        super.addAll(values, primitivegroup_);
+        
+        return this;
+      }
+      public Builder clearPrimitivegroup() {
+        primitivegroup_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      public Builder removePrimitivegroup(int index) {
+        ensurePrimitivegroupIsMutable();
+        primitivegroup_.remove(index);
+        
+        return this;
+      }
+      
+      // optional int32 granularity = 17 [default = 100];
+      private int granularity_ = 100;
+      public boolean hasGranularity() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      public int getGranularity() {
+        return granularity_;
+      }
+      public Builder setGranularity(int value) {
+        bitField0_ |= 0x00000004;
+        granularity_ = value;
+        
+        return this;
+      }
+      public Builder clearGranularity() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        granularity_ = 100;
+        
+        return this;
+      }
+      
+      // optional int64 lat_offset = 19 [default = 0];
+      private long latOffset_ ;
+      public boolean hasLatOffset() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public long getLatOffset() {
+        return latOffset_;
+      }
+      public Builder setLatOffset(long value) {
+        bitField0_ |= 0x00000008;
+        latOffset_ = value;
+        
+        return this;
+      }
+      public Builder clearLatOffset() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        latOffset_ = 0L;
+        
+        return this;
+      }
+      
+      // optional int64 lon_offset = 20 [default = 0];
+      private long lonOffset_ ;
+      public boolean hasLonOffset() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      public long getLonOffset() {
+        return lonOffset_;
+      }
+      public Builder setLonOffset(long value) {
+        bitField0_ |= 0x00000010;
+        lonOffset_ = value;
+        
+        return this;
+      }
+      public Builder clearLonOffset() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        lonOffset_ = 0L;
+        
+        return this;
+      }
+      
+      // optional int32 date_granularity = 18 [default = 1000];
+      private int dateGranularity_ = 1000;
+      public boolean hasDateGranularity() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      public int getDateGranularity() {
+        return dateGranularity_;
+      }
+      public Builder setDateGranularity(int value) {
+        bitField0_ |= 0x00000020;
+        dateGranularity_ = value;
+        
+        return this;
+      }
+      public Builder clearDateGranularity() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        dateGranularity_ = 1000;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.PrimitiveBlock)
+    }
+    
+    static {
+      defaultInstance = new PrimitiveBlock(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.PrimitiveBlock)
+  }
+  
+  public interface PrimitiveGroupOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // repeated .OSMPBF.Node nodes = 1;
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Node> 
+        getNodesList();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Node getNodes(int index);
+    int getNodesCount();
+    
+    // optional .OSMPBF.DenseNodes dense = 2;
+    boolean hasDense();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes getDense();
+    
+    // repeated .OSMPBF.Way ways = 3;
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Way> 
+        getWaysList();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Way getWays(int index);
+    int getWaysCount();
+    
+    // repeated .OSMPBF.Relation relations = 4;
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> 
+        getRelationsList();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Relation getRelations(int index);
+    int getRelationsCount();
+    
+    // repeated .OSMPBF.ChangeSet changesets = 5;
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> 
+        getChangesetsList();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet getChangesets(int index);
+    int getChangesetsCount();
+  }
+  public static final class PrimitiveGroup extends
+      com.google.protobuf.GeneratedMessageLite
+      implements PrimitiveGroupOrBuilder {
+    // Use PrimitiveGroup.newBuilder() to construct.
+    private PrimitiveGroup(Builder builder) {
+      super(builder);
+    }
+    private PrimitiveGroup(boolean noInit) {}
+    
+    private static final PrimitiveGroup defaultInstance;
+    public static PrimitiveGroup getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public PrimitiveGroup getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // repeated .OSMPBF.Node nodes = 1;
+    public static final int NODES_FIELD_NUMBER = 1;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Node> nodes_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Node> getNodesList() {
+      return nodes_;
+    }
+    public java.util.List<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.NodeOrBuilder> 
+        getNodesOrBuilderList() {
+      return nodes_;
+    }
+    public int getNodesCount() {
+      return nodes_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Node getNodes(int index) {
+      return nodes_.get(index);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.NodeOrBuilder getNodesOrBuilder(
+        int index) {
+      return nodes_.get(index);
+    }
+    
+    // optional .OSMPBF.DenseNodes dense = 2;
+    public static final int DENSE_FIELD_NUMBER = 2;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes dense_;
+    public boolean hasDense() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes getDense() {
+      return dense_;
+    }
+    
+    // repeated .OSMPBF.Way ways = 3;
+    public static final int WAYS_FIELD_NUMBER = 3;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Way> ways_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Way> getWaysList() {
+      return ways_;
+    }
+    public java.util.List<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.WayOrBuilder> 
+        getWaysOrBuilderList() {
+      return ways_;
+    }
+    public int getWaysCount() {
+      return ways_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Way getWays(int index) {
+      return ways_.get(index);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.WayOrBuilder getWaysOrBuilder(
+        int index) {
+      return ways_.get(index);
+    }
+    
+    // repeated .OSMPBF.Relation relations = 4;
+    public static final int RELATIONS_FIELD_NUMBER = 4;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> relations_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> getRelationsList() {
+      return relations_;
+    }
+    public java.util.List<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.RelationOrBuilder> 
+        getRelationsOrBuilderList() {
+      return relations_;
+    }
+    public int getRelationsCount() {
+      return relations_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation getRelations(int index) {
+      return relations_.get(index);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.RelationOrBuilder getRelationsOrBuilder(
+        int index) {
+      return relations_.get(index);
+    }
+    
+    // repeated .OSMPBF.ChangeSet changesets = 5;
+    public static final int CHANGESETS_FIELD_NUMBER = 5;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> changesets_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> getChangesetsList() {
+      return changesets_;
+    }
+    public java.util.List<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSetOrBuilder> 
+        getChangesetsOrBuilderList() {
+      return changesets_;
+    }
+    public int getChangesetsCount() {
+      return changesets_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet getChangesets(int index) {
+      return changesets_.get(index);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSetOrBuilder getChangesetsOrBuilder(
+        int index) {
+      return changesets_.get(index);
+    }
+    
+    private void initFields() {
+      nodes_ = java.util.Collections.emptyList();
+      dense_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance();
+      ways_ = java.util.Collections.emptyList();
+      relations_ = java.util.Collections.emptyList();
+      changesets_ = java.util.Collections.emptyList();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      for (int i = 0; i < getNodesCount(); i++) {
+        if (!getNodes(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      for (int i = 0; i < getWaysCount(); i++) {
+        if (!getWays(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      for (int i = 0; i < getRelationsCount(); i++) {
+        if (!getRelations(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      for (int i = 0; i < getChangesetsCount(); i++) {
+        if (!getChangesets(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      for (int i = 0; i < nodes_.size(); i++) {
+        output.writeMessage(1, nodes_.get(i));
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(2, dense_);
+      }
+      for (int i = 0; i < ways_.size(); i++) {
+        output.writeMessage(3, ways_.get(i));
+      }
+      for (int i = 0; i < relations_.size(); i++) {
+        output.writeMessage(4, relations_.get(i));
+      }
+      for (int i = 0; i < changesets_.size(); i++) {
+        output.writeMessage(5, changesets_.get(i));
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      for (int i = 0; i < nodes_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(1, nodes_.get(i));
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(2, dense_);
+      }
+      for (int i = 0; i < ways_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(3, ways_.get(i));
+      }
+      for (int i = 0; i < relations_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, relations_.get(i));
+      }
+      for (int i = 0; i < changesets_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(5, changesets_.get(i));
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroupOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        nodes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        dense_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000002);
+        ways_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000004);
+        relations_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000008);
+        changesets_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000010);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup result = new org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          nodes_ = java.util.Collections.unmodifiableList(nodes_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.nodes_ = nodes_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.dense_ = dense_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          ways_ = java.util.Collections.unmodifiableList(ways_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.ways_ = ways_;
+        if (((bitField0_ & 0x00000008) == 0x00000008)) {
+          relations_ = java.util.Collections.unmodifiableList(relations_);
+          bitField0_ = (bitField0_ & ~0x00000008);
+        }
+        result.relations_ = relations_;
+        if (((bitField0_ & 0x00000010) == 0x00000010)) {
+          changesets_ = java.util.Collections.unmodifiableList(changesets_);
+          bitField0_ = (bitField0_ & ~0x00000010);
+        }
+        result.changesets_ = changesets_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup.getDefaultInstance()) return this;
+        if (!other.nodes_.isEmpty()) {
+          if (nodes_.isEmpty()) {
+            nodes_ = other.nodes_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureNodesIsMutable();
+            nodes_.addAll(other.nodes_);
+          }
+          
+        }
+        if (other.hasDense()) {
+          mergeDense(other.getDense());
+        }
+        if (!other.ways_.isEmpty()) {
+          if (ways_.isEmpty()) {
+            ways_ = other.ways_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureWaysIsMutable();
+            ways_.addAll(other.ways_);
+          }
+          
+        }
+        if (!other.relations_.isEmpty()) {
+          if (relations_.isEmpty()) {
+            relations_ = other.relations_;
+            bitField0_ = (bitField0_ & ~0x00000008);
+          } else {
+            ensureRelationsIsMutable();
+            relations_.addAll(other.relations_);
+          }
+          
+        }
+        if (!other.changesets_.isEmpty()) {
+          if (changesets_.isEmpty()) {
+            changesets_ = other.changesets_;
+            bitField0_ = (bitField0_ & ~0x00000010);
+          } else {
+            ensureChangesetsIsMutable();
+            changesets_.addAll(other.changesets_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        for (int i = 0; i < getNodesCount(); i++) {
+          if (!getNodes(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        for (int i = 0; i < getWaysCount(); i++) {
+          if (!getWays(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        for (int i = 0; i < getRelationsCount(); i++) {
+          if (!getRelations(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        for (int i = 0; i < getChangesetsCount(); i++) {
+          if (!getChangesets(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Node.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Node.newBuilder();
+              input.readMessage(subBuilder, extensionRegistry);
+              addNodes(subBuilder.buildPartial());
+              break;
+            }
+            case 18: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.newBuilder();
+              if (hasDense()) {
+                subBuilder.mergeFrom(getDense());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setDense(subBuilder.buildPartial());
+              break;
+            }
+            case 26: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Way.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Way.newBuilder();
+              input.readMessage(subBuilder, extensionRegistry);
+              addWays(subBuilder.buildPartial());
+              break;
+            }
+            case 34: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.newBuilder();
+              input.readMessage(subBuilder, extensionRegistry);
+              addRelations(subBuilder.buildPartial());
+              break;
+            }
+            case 42: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.newBuilder();
+              input.readMessage(subBuilder, extensionRegistry);
+              addChangesets(subBuilder.buildPartial());
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // repeated .OSMPBF.Node nodes = 1;
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Node> nodes_ =
+        java.util.Collections.emptyList();
+      private void ensureNodesIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          nodes_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.Node>(nodes_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+      
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Node> getNodesList() {
+        return java.util.Collections.unmodifiableList(nodes_);
+      }
+      public int getNodesCount() {
+        return nodes_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Node getNodes(int index) {
+        return nodes_.get(index);
+      }
+      public Builder setNodes(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Node value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureNodesIsMutable();
+        nodes_.set(index, value);
+        
+        return this;
+      }
+      public Builder setNodes(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Node.Builder builderForValue) {
+        ensureNodesIsMutable();
+        nodes_.set(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addNodes(org.openstreetmap.osmosis.osmbinary.Osmformat.Node value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureNodesIsMutable();
+        nodes_.add(value);
+        
+        return this;
+      }
+      public Builder addNodes(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Node value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureNodesIsMutable();
+        nodes_.add(index, value);
+        
+        return this;
+      }
+      public Builder addNodes(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Node.Builder builderForValue) {
+        ensureNodesIsMutable();
+        nodes_.add(builderForValue.build());
+        
+        return this;
+      }
+      public Builder addNodes(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Node.Builder builderForValue) {
+        ensureNodesIsMutable();
+        nodes_.add(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addAllNodes(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.Node> values) {
+        ensureNodesIsMutable();
+        super.addAll(values, nodes_);
+        
+        return this;
+      }
+      public Builder clearNodes() {
+        nodes_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000001);
+        
+        return this;
+      }
+      public Builder removeNodes(int index) {
+        ensureNodesIsMutable();
+        nodes_.remove(index);
+        
+        return this;
+      }
+      
+      // optional .OSMPBF.DenseNodes dense = 2;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes dense_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance();
+      public boolean hasDense() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes getDense() {
+        return dense_;
+      }
+      public Builder setDense(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        dense_ = value;
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder setDense(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.Builder builderForValue) {
+        dense_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder mergeDense(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes value) {
+        if (((bitField0_ & 0x00000002) == 0x00000002) &&
+            dense_ != org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance()) {
+          dense_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.newBuilder(dense_).mergeFrom(value).buildPartial();
+        } else {
+          dense_ = value;
+        }
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder clearDense() {
+        dense_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000002);
+        return this;
+      }
+      
+      // repeated .OSMPBF.Way ways = 3;
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Way> ways_ =
+        java.util.Collections.emptyList();
+      private void ensureWaysIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          ways_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.Way>(ways_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Way> getWaysList() {
+        return java.util.Collections.unmodifiableList(ways_);
+      }
+      public int getWaysCount() {
+        return ways_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Way getWays(int index) {
+        return ways_.get(index);
+      }
+      public Builder setWays(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Way value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureWaysIsMutable();
+        ways_.set(index, value);
+        
+        return this;
+      }
+      public Builder setWays(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Way.Builder builderForValue) {
+        ensureWaysIsMutable();
+        ways_.set(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addWays(org.openstreetmap.osmosis.osmbinary.Osmformat.Way value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureWaysIsMutable();
+        ways_.add(value);
+        
+        return this;
+      }
+      public Builder addWays(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Way value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureWaysIsMutable();
+        ways_.add(index, value);
+        
+        return this;
+      }
+      public Builder addWays(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Way.Builder builderForValue) {
+        ensureWaysIsMutable();
+        ways_.add(builderForValue.build());
+        
+        return this;
+      }
+      public Builder addWays(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Way.Builder builderForValue) {
+        ensureWaysIsMutable();
+        ways_.add(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addAllWays(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.Way> values) {
+        ensureWaysIsMutable();
+        super.addAll(values, ways_);
+        
+        return this;
+      }
+      public Builder clearWays() {
+        ways_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      public Builder removeWays(int index) {
+        ensureWaysIsMutable();
+        ways_.remove(index);
+        
+        return this;
+      }
+      
+      // repeated .OSMPBF.Relation relations = 4;
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> relations_ =
+        java.util.Collections.emptyList();
+      private void ensureRelationsIsMutable() {
+        if (!((bitField0_ & 0x00000008) == 0x00000008)) {
+          relations_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation>(relations_);
+          bitField0_ |= 0x00000008;
+         }
+      }
+      
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> getRelationsList() {
+        return java.util.Collections.unmodifiableList(relations_);
+      }
+      public int getRelationsCount() {
+        return relations_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation getRelations(int index) {
+        return relations_.get(index);
+      }
+      public Builder setRelations(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Relation value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureRelationsIsMutable();
+        relations_.set(index, value);
+        
+        return this;
+      }
+      public Builder setRelations(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.Builder builderForValue) {
+        ensureRelationsIsMutable();
+        relations_.set(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addRelations(org.openstreetmap.osmosis.osmbinary.Osmformat.Relation value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureRelationsIsMutable();
+        relations_.add(value);
+        
+        return this;
+      }
+      public Builder addRelations(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Relation value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureRelationsIsMutable();
+        relations_.add(index, value);
+        
+        return this;
+      }
+      public Builder addRelations(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.Builder builderForValue) {
+        ensureRelationsIsMutable();
+        relations_.add(builderForValue.build());
+        
+        return this;
+      }
+      public Builder addRelations(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.Builder builderForValue) {
+        ensureRelationsIsMutable();
+        relations_.add(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addAllRelations(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.Relation> values) {
+        ensureRelationsIsMutable();
+        super.addAll(values, relations_);
+        
+        return this;
+      }
+      public Builder clearRelations() {
+        relations_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000008);
+        
+        return this;
+      }
+      public Builder removeRelations(int index) {
+        ensureRelationsIsMutable();
+        relations_.remove(index);
+        
+        return this;
+      }
+      
+      // repeated .OSMPBF.ChangeSet changesets = 5;
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> changesets_ =
+        java.util.Collections.emptyList();
+      private void ensureChangesetsIsMutable() {
+        if (!((bitField0_ & 0x00000010) == 0x00000010)) {
+          changesets_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet>(changesets_);
+          bitField0_ |= 0x00000010;
+         }
+      }
+      
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> getChangesetsList() {
+        return java.util.Collections.unmodifiableList(changesets_);
+      }
+      public int getChangesetsCount() {
+        return changesets_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet getChangesets(int index) {
+        return changesets_.get(index);
+      }
+      public Builder setChangesets(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureChangesetsIsMutable();
+        changesets_.set(index, value);
+        
+        return this;
+      }
+      public Builder setChangesets(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.Builder builderForValue) {
+        ensureChangesetsIsMutable();
+        changesets_.set(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addChangesets(org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureChangesetsIsMutable();
+        changesets_.add(value);
+        
+        return this;
+      }
+      public Builder addChangesets(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureChangesetsIsMutable();
+        changesets_.add(index, value);
+        
+        return this;
+      }
+      public Builder addChangesets(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.Builder builderForValue) {
+        ensureChangesetsIsMutable();
+        changesets_.add(builderForValue.build());
+        
+        return this;
+      }
+      public Builder addChangesets(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.Builder builderForValue) {
+        ensureChangesetsIsMutable();
+        changesets_.add(index, builderForValue.build());
+        
+        return this;
+      }
+      public Builder addAllChangesets(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet> values) {
+        ensureChangesetsIsMutable();
+        super.addAll(values, changesets_);
+        
+        return this;
+      }
+      public Builder clearChangesets() {
+        changesets_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000010);
+        
+        return this;
+      }
+      public Builder removeChangesets(int index) {
+        ensureChangesetsIsMutable();
+        changesets_.remove(index);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.PrimitiveGroup)
+    }
+    
+    static {
+      defaultInstance = new PrimitiveGroup(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.PrimitiveGroup)
+  }
+  
+  public interface StringTableOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // repeated bytes s = 1;
+    java.util.List<com.google.protobuf.ByteString> getSList();
+    int getSCount();
+    com.google.protobuf.ByteString getS(int index);
+  }
+  public static final class StringTable extends
+      com.google.protobuf.GeneratedMessageLite
+      implements StringTableOrBuilder {
+    // Use StringTable.newBuilder() to construct.
+    private StringTable(Builder builder) {
+      super(builder);
+    }
+    private StringTable(boolean noInit) {}
+    
+    private static final StringTable defaultInstance;
+    public static StringTable getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public StringTable getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    // repeated bytes s = 1;
+    public static final int S_FIELD_NUMBER = 1;
+    private java.util.List<com.google.protobuf.ByteString> s_;
+    public java.util.List<com.google.protobuf.ByteString>
+        getSList() {
+      return s_;
+    }
+    public int getSCount() {
+      return s_.size();
+    }
+    public com.google.protobuf.ByteString getS(int index) {
+      return s_.get(index);
+    }
+    
+    private void initFields() {
+      s_ = java.util.Collections.emptyList();;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      for (int i = 0; i < s_.size(); i++) {
+        output.writeBytes(1, s_.get(i));
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      {
+        int dataSize = 0;
+        for (int i = 0; i < s_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeBytesSizeNoTag(s_.get(i));
+        }
+        size += dataSize;
+        size += 1 * getSList().size();
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.StringTableOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        s_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable result = new org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable(this);
+        int from_bitField0_ = bitField0_;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          s_ = java.util.Collections.unmodifiableList(s_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.s_ = s_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable.getDefaultInstance()) return this;
+        if (!other.s_.isEmpty()) {
+          if (s_.isEmpty()) {
+            s_ = other.s_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureSIsMutable();
+            s_.addAll(other.s_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              ensureSIsMutable();
+              s_.add(input.readBytes());
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // repeated bytes s = 1;
+      private java.util.List<com.google.protobuf.ByteString> s_ = java.util.Collections.emptyList();;
+      private void ensureSIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          s_ = new java.util.ArrayList<com.google.protobuf.ByteString>(s_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+      public java.util.List<com.google.protobuf.ByteString>
+          getSList() {
+        return java.util.Collections.unmodifiableList(s_);
+      }
+      public int getSCount() {
+        return s_.size();
+      }
+      public com.google.protobuf.ByteString getS(int index) {
+        return s_.get(index);
+      }
+      public Builder setS(
+          int index, com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureSIsMutable();
+        s_.set(index, value);
+        
+        return this;
+      }
+      public Builder addS(com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  ensureSIsMutable();
+        s_.add(value);
+        
+        return this;
+      }
+      public Builder addAllS(
+          java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {
+        ensureSIsMutable();
+        super.addAll(values, s_);
+        
+        return this;
+      }
+      public Builder clearS() {
+        s_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.StringTable)
+    }
+    
+    static {
+      defaultInstance = new StringTable(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.StringTable)
+  }
+  
+  public interface InfoOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // optional int32 version = 1 [default = -1];
+    boolean hasVersion();
+    int getVersion();
+    
+    // optional int64 timestamp = 2;
+    boolean hasTimestamp();
+    long getTimestamp();
+    
+    // optional int64 changeset = 3;
+    boolean hasChangeset();
+    long getChangeset();
+    
+    // optional int32 uid = 4;
+    boolean hasUid();
+    int getUid();
+    
+    // optional uint32 user_sid = 5;
+    boolean hasUserSid();
+    int getUserSid();
+    
+    // optional bool visible = 6;
+    boolean hasVisible();
+    boolean getVisible();
+  }
+  public static final class Info extends
+      com.google.protobuf.GeneratedMessageLite
+      implements InfoOrBuilder {
+    // Use Info.newBuilder() to construct.
+    private Info(Builder builder) {
+      super(builder);
+    }
+    private Info(boolean noInit) {}
+    
+    private static final Info defaultInstance;
+    public static Info getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Info getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // optional int32 version = 1 [default = -1];
+    public static final int VERSION_FIELD_NUMBER = 1;
+    private int version_;
+    public boolean hasVersion() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public int getVersion() {
+      return version_;
+    }
+    
+    // optional int64 timestamp = 2;
+    public static final int TIMESTAMP_FIELD_NUMBER = 2;
+    private long timestamp_;
+    public boolean hasTimestamp() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public long getTimestamp() {
+      return timestamp_;
+    }
+    
+    // optional int64 changeset = 3;
+    public static final int CHANGESET_FIELD_NUMBER = 3;
+    private long changeset_;
+    public boolean hasChangeset() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public long getChangeset() {
+      return changeset_;
+    }
+    
+    // optional int32 uid = 4;
+    public static final int UID_FIELD_NUMBER = 4;
+    private int uid_;
+    public boolean hasUid() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public int getUid() {
+      return uid_;
+    }
+    
+    // optional uint32 user_sid = 5;
+    public static final int USER_SID_FIELD_NUMBER = 5;
+    private int userSid_;
+    public boolean hasUserSid() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    public int getUserSid() {
+      return userSid_;
+    }
+    
+    // optional bool visible = 6;
+    public static final int VISIBLE_FIELD_NUMBER = 6;
+    private boolean visible_;
+    public boolean hasVisible() {
+      return ((bitField0_ & 0x00000020) == 0x00000020);
+    }
+    public boolean getVisible() {
+      return visible_;
+    }
+    
+    private void initFields() {
+      version_ = -1;
+      timestamp_ = 0L;
+      changeset_ = 0L;
+      uid_ = 0;
+      userSid_ = 0;
+      visible_ = false;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt32(1, version_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt64(2, timestamp_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt64(3, changeset_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeInt32(4, uid_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeUInt32(5, userSid_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        output.writeBool(6, visible_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(1, version_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(2, timestamp_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(3, changeset_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(4, uid_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeUInt32Size(5, userSid_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBoolSize(6, visible_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Info parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.Info prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Info, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.InfoOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        version_ = -1;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        timestamp_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        changeset_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        uid_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        userSid_ = 0;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        visible_ = false;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Info result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Info buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Info result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Info result = new org.openstreetmap.osmosis.osmbinary.Osmformat.Info(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.version_ = version_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.timestamp_ = timestamp_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.changeset_ = changeset_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.uid_ = uid_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.userSid_ = userSid_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000020;
+        }
+        result.visible_ = visible_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.Info other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance()) return this;
+        if (other.hasVersion()) {
+          setVersion(other.getVersion());
+        }
+        if (other.hasTimestamp()) {
+          setTimestamp(other.getTimestamp());
+        }
+        if (other.hasChangeset()) {
+          setChangeset(other.getChangeset());
+        }
+        if (other.hasUid()) {
+          setUid(other.getUid());
+        }
+        if (other.hasUserSid()) {
+          setUserSid(other.getUserSid());
+        }
+        if (other.hasVisible()) {
+          setVisible(other.getVisible());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              version_ = input.readInt32();
+              break;
+            }
+            case 16: {
+              bitField0_ |= 0x00000002;
+              timestamp_ = input.readInt64();
+              break;
+            }
+            case 24: {
+              bitField0_ |= 0x00000004;
+              changeset_ = input.readInt64();
+              break;
+            }
+            case 32: {
+              bitField0_ |= 0x00000008;
+              uid_ = input.readInt32();
+              break;
+            }
+            case 40: {
+              bitField0_ |= 0x00000010;
+              userSid_ = input.readUInt32();
+              break;
+            }
+            case 48: {
+              bitField0_ |= 0x00000020;
+              visible_ = input.readBool();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // optional int32 version = 1 [default = -1];
+      private int version_ = -1;
+      public boolean hasVersion() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public int getVersion() {
+        return version_;
+      }
+      public Builder setVersion(int value) {
+        bitField0_ |= 0x00000001;
+        version_ = value;
+        
+        return this;
+      }
+      public Builder clearVersion() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        version_ = -1;
+        
+        return this;
+      }
+      
+      // optional int64 timestamp = 2;
+      private long timestamp_ ;
+      public boolean hasTimestamp() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public long getTimestamp() {
+        return timestamp_;
+      }
+      public Builder setTimestamp(long value) {
+        bitField0_ |= 0x00000002;
+        timestamp_ = value;
+        
+        return this;
+      }
+      public Builder clearTimestamp() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        timestamp_ = 0L;
+        
+        return this;
+      }
+      
+      // optional int64 changeset = 3;
+      private long changeset_ ;
+      public boolean hasChangeset() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      public long getChangeset() {
+        return changeset_;
+      }
+      public Builder setChangeset(long value) {
+        bitField0_ |= 0x00000004;
+        changeset_ = value;
+        
+        return this;
+      }
+      public Builder clearChangeset() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        changeset_ = 0L;
+        
+        return this;
+      }
+      
+      // optional int32 uid = 4;
+      private int uid_ ;
+      public boolean hasUid() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public int getUid() {
+        return uid_;
+      }
+      public Builder setUid(int value) {
+        bitField0_ |= 0x00000008;
+        uid_ = value;
+        
+        return this;
+      }
+      public Builder clearUid() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        uid_ = 0;
+        
+        return this;
+      }
+      
+      // optional uint32 user_sid = 5;
+      private int userSid_ ;
+      public boolean hasUserSid() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      public int getUserSid() {
+        return userSid_;
+      }
+      public Builder setUserSid(int value) {
+        bitField0_ |= 0x00000010;
+        userSid_ = value;
+        
+        return this;
+      }
+      public Builder clearUserSid() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        userSid_ = 0;
+        
+        return this;
+      }
+      
+      // optional bool visible = 6;
+      private boolean visible_ ;
+      public boolean hasVisible() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      public boolean getVisible() {
+        return visible_;
+      }
+      public Builder setVisible(boolean value) {
+        bitField0_ |= 0x00000020;
+        visible_ = value;
+        
+        return this;
+      }
+      public Builder clearVisible() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        visible_ = false;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.Info)
+    }
+    
+    static {
+      defaultInstance = new Info(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.Info)
+  }
+  
+  public interface DenseInfoOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // repeated int32 version = 1 [packed = true];
+    java.util.List<java.lang.Integer> getVersionList();
+    int getVersionCount();
+    int getVersion(int index);
+    
+    // repeated sint64 timestamp = 2 [packed = true];
+    java.util.List<java.lang.Long> getTimestampList();
+    int getTimestampCount();
+    long getTimestamp(int index);
+    
+    // repeated sint64 changeset = 3 [packed = true];
+    java.util.List<java.lang.Long> getChangesetList();
+    int getChangesetCount();
+    long getChangeset(int index);
+    
+    // repeated sint32 uid = 4 [packed = true];
+    java.util.List<java.lang.Integer> getUidList();
+    int getUidCount();
+    int getUid(int index);
+    
+    // repeated sint32 user_sid = 5 [packed = true];
+    java.util.List<java.lang.Integer> getUserSidList();
+    int getUserSidCount();
+    int getUserSid(int index);
+    
+    // repeated bool visible = 6 [packed = true];
+    java.util.List<java.lang.Boolean> getVisibleList();
+    int getVisibleCount();
+    boolean getVisible(int index);
+  }
+  public static final class DenseInfo extends
+      com.google.protobuf.GeneratedMessageLite
+      implements DenseInfoOrBuilder {
+    // Use DenseInfo.newBuilder() to construct.
+    private DenseInfo(Builder builder) {
+      super(builder);
+    }
+    private DenseInfo(boolean noInit) {}
+    
+    private static final DenseInfo defaultInstance;
+    public static DenseInfo getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public DenseInfo getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    // repeated int32 version = 1 [packed = true];
+    public static final int VERSION_FIELD_NUMBER = 1;
+    private java.util.List<java.lang.Integer> version_;
+    public java.util.List<java.lang.Integer>
+        getVersionList() {
+      return version_;
+    }
+    public int getVersionCount() {
+      return version_.size();
+    }
+    public int getVersion(int index) {
+      return version_.get(index);
+    }
+    private int versionMemoizedSerializedSize = -1;
+    
+    // repeated sint64 timestamp = 2 [packed = true];
+    public static final int TIMESTAMP_FIELD_NUMBER = 2;
+    private java.util.List<java.lang.Long> timestamp_;
+    public java.util.List<java.lang.Long>
+        getTimestampList() {
+      return timestamp_;
+    }
+    public int getTimestampCount() {
+      return timestamp_.size();
+    }
+    public long getTimestamp(int index) {
+      return timestamp_.get(index);
+    }
+    private int timestampMemoizedSerializedSize = -1;
+    
+    // repeated sint64 changeset = 3 [packed = true];
+    public static final int CHANGESET_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Long> changeset_;
+    public java.util.List<java.lang.Long>
+        getChangesetList() {
+      return changeset_;
+    }
+    public int getChangesetCount() {
+      return changeset_.size();
+    }
+    public long getChangeset(int index) {
+      return changeset_.get(index);
+    }
+    private int changesetMemoizedSerializedSize = -1;
+    
+    // repeated sint32 uid = 4 [packed = true];
+    public static final int UID_FIELD_NUMBER = 4;
+    private java.util.List<java.lang.Integer> uid_;
+    public java.util.List<java.lang.Integer>
+        getUidList() {
+      return uid_;
+    }
+    public int getUidCount() {
+      return uid_.size();
+    }
+    public int getUid(int index) {
+      return uid_.get(index);
+    }
+    private int uidMemoizedSerializedSize = -1;
+    
+    // repeated sint32 user_sid = 5 [packed = true];
+    public static final int USER_SID_FIELD_NUMBER = 5;
+    private java.util.List<java.lang.Integer> userSid_;
+    public java.util.List<java.lang.Integer>
+        getUserSidList() {
+      return userSid_;
+    }
+    public int getUserSidCount() {
+      return userSid_.size();
+    }
+    public int getUserSid(int index) {
+      return userSid_.get(index);
+    }
+    private int userSidMemoizedSerializedSize = -1;
+    
+    // repeated bool visible = 6 [packed = true];
+    public static final int VISIBLE_FIELD_NUMBER = 6;
+    private java.util.List<java.lang.Boolean> visible_;
+    public java.util.List<java.lang.Boolean>
+        getVisibleList() {
+      return visible_;
+    }
+    public int getVisibleCount() {
+      return visible_.size();
+    }
+    public boolean getVisible(int index) {
+      return visible_.get(index);
+    }
+    private int visibleMemoizedSerializedSize = -1;
+    
+    private void initFields() {
+      version_ = java.util.Collections.emptyList();;
+      timestamp_ = java.util.Collections.emptyList();;
+      changeset_ = java.util.Collections.emptyList();;
+      uid_ = java.util.Collections.emptyList();;
+      userSid_ = java.util.Collections.emptyList();;
+      visible_ = java.util.Collections.emptyList();;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (getVersionList().size() > 0) {
+        output.writeRawVarint32(10);
+        output.writeRawVarint32(versionMemoizedSerializedSize);
+      }
+      for (int i = 0; i < version_.size(); i++) {
+        output.writeInt32NoTag(version_.get(i));
+      }
+      if (getTimestampList().size() > 0) {
+        output.writeRawVarint32(18);
+        output.writeRawVarint32(timestampMemoizedSerializedSize);
+      }
+      for (int i = 0; i < timestamp_.size(); i++) {
+        output.writeSInt64NoTag(timestamp_.get(i));
+      }
+      if (getChangesetList().size() > 0) {
+        output.writeRawVarint32(26);
+        output.writeRawVarint32(changesetMemoizedSerializedSize);
+      }
+      for (int i = 0; i < changeset_.size(); i++) {
+        output.writeSInt64NoTag(changeset_.get(i));
+      }
+      if (getUidList().size() > 0) {
+        output.writeRawVarint32(34);
+        output.writeRawVarint32(uidMemoizedSerializedSize);
+      }
+      for (int i = 0; i < uid_.size(); i++) {
+        output.writeSInt32NoTag(uid_.get(i));
+      }
+      if (getUserSidList().size() > 0) {
+        output.writeRawVarint32(42);
+        output.writeRawVarint32(userSidMemoizedSerializedSize);
+      }
+      for (int i = 0; i < userSid_.size(); i++) {
+        output.writeSInt32NoTag(userSid_.get(i));
+      }
+      if (getVisibleList().size() > 0) {
+        output.writeRawVarint32(50);
+        output.writeRawVarint32(visibleMemoizedSerializedSize);
+      }
+      for (int i = 0; i < visible_.size(); i++) {
+        output.writeBoolNoTag(visible_.get(i));
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      {
+        int dataSize = 0;
+        for (int i = 0; i < version_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeInt32SizeNoTag(version_.get(i));
+        }
+        size += dataSize;
+        if (!getVersionList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        versionMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < timestamp_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(timestamp_.get(i));
+        }
+        size += dataSize;
+        if (!getTimestampList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        timestampMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < changeset_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(changeset_.get(i));
+        }
+        size += dataSize;
+        if (!getChangesetList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        changesetMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < uid_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt32SizeNoTag(uid_.get(i));
+        }
+        size += dataSize;
+        if (!getUidList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        uidMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < userSid_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt32SizeNoTag(userSid_.get(i));
+        }
+        size += dataSize;
+        if (!getUserSidList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        userSidMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        dataSize = 1 * getVisibleList().size();
+        size += dataSize;
+        if (!getVisibleList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        visibleMemoizedSerializedSize = dataSize;
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfoOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        version_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        timestamp_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        changeset_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        uid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        userSid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        visible_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo result = new org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo(this);
+        int from_bitField0_ = bitField0_;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          version_ = java.util.Collections.unmodifiableList(version_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.version_ = version_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          timestamp_ = java.util.Collections.unmodifiableList(timestamp_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.timestamp_ = timestamp_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          changeset_ = java.util.Collections.unmodifiableList(changeset_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.changeset_ = changeset_;
+        if (((bitField0_ & 0x00000008) == 0x00000008)) {
+          uid_ = java.util.Collections.unmodifiableList(uid_);
+          bitField0_ = (bitField0_ & ~0x00000008);
+        }
+        result.uid_ = uid_;
+        if (((bitField0_ & 0x00000010) == 0x00000010)) {
+          userSid_ = java.util.Collections.unmodifiableList(userSid_);
+          bitField0_ = (bitField0_ & ~0x00000010);
+        }
+        result.userSid_ = userSid_;
+        if (((bitField0_ & 0x00000020) == 0x00000020)) {
+          visible_ = java.util.Collections.unmodifiableList(visible_);
+          bitField0_ = (bitField0_ & ~0x00000020);
+        }
+        result.visible_ = visible_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance()) return this;
+        if (!other.version_.isEmpty()) {
+          if (version_.isEmpty()) {
+            version_ = other.version_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureVersionIsMutable();
+            version_.addAll(other.version_);
+          }
+          
+        }
+        if (!other.timestamp_.isEmpty()) {
+          if (timestamp_.isEmpty()) {
+            timestamp_ = other.timestamp_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensureTimestampIsMutable();
+            timestamp_.addAll(other.timestamp_);
+          }
+          
+        }
+        if (!other.changeset_.isEmpty()) {
+          if (changeset_.isEmpty()) {
+            changeset_ = other.changeset_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureChangesetIsMutable();
+            changeset_.addAll(other.changeset_);
+          }
+          
+        }
+        if (!other.uid_.isEmpty()) {
+          if (uid_.isEmpty()) {
+            uid_ = other.uid_;
+            bitField0_ = (bitField0_ & ~0x00000008);
+          } else {
+            ensureUidIsMutable();
+            uid_.addAll(other.uid_);
+          }
+          
+        }
+        if (!other.userSid_.isEmpty()) {
+          if (userSid_.isEmpty()) {
+            userSid_ = other.userSid_;
+            bitField0_ = (bitField0_ & ~0x00000010);
+          } else {
+            ensureUserSidIsMutable();
+            userSid_.addAll(other.userSid_);
+          }
+          
+        }
+        if (!other.visible_.isEmpty()) {
+          if (visible_.isEmpty()) {
+            visible_ = other.visible_;
+            bitField0_ = (bitField0_ & ~0x00000020);
+          } else {
+            ensureVisibleIsMutable();
+            visible_.addAll(other.visible_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              ensureVersionIsMutable();
+              version_.add(input.readInt32());
+              break;
+            }
+            case 10: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addVersion(input.readInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 16: {
+              ensureTimestampIsMutable();
+              timestamp_.add(input.readSInt64());
+              break;
+            }
+            case 18: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addTimestamp(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 24: {
+              ensureChangesetIsMutable();
+              changeset_.add(input.readSInt64());
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addChangeset(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 32: {
+              ensureUidIsMutable();
+              uid_.add(input.readSInt32());
+              break;
+            }
+            case 34: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addUid(input.readSInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 40: {
+              ensureUserSidIsMutable();
+              userSid_.add(input.readSInt32());
+              break;
+            }
+            case 42: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addUserSid(input.readSInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 48: {
+              ensureVisibleIsMutable();
+              visible_.add(input.readBool());
+              break;
+            }
+            case 50: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addVisible(input.readBool());
+              }
+              input.popLimit(limit);
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // repeated int32 version = 1 [packed = true];
+      private java.util.List<java.lang.Integer> version_ = java.util.Collections.emptyList();;
+      private void ensureVersionIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          version_ = new java.util.ArrayList<java.lang.Integer>(version_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getVersionList() {
+        return java.util.Collections.unmodifiableList(version_);
+      }
+      public int getVersionCount() {
+        return version_.size();
+      }
+      public int getVersion(int index) {
+        return version_.get(index);
+      }
+      public Builder setVersion(
+          int index, int value) {
+        ensureVersionIsMutable();
+        version_.set(index, value);
+        
+        return this;
+      }
+      public Builder addVersion(int value) {
+        ensureVersionIsMutable();
+        version_.add(value);
+        
+        return this;
+      }
+      public Builder addAllVersion(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureVersionIsMutable();
+        super.addAll(values, version_);
+        
+        return this;
+      }
+      public Builder clearVersion() {
+        version_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        
+        return this;
+      }
+      
+      // repeated sint64 timestamp = 2 [packed = true];
+      private java.util.List<java.lang.Long> timestamp_ = java.util.Collections.emptyList();;
+      private void ensureTimestampIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          timestamp_ = new java.util.ArrayList<java.lang.Long>(timestamp_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getTimestampList() {
+        return java.util.Collections.unmodifiableList(timestamp_);
+      }
+      public int getTimestampCount() {
+        return timestamp_.size();
+      }
+      public long getTimestamp(int index) {
+        return timestamp_.get(index);
+      }
+      public Builder setTimestamp(
+          int index, long value) {
+        ensureTimestampIsMutable();
+        timestamp_.set(index, value);
+        
+        return this;
+      }
+      public Builder addTimestamp(long value) {
+        ensureTimestampIsMutable();
+        timestamp_.add(value);
+        
+        return this;
+      }
+      public Builder addAllTimestamp(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureTimestampIsMutable();
+        super.addAll(values, timestamp_);
+        
+        return this;
+      }
+      public Builder clearTimestamp() {
+        timestamp_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      
+      // repeated sint64 changeset = 3 [packed = true];
+      private java.util.List<java.lang.Long> changeset_ = java.util.Collections.emptyList();;
+      private void ensureChangesetIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          changeset_ = new java.util.ArrayList<java.lang.Long>(changeset_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getChangesetList() {
+        return java.util.Collections.unmodifiableList(changeset_);
+      }
+      public int getChangesetCount() {
+        return changeset_.size();
+      }
+      public long getChangeset(int index) {
+        return changeset_.get(index);
+      }
+      public Builder setChangeset(
+          int index, long value) {
+        ensureChangesetIsMutable();
+        changeset_.set(index, value);
+        
+        return this;
+      }
+      public Builder addChangeset(long value) {
+        ensureChangesetIsMutable();
+        changeset_.add(value);
+        
+        return this;
+      }
+      public Builder addAllChangeset(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureChangesetIsMutable();
+        super.addAll(values, changeset_);
+        
+        return this;
+      }
+      public Builder clearChangeset() {
+        changeset_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      
+      // repeated sint32 uid = 4 [packed = true];
+      private java.util.List<java.lang.Integer> uid_ = java.util.Collections.emptyList();;
+      private void ensureUidIsMutable() {
+        if (!((bitField0_ & 0x00000008) == 0x00000008)) {
+          uid_ = new java.util.ArrayList<java.lang.Integer>(uid_);
+          bitField0_ |= 0x00000008;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getUidList() {
+        return java.util.Collections.unmodifiableList(uid_);
+      }
+      public int getUidCount() {
+        return uid_.size();
+      }
+      public int getUid(int index) {
+        return uid_.get(index);
+      }
+      public Builder setUid(
+          int index, int value) {
+        ensureUidIsMutable();
+        uid_.set(index, value);
+        
+        return this;
+      }
+      public Builder addUid(int value) {
+        ensureUidIsMutable();
+        uid_.add(value);
+        
+        return this;
+      }
+      public Builder addAllUid(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureUidIsMutable();
+        super.addAll(values, uid_);
+        
+        return this;
+      }
+      public Builder clearUid() {
+        uid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        
+        return this;
+      }
+      
+      // repeated sint32 user_sid = 5 [packed = true];
+      private java.util.List<java.lang.Integer> userSid_ = java.util.Collections.emptyList();;
+      private void ensureUserSidIsMutable() {
+        if (!((bitField0_ & 0x00000010) == 0x00000010)) {
+          userSid_ = new java.util.ArrayList<java.lang.Integer>(userSid_);
+          bitField0_ |= 0x00000010;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getUserSidList() {
+        return java.util.Collections.unmodifiableList(userSid_);
+      }
+      public int getUserSidCount() {
+        return userSid_.size();
+      }
+      public int getUserSid(int index) {
+        return userSid_.get(index);
+      }
+      public Builder setUserSid(
+          int index, int value) {
+        ensureUserSidIsMutable();
+        userSid_.set(index, value);
+        
+        return this;
+      }
+      public Builder addUserSid(int value) {
+        ensureUserSidIsMutable();
+        userSid_.add(value);
+        
+        return this;
+      }
+      public Builder addAllUserSid(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureUserSidIsMutable();
+        super.addAll(values, userSid_);
+        
+        return this;
+      }
+      public Builder clearUserSid() {
+        userSid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        
+        return this;
+      }
+      
+      // repeated bool visible = 6 [packed = true];
+      private java.util.List<java.lang.Boolean> visible_ = java.util.Collections.emptyList();;
+      private void ensureVisibleIsMutable() {
+        if (!((bitField0_ & 0x00000020) == 0x00000020)) {
+          visible_ = new java.util.ArrayList<java.lang.Boolean>(visible_);
+          bitField0_ |= 0x00000020;
+         }
+      }
+      public java.util.List<java.lang.Boolean>
+          getVisibleList() {
+        return java.util.Collections.unmodifiableList(visible_);
+      }
+      public int getVisibleCount() {
+        return visible_.size();
+      }
+      public boolean getVisible(int index) {
+        return visible_.get(index);
+      }
+      public Builder setVisible(
+          int index, boolean value) {
+        ensureVisibleIsMutable();
+        visible_.set(index, value);
+        
+        return this;
+      }
+      public Builder addVisible(boolean value) {
+        ensureVisibleIsMutable();
+        visible_.add(value);
+        
+        return this;
+      }
+      public Builder addAllVisible(
+          java.lang.Iterable<? extends java.lang.Boolean> values) {
+        ensureVisibleIsMutable();
+        super.addAll(values, visible_);
+        
+        return this;
+      }
+      public Builder clearVisible() {
+        visible_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.DenseInfo)
+    }
+    
+    static {
+      defaultInstance = new DenseInfo(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.DenseInfo)
+  }
+  
+  public interface ChangeSetOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required int64 id = 1;
+    boolean hasId();
+    long getId();
+  }
+  public static final class ChangeSet extends
+      com.google.protobuf.GeneratedMessageLite
+      implements ChangeSetOrBuilder {
+    // Use ChangeSet.newBuilder() to construct.
+    private ChangeSet(Builder builder) {
+      super(builder);
+    }
+    private ChangeSet(boolean noInit) {}
+    
+    private static final ChangeSet defaultInstance;
+    public static ChangeSet getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public ChangeSet getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required int64 id = 1;
+    public static final int ID_FIELD_NUMBER = 1;
+    private long id_;
+    public boolean hasId() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public long getId() {
+      return id_;
+    }
+    
+    private void initFields() {
+      id_ = 0L;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(1, id_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(1, id_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSetOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        id_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet result = new org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.id_ = id_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.ChangeSet.getDefaultInstance()) return this;
+        if (other.hasId()) {
+          setId(other.getId());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasId()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              id_ = input.readInt64();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required int64 id = 1;
+      private long id_ ;
+      public boolean hasId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public long getId() {
+        return id_;
+      }
+      public Builder setId(long value) {
+        bitField0_ |= 0x00000001;
+        id_ = value;
+        
+        return this;
+      }
+      public Builder clearId() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        id_ = 0L;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.ChangeSet)
+    }
+    
+    static {
+      defaultInstance = new ChangeSet(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.ChangeSet)
+  }
+  
+  public interface NodeOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required sint64 id = 1;
+    boolean hasId();
+    long getId();
+    
+    // repeated uint32 keys = 2 [packed = true];
+    java.util.List<java.lang.Integer> getKeysList();
+    int getKeysCount();
+    int getKeys(int index);
+    
+    // repeated uint32 vals = 3 [packed = true];
+    java.util.List<java.lang.Integer> getValsList();
+    int getValsCount();
+    int getVals(int index);
+    
+    // optional .OSMPBF.Info info = 4;
+    boolean hasInfo();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo();
+    
+    // required sint64 lat = 8;
+    boolean hasLat();
+    long getLat();
+    
+    // required sint64 lon = 9;
+    boolean hasLon();
+    long getLon();
+  }
+  public static final class Node extends
+      com.google.protobuf.GeneratedMessageLite
+      implements NodeOrBuilder {
+    // Use Node.newBuilder() to construct.
+    private Node(Builder builder) {
+      super(builder);
+    }
+    private Node(boolean noInit) {}
+    
+    private static final Node defaultInstance;
+    public static Node getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Node getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required sint64 id = 1;
+    public static final int ID_FIELD_NUMBER = 1;
+    private long id_;
+    public boolean hasId() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public long getId() {
+      return id_;
+    }
+    
+    // repeated uint32 keys = 2 [packed = true];
+    public static final int KEYS_FIELD_NUMBER = 2;
+    private java.util.List<java.lang.Integer> keys_;
+    public java.util.List<java.lang.Integer>
+        getKeysList() {
+      return keys_;
+    }
+    public int getKeysCount() {
+      return keys_.size();
+    }
+    public int getKeys(int index) {
+      return keys_.get(index);
+    }
+    private int keysMemoizedSerializedSize = -1;
+    
+    // repeated uint32 vals = 3 [packed = true];
+    public static final int VALS_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Integer> vals_;
+    public java.util.List<java.lang.Integer>
+        getValsList() {
+      return vals_;
+    }
+    public int getValsCount() {
+      return vals_.size();
+    }
+    public int getVals(int index) {
+      return vals_.get(index);
+    }
+    private int valsMemoizedSerializedSize = -1;
+    
+    // optional .OSMPBF.Info info = 4;
+    public static final int INFO_FIELD_NUMBER = 4;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_;
+    public boolean hasInfo() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+      return info_;
+    }
+    
+    // required sint64 lat = 8;
+    public static final int LAT_FIELD_NUMBER = 8;
+    private long lat_;
+    public boolean hasLat() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    public long getLat() {
+      return lat_;
+    }
+    
+    // required sint64 lon = 9;
+    public static final int LON_FIELD_NUMBER = 9;
+    private long lon_;
+    public boolean hasLon() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    public long getLon() {
+      return lon_;
+    }
+    
+    private void initFields() {
+      id_ = 0L;
+      keys_ = java.util.Collections.emptyList();;
+      vals_ = java.util.Collections.emptyList();;
+      info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      lat_ = 0L;
+      lon_ = 0L;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasLat()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasLon()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeSInt64(1, id_);
+      }
+      if (getKeysList().size() > 0) {
+        output.writeRawVarint32(18);
+        output.writeRawVarint32(keysMemoizedSerializedSize);
+      }
+      for (int i = 0; i < keys_.size(); i++) {
+        output.writeUInt32NoTag(keys_.get(i));
+      }
+      if (getValsList().size() > 0) {
+        output.writeRawVarint32(26);
+        output.writeRawVarint32(valsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < vals_.size(); i++) {
+        output.writeUInt32NoTag(vals_.get(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeMessage(4, info_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeSInt64(8, lat_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeSInt64(9, lon_);
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(1, id_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < keys_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(keys_.get(i));
+        }
+        size += dataSize;
+        if (!getKeysList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        keysMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < vals_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(vals_.get(i));
+        }
+        size += dataSize;
+        if (!getValsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        valsMemoizedSerializedSize = dataSize;
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, info_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(8, lat_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeSInt64Size(9, lon_);
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Node parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.Node prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Node, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.NodeOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.Node.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        id_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000008);
+        lat_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        lon_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Node getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.Node.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Node build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Node result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Node buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Node result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Node buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Node result = new org.openstreetmap.osmosis.osmbinary.Osmformat.Node(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.id_ = id_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = java.util.Collections.unmodifiableList(keys_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.keys_ = keys_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = java.util.Collections.unmodifiableList(vals_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.vals_ = vals_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.info_ = info_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.lat_ = lat_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.lon_ = lon_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.Node other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.Node.getDefaultInstance()) return this;
+        if (other.hasId()) {
+          setId(other.getId());
+        }
+        if (!other.keys_.isEmpty()) {
+          if (keys_.isEmpty()) {
+            keys_ = other.keys_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensureKeysIsMutable();
+            keys_.addAll(other.keys_);
+          }
+          
+        }
+        if (!other.vals_.isEmpty()) {
+          if (vals_.isEmpty()) {
+            vals_ = other.vals_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureValsIsMutable();
+            vals_.addAll(other.vals_);
+          }
+          
+        }
+        if (other.hasInfo()) {
+          mergeInfo(other.getInfo());
+        }
+        if (other.hasLat()) {
+          setLat(other.getLat());
+        }
+        if (other.hasLon()) {
+          setLon(other.getLon());
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasId()) {
+          
+          return false;
+        }
+        if (!hasLat()) {
+          
+          return false;
+        }
+        if (!hasLon()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              id_ = input.readSInt64();
+              break;
+            }
+            case 16: {
+              ensureKeysIsMutable();
+              keys_.add(input.readUInt32());
+              break;
+            }
+            case 18: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addKeys(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 24: {
+              ensureValsIsMutable();
+              vals_.add(input.readUInt32());
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addVals(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 34: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder();
+              if (hasInfo()) {
+                subBuilder.mergeFrom(getInfo());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setInfo(subBuilder.buildPartial());
+              break;
+            }
+            case 64: {
+              bitField0_ |= 0x00000010;
+              lat_ = input.readSInt64();
+              break;
+            }
+            case 72: {
+              bitField0_ |= 0x00000020;
+              lon_ = input.readSInt64();
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required sint64 id = 1;
+      private long id_ ;
+      public boolean hasId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public long getId() {
+        return id_;
+      }
+      public Builder setId(long value) {
+        bitField0_ |= 0x00000001;
+        id_ = value;
+        
+        return this;
+      }
+      public Builder clearId() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        id_ = 0L;
+        
+        return this;
+      }
+      
+      // repeated uint32 keys = 2 [packed = true];
+      private java.util.List<java.lang.Integer> keys_ = java.util.Collections.emptyList();;
+      private void ensureKeysIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = new java.util.ArrayList<java.lang.Integer>(keys_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getKeysList() {
+        return java.util.Collections.unmodifiableList(keys_);
+      }
+      public int getKeysCount() {
+        return keys_.size();
+      }
+      public int getKeys(int index) {
+        return keys_.get(index);
+      }
+      public Builder setKeys(
+          int index, int value) {
+        ensureKeysIsMutable();
+        keys_.set(index, value);
+        
+        return this;
+      }
+      public Builder addKeys(int value) {
+        ensureKeysIsMutable();
+        keys_.add(value);
+        
+        return this;
+      }
+      public Builder addAllKeys(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureKeysIsMutable();
+        super.addAll(values, keys_);
+        
+        return this;
+      }
+      public Builder clearKeys() {
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      
+      // repeated uint32 vals = 3 [packed = true];
+      private java.util.List<java.lang.Integer> vals_ = java.util.Collections.emptyList();;
+      private void ensureValsIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = new java.util.ArrayList<java.lang.Integer>(vals_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getValsList() {
+        return java.util.Collections.unmodifiableList(vals_);
+      }
+      public int getValsCount() {
+        return vals_.size();
+      }
+      public int getVals(int index) {
+        return vals_.get(index);
+      }
+      public Builder setVals(
+          int index, int value) {
+        ensureValsIsMutable();
+        vals_.set(index, value);
+        
+        return this;
+      }
+      public Builder addVals(int value) {
+        ensureValsIsMutable();
+        vals_.add(value);
+        
+        return this;
+      }
+      public Builder addAllVals(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureValsIsMutable();
+        super.addAll(values, vals_);
+        
+        return this;
+      }
+      public Builder clearVals() {
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      
+      // optional .OSMPBF.Info info = 4;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      public boolean hasInfo() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+        return info_;
+      }
+      public Builder setInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        info_ = value;
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder setInfo(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder builderForValue) {
+        info_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder mergeInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (((bitField0_ & 0x00000008) == 0x00000008) &&
+            info_ != org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance()) {
+          info_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder(info_).mergeFrom(value).buildPartial();
+        } else {
+          info_ = value;
+        }
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder clearInfo() {
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000008);
+        return this;
+      }
+      
+      // required sint64 lat = 8;
+      private long lat_ ;
+      public boolean hasLat() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      public long getLat() {
+        return lat_;
+      }
+      public Builder setLat(long value) {
+        bitField0_ |= 0x00000010;
+        lat_ = value;
+        
+        return this;
+      }
+      public Builder clearLat() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        lat_ = 0L;
+        
+        return this;
+      }
+      
+      // required sint64 lon = 9;
+      private long lon_ ;
+      public boolean hasLon() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      public long getLon() {
+        return lon_;
+      }
+      public Builder setLon(long value) {
+        bitField0_ |= 0x00000020;
+        lon_ = value;
+        
+        return this;
+      }
+      public Builder clearLon() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        lon_ = 0L;
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.Node)
+    }
+    
+    static {
+      defaultInstance = new Node(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.Node)
+  }
+  
+  public interface DenseNodesOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // repeated sint64 id = 1 [packed = true];
+    java.util.List<java.lang.Long> getIdList();
+    int getIdCount();
+    long getId(int index);
+    
+    // optional .OSMPBF.DenseInfo denseinfo = 5;
+    boolean hasDenseinfo();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo getDenseinfo();
+    
+    // repeated sint64 lat = 8 [packed = true];
+    java.util.List<java.lang.Long> getLatList();
+    int getLatCount();
+    long getLat(int index);
+    
+    // repeated sint64 lon = 9 [packed = true];
+    java.util.List<java.lang.Long> getLonList();
+    int getLonCount();
+    long getLon(int index);
+    
+    // repeated int32 keys_vals = 10 [packed = true];
+    java.util.List<java.lang.Integer> getKeysValsList();
+    int getKeysValsCount();
+    int getKeysVals(int index);
+  }
+  public static final class DenseNodes extends
+      com.google.protobuf.GeneratedMessageLite
+      implements DenseNodesOrBuilder {
+    // Use DenseNodes.newBuilder() to construct.
+    private DenseNodes(Builder builder) {
+      super(builder);
+    }
+    private DenseNodes(boolean noInit) {}
+    
+    private static final DenseNodes defaultInstance;
+    public static DenseNodes getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public DenseNodes getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // repeated sint64 id = 1 [packed = true];
+    public static final int ID_FIELD_NUMBER = 1;
+    private java.util.List<java.lang.Long> id_;
+    public java.util.List<java.lang.Long>
+        getIdList() {
+      return id_;
+    }
+    public int getIdCount() {
+      return id_.size();
+    }
+    public long getId(int index) {
+      return id_.get(index);
+    }
+    private int idMemoizedSerializedSize = -1;
+    
+    // optional .OSMPBF.DenseInfo denseinfo = 5;
+    public static final int DENSEINFO_FIELD_NUMBER = 5;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo denseinfo_;
+    public boolean hasDenseinfo() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo getDenseinfo() {
+      return denseinfo_;
+    }
+    
+    // repeated sint64 lat = 8 [packed = true];
+    public static final int LAT_FIELD_NUMBER = 8;
+    private java.util.List<java.lang.Long> lat_;
+    public java.util.List<java.lang.Long>
+        getLatList() {
+      return lat_;
+    }
+    public int getLatCount() {
+      return lat_.size();
+    }
+    public long getLat(int index) {
+      return lat_.get(index);
+    }
+    private int latMemoizedSerializedSize = -1;
+    
+    // repeated sint64 lon = 9 [packed = true];
+    public static final int LON_FIELD_NUMBER = 9;
+    private java.util.List<java.lang.Long> lon_;
+    public java.util.List<java.lang.Long>
+        getLonList() {
+      return lon_;
+    }
+    public int getLonCount() {
+      return lon_.size();
+    }
+    public long getLon(int index) {
+      return lon_.get(index);
+    }
+    private int lonMemoizedSerializedSize = -1;
+    
+    // repeated int32 keys_vals = 10 [packed = true];
+    public static final int KEYS_VALS_FIELD_NUMBER = 10;
+    private java.util.List<java.lang.Integer> keysVals_;
+    public java.util.List<java.lang.Integer>
+        getKeysValsList() {
+      return keysVals_;
+    }
+    public int getKeysValsCount() {
+      return keysVals_.size();
+    }
+    public int getKeysVals(int index) {
+      return keysVals_.get(index);
+    }
+    private int keysValsMemoizedSerializedSize = -1;
+    
+    private void initFields() {
+      id_ = java.util.Collections.emptyList();;
+      denseinfo_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance();
+      lat_ = java.util.Collections.emptyList();;
+      lon_ = java.util.Collections.emptyList();;
+      keysVals_ = java.util.Collections.emptyList();;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (getIdList().size() > 0) {
+        output.writeRawVarint32(10);
+        output.writeRawVarint32(idMemoizedSerializedSize);
+      }
+      for (int i = 0; i < id_.size(); i++) {
+        output.writeSInt64NoTag(id_.get(i));
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeMessage(5, denseinfo_);
+      }
+      if (getLatList().size() > 0) {
+        output.writeRawVarint32(66);
+        output.writeRawVarint32(latMemoizedSerializedSize);
+      }
+      for (int i = 0; i < lat_.size(); i++) {
+        output.writeSInt64NoTag(lat_.get(i));
+      }
+      if (getLonList().size() > 0) {
+        output.writeRawVarint32(74);
+        output.writeRawVarint32(lonMemoizedSerializedSize);
+      }
+      for (int i = 0; i < lon_.size(); i++) {
+        output.writeSInt64NoTag(lon_.get(i));
+      }
+      if (getKeysValsList().size() > 0) {
+        output.writeRawVarint32(82);
+        output.writeRawVarint32(keysValsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < keysVals_.size(); i++) {
+        output.writeInt32NoTag(keysVals_.get(i));
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      {
+        int dataSize = 0;
+        for (int i = 0; i < id_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(id_.get(i));
+        }
+        size += dataSize;
+        if (!getIdList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        idMemoizedSerializedSize = dataSize;
+      }
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(5, denseinfo_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < lat_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(lat_.get(i));
+        }
+        size += dataSize;
+        if (!getLatList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        latMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < lon_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(lon_.get(i));
+        }
+        size += dataSize;
+        if (!getLonList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        lonMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < keysVals_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeInt32SizeNoTag(keysVals_.get(i));
+        }
+        size += dataSize;
+        if (!getKeysValsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        keysValsMemoizedSerializedSize = dataSize;
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodesOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        id_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        denseinfo_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000002);
+        lat_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        lon_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        keysVals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes result = new org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((bitField0_ & 0x00000001) == 0x00000001)) {
+          id_ = java.util.Collections.unmodifiableList(id_);
+          bitField0_ = (bitField0_ & ~0x00000001);
+        }
+        result.id_ = id_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.denseinfo_ = denseinfo_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          lat_ = java.util.Collections.unmodifiableList(lat_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.lat_ = lat_;
+        if (((bitField0_ & 0x00000008) == 0x00000008)) {
+          lon_ = java.util.Collections.unmodifiableList(lon_);
+          bitField0_ = (bitField0_ & ~0x00000008);
+        }
+        result.lon_ = lon_;
+        if (((bitField0_ & 0x00000010) == 0x00000010)) {
+          keysVals_ = java.util.Collections.unmodifiableList(keysVals_);
+          bitField0_ = (bitField0_ & ~0x00000010);
+        }
+        result.keysVals_ = keysVals_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes.getDefaultInstance()) return this;
+        if (!other.id_.isEmpty()) {
+          if (id_.isEmpty()) {
+            id_ = other.id_;
+            bitField0_ = (bitField0_ & ~0x00000001);
+          } else {
+            ensureIdIsMutable();
+            id_.addAll(other.id_);
+          }
+          
+        }
+        if (other.hasDenseinfo()) {
+          mergeDenseinfo(other.getDenseinfo());
+        }
+        if (!other.lat_.isEmpty()) {
+          if (lat_.isEmpty()) {
+            lat_ = other.lat_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureLatIsMutable();
+            lat_.addAll(other.lat_);
+          }
+          
+        }
+        if (!other.lon_.isEmpty()) {
+          if (lon_.isEmpty()) {
+            lon_ = other.lon_;
+            bitField0_ = (bitField0_ & ~0x00000008);
+          } else {
+            ensureLonIsMutable();
+            lon_.addAll(other.lon_);
+          }
+          
+        }
+        if (!other.keysVals_.isEmpty()) {
+          if (keysVals_.isEmpty()) {
+            keysVals_ = other.keysVals_;
+            bitField0_ = (bitField0_ & ~0x00000010);
+          } else {
+            ensureKeysValsIsMutable();
+            keysVals_.addAll(other.keysVals_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              ensureIdIsMutable();
+              id_.add(input.readSInt64());
+              break;
+            }
+            case 10: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addId(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 42: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.newBuilder();
+              if (hasDenseinfo()) {
+                subBuilder.mergeFrom(getDenseinfo());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setDenseinfo(subBuilder.buildPartial());
+              break;
+            }
+            case 64: {
+              ensureLatIsMutable();
+              lat_.add(input.readSInt64());
+              break;
+            }
+            case 66: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addLat(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 72: {
+              ensureLonIsMutable();
+              lon_.add(input.readSInt64());
+              break;
+            }
+            case 74: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addLon(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 80: {
+              ensureKeysValsIsMutable();
+              keysVals_.add(input.readInt32());
+              break;
+            }
+            case 82: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addKeysVals(input.readInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // repeated sint64 id = 1 [packed = true];
+      private java.util.List<java.lang.Long> id_ = java.util.Collections.emptyList();;
+      private void ensureIdIsMutable() {
+        if (!((bitField0_ & 0x00000001) == 0x00000001)) {
+          id_ = new java.util.ArrayList<java.lang.Long>(id_);
+          bitField0_ |= 0x00000001;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getIdList() {
+        return java.util.Collections.unmodifiableList(id_);
+      }
+      public int getIdCount() {
+        return id_.size();
+      }
+      public long getId(int index) {
+        return id_.get(index);
+      }
+      public Builder setId(
+          int index, long value) {
+        ensureIdIsMutable();
+        id_.set(index, value);
+        
+        return this;
+      }
+      public Builder addId(long value) {
+        ensureIdIsMutable();
+        id_.add(value);
+        
+        return this;
+      }
+      public Builder addAllId(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureIdIsMutable();
+        super.addAll(values, id_);
+        
+        return this;
+      }
+      public Builder clearId() {
+        id_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        
+        return this;
+      }
+      
+      // optional .OSMPBF.DenseInfo denseinfo = 5;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo denseinfo_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance();
+      public boolean hasDenseinfo() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo getDenseinfo() {
+        return denseinfo_;
+      }
+      public Builder setDenseinfo(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        denseinfo_ = value;
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder setDenseinfo(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.Builder builderForValue) {
+        denseinfo_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder mergeDenseinfo(org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo value) {
+        if (((bitField0_ & 0x00000002) == 0x00000002) &&
+            denseinfo_ != org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance()) {
+          denseinfo_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.newBuilder(denseinfo_).mergeFrom(value).buildPartial();
+        } else {
+          denseinfo_ = value;
+        }
+        
+        bitField0_ |= 0x00000002;
+        return this;
+      }
+      public Builder clearDenseinfo() {
+        denseinfo_ = org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000002);
+        return this;
+      }
+      
+      // repeated sint64 lat = 8 [packed = true];
+      private java.util.List<java.lang.Long> lat_ = java.util.Collections.emptyList();;
+      private void ensureLatIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          lat_ = new java.util.ArrayList<java.lang.Long>(lat_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getLatList() {
+        return java.util.Collections.unmodifiableList(lat_);
+      }
+      public int getLatCount() {
+        return lat_.size();
+      }
+      public long getLat(int index) {
+        return lat_.get(index);
+      }
+      public Builder setLat(
+          int index, long value) {
+        ensureLatIsMutable();
+        lat_.set(index, value);
+        
+        return this;
+      }
+      public Builder addLat(long value) {
+        ensureLatIsMutable();
+        lat_.add(value);
+        
+        return this;
+      }
+      public Builder addAllLat(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureLatIsMutable();
+        super.addAll(values, lat_);
+        
+        return this;
+      }
+      public Builder clearLat() {
+        lat_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      
+      // repeated sint64 lon = 9 [packed = true];
+      private java.util.List<java.lang.Long> lon_ = java.util.Collections.emptyList();;
+      private void ensureLonIsMutable() {
+        if (!((bitField0_ & 0x00000008) == 0x00000008)) {
+          lon_ = new java.util.ArrayList<java.lang.Long>(lon_);
+          bitField0_ |= 0x00000008;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getLonList() {
+        return java.util.Collections.unmodifiableList(lon_);
+      }
+      public int getLonCount() {
+        return lon_.size();
+      }
+      public long getLon(int index) {
+        return lon_.get(index);
+      }
+      public Builder setLon(
+          int index, long value) {
+        ensureLonIsMutable();
+        lon_.set(index, value);
+        
+        return this;
+      }
+      public Builder addLon(long value) {
+        ensureLonIsMutable();
+        lon_.add(value);
+        
+        return this;
+      }
+      public Builder addAllLon(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureLonIsMutable();
+        super.addAll(values, lon_);
+        
+        return this;
+      }
+      public Builder clearLon() {
+        lon_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        
+        return this;
+      }
+      
+      // repeated int32 keys_vals = 10 [packed = true];
+      private java.util.List<java.lang.Integer> keysVals_ = java.util.Collections.emptyList();;
+      private void ensureKeysValsIsMutable() {
+        if (!((bitField0_ & 0x00000010) == 0x00000010)) {
+          keysVals_ = new java.util.ArrayList<java.lang.Integer>(keysVals_);
+          bitField0_ |= 0x00000010;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getKeysValsList() {
+        return java.util.Collections.unmodifiableList(keysVals_);
+      }
+      public int getKeysValsCount() {
+        return keysVals_.size();
+      }
+      public int getKeysVals(int index) {
+        return keysVals_.get(index);
+      }
+      public Builder setKeysVals(
+          int index, int value) {
+        ensureKeysValsIsMutable();
+        keysVals_.set(index, value);
+        
+        return this;
+      }
+      public Builder addKeysVals(int value) {
+        ensureKeysValsIsMutable();
+        keysVals_.add(value);
+        
+        return this;
+      }
+      public Builder addAllKeysVals(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureKeysValsIsMutable();
+        super.addAll(values, keysVals_);
+        
+        return this;
+      }
+      public Builder clearKeysVals() {
+        keysVals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.DenseNodes)
+    }
+    
+    static {
+      defaultInstance = new DenseNodes(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.DenseNodes)
+  }
+  
+  public interface WayOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required int64 id = 1;
+    boolean hasId();
+    long getId();
+    
+    // repeated uint32 keys = 2 [packed = true];
+    java.util.List<java.lang.Integer> getKeysList();
+    int getKeysCount();
+    int getKeys(int index);
+    
+    // repeated uint32 vals = 3 [packed = true];
+    java.util.List<java.lang.Integer> getValsList();
+    int getValsCount();
+    int getVals(int index);
+    
+    // optional .OSMPBF.Info info = 4;
+    boolean hasInfo();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo();
+    
+    // repeated sint64 refs = 8 [packed = true];
+    java.util.List<java.lang.Long> getRefsList();
+    int getRefsCount();
+    long getRefs(int index);
+  }
+  public static final class Way extends
+      com.google.protobuf.GeneratedMessageLite
+      implements WayOrBuilder {
+    // Use Way.newBuilder() to construct.
+    private Way(Builder builder) {
+      super(builder);
+    }
+    private Way(boolean noInit) {}
+    
+    private static final Way defaultInstance;
+    public static Way getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Way getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    private int bitField0_;
+    // required int64 id = 1;
+    public static final int ID_FIELD_NUMBER = 1;
+    private long id_;
+    public boolean hasId() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public long getId() {
+      return id_;
+    }
+    
+    // repeated uint32 keys = 2 [packed = true];
+    public static final int KEYS_FIELD_NUMBER = 2;
+    private java.util.List<java.lang.Integer> keys_;
+    public java.util.List<java.lang.Integer>
+        getKeysList() {
+      return keys_;
+    }
+    public int getKeysCount() {
+      return keys_.size();
+    }
+    public int getKeys(int index) {
+      return keys_.get(index);
+    }
+    private int keysMemoizedSerializedSize = -1;
+    
+    // repeated uint32 vals = 3 [packed = true];
+    public static final int VALS_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Integer> vals_;
+    public java.util.List<java.lang.Integer>
+        getValsList() {
+      return vals_;
+    }
+    public int getValsCount() {
+      return vals_.size();
+    }
+    public int getVals(int index) {
+      return vals_.get(index);
+    }
+    private int valsMemoizedSerializedSize = -1;
+    
+    // optional .OSMPBF.Info info = 4;
+    public static final int INFO_FIELD_NUMBER = 4;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_;
+    public boolean hasInfo() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+      return info_;
+    }
+    
+    // repeated sint64 refs = 8 [packed = true];
+    public static final int REFS_FIELD_NUMBER = 8;
+    private java.util.List<java.lang.Long> refs_;
+    public java.util.List<java.lang.Long>
+        getRefsList() {
+      return refs_;
+    }
+    public int getRefsCount() {
+      return refs_.size();
+    }
+    public long getRefs(int index) {
+      return refs_.get(index);
+    }
+    private int refsMemoizedSerializedSize = -1;
+    
+    private void initFields() {
+      id_ = 0L;
+      keys_ = java.util.Collections.emptyList();;
+      vals_ = java.util.Collections.emptyList();;
+      info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      refs_ = java.util.Collections.emptyList();;
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(1, id_);
+      }
+      if (getKeysList().size() > 0) {
+        output.writeRawVarint32(18);
+        output.writeRawVarint32(keysMemoizedSerializedSize);
+      }
+      for (int i = 0; i < keys_.size(); i++) {
+        output.writeUInt32NoTag(keys_.get(i));
+      }
+      if (getValsList().size() > 0) {
+        output.writeRawVarint32(26);
+        output.writeRawVarint32(valsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < vals_.size(); i++) {
+        output.writeUInt32NoTag(vals_.get(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeMessage(4, info_);
+      }
+      if (getRefsList().size() > 0) {
+        output.writeRawVarint32(66);
+        output.writeRawVarint32(refsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < refs_.size(); i++) {
+        output.writeSInt64NoTag(refs_.get(i));
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(1, id_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < keys_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(keys_.get(i));
+        }
+        size += dataSize;
+        if (!getKeysList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        keysMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < vals_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(vals_.get(i));
+        }
+        size += dataSize;
+        if (!getValsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        valsMemoizedSerializedSize = dataSize;
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, info_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < refs_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(refs_.get(i));
+        }
+        size += dataSize;
+        if (!getRefsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        refsMemoizedSerializedSize = dataSize;
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Way parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.Way prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Way, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.WayOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.Way.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        id_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000008);
+        refs_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Way getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.Way.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Way build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Way result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Way buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Way result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Way buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Way result = new org.openstreetmap.osmosis.osmbinary.Osmformat.Way(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.id_ = id_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = java.util.Collections.unmodifiableList(keys_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.keys_ = keys_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = java.util.Collections.unmodifiableList(vals_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.vals_ = vals_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.info_ = info_;
+        if (((bitField0_ & 0x00000010) == 0x00000010)) {
+          refs_ = java.util.Collections.unmodifiableList(refs_);
+          bitField0_ = (bitField0_ & ~0x00000010);
+        }
+        result.refs_ = refs_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.Way other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.Way.getDefaultInstance()) return this;
+        if (other.hasId()) {
+          setId(other.getId());
+        }
+        if (!other.keys_.isEmpty()) {
+          if (keys_.isEmpty()) {
+            keys_ = other.keys_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensureKeysIsMutable();
+            keys_.addAll(other.keys_);
+          }
+          
+        }
+        if (!other.vals_.isEmpty()) {
+          if (vals_.isEmpty()) {
+            vals_ = other.vals_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureValsIsMutable();
+            vals_.addAll(other.vals_);
+          }
+          
+        }
+        if (other.hasInfo()) {
+          mergeInfo(other.getInfo());
+        }
+        if (!other.refs_.isEmpty()) {
+          if (refs_.isEmpty()) {
+            refs_ = other.refs_;
+            bitField0_ = (bitField0_ & ~0x00000010);
+          } else {
+            ensureRefsIsMutable();
+            refs_.addAll(other.refs_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasId()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              id_ = input.readInt64();
+              break;
+            }
+            case 16: {
+              ensureKeysIsMutable();
+              keys_.add(input.readUInt32());
+              break;
+            }
+            case 18: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addKeys(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 24: {
+              ensureValsIsMutable();
+              vals_.add(input.readUInt32());
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addVals(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 34: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder();
+              if (hasInfo()) {
+                subBuilder.mergeFrom(getInfo());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setInfo(subBuilder.buildPartial());
+              break;
+            }
+            case 64: {
+              ensureRefsIsMutable();
+              refs_.add(input.readSInt64());
+              break;
+            }
+            case 66: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addRefs(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required int64 id = 1;
+      private long id_ ;
+      public boolean hasId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public long getId() {
+        return id_;
+      }
+      public Builder setId(long value) {
+        bitField0_ |= 0x00000001;
+        id_ = value;
+        
+        return this;
+      }
+      public Builder clearId() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        id_ = 0L;
+        
+        return this;
+      }
+      
+      // repeated uint32 keys = 2 [packed = true];
+      private java.util.List<java.lang.Integer> keys_ = java.util.Collections.emptyList();;
+      private void ensureKeysIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = new java.util.ArrayList<java.lang.Integer>(keys_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getKeysList() {
+        return java.util.Collections.unmodifiableList(keys_);
+      }
+      public int getKeysCount() {
+        return keys_.size();
+      }
+      public int getKeys(int index) {
+        return keys_.get(index);
+      }
+      public Builder setKeys(
+          int index, int value) {
+        ensureKeysIsMutable();
+        keys_.set(index, value);
+        
+        return this;
+      }
+      public Builder addKeys(int value) {
+        ensureKeysIsMutable();
+        keys_.add(value);
+        
+        return this;
+      }
+      public Builder addAllKeys(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureKeysIsMutable();
+        super.addAll(values, keys_);
+        
+        return this;
+      }
+      public Builder clearKeys() {
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      
+      // repeated uint32 vals = 3 [packed = true];
+      private java.util.List<java.lang.Integer> vals_ = java.util.Collections.emptyList();;
+      private void ensureValsIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = new java.util.ArrayList<java.lang.Integer>(vals_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getValsList() {
+        return java.util.Collections.unmodifiableList(vals_);
+      }
+      public int getValsCount() {
+        return vals_.size();
+      }
+      public int getVals(int index) {
+        return vals_.get(index);
+      }
+      public Builder setVals(
+          int index, int value) {
+        ensureValsIsMutable();
+        vals_.set(index, value);
+        
+        return this;
+      }
+      public Builder addVals(int value) {
+        ensureValsIsMutable();
+        vals_.add(value);
+        
+        return this;
+      }
+      public Builder addAllVals(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureValsIsMutable();
+        super.addAll(values, vals_);
+        
+        return this;
+      }
+      public Builder clearVals() {
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      
+      // optional .OSMPBF.Info info = 4;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      public boolean hasInfo() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+        return info_;
+      }
+      public Builder setInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        info_ = value;
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder setInfo(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder builderForValue) {
+        info_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder mergeInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (((bitField0_ & 0x00000008) == 0x00000008) &&
+            info_ != org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance()) {
+          info_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder(info_).mergeFrom(value).buildPartial();
+        } else {
+          info_ = value;
+        }
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder clearInfo() {
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000008);
+        return this;
+      }
+      
+      // repeated sint64 refs = 8 [packed = true];
+      private java.util.List<java.lang.Long> refs_ = java.util.Collections.emptyList();;
+      private void ensureRefsIsMutable() {
+        if (!((bitField0_ & 0x00000010) == 0x00000010)) {
+          refs_ = new java.util.ArrayList<java.lang.Long>(refs_);
+          bitField0_ |= 0x00000010;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getRefsList() {
+        return java.util.Collections.unmodifiableList(refs_);
+      }
+      public int getRefsCount() {
+        return refs_.size();
+      }
+      public long getRefs(int index) {
+        return refs_.get(index);
+      }
+      public Builder setRefs(
+          int index, long value) {
+        ensureRefsIsMutable();
+        refs_.set(index, value);
+        
+        return this;
+      }
+      public Builder addRefs(long value) {
+        ensureRefsIsMutable();
+        refs_.add(value);
+        
+        return this;
+      }
+      public Builder addAllRefs(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureRefsIsMutable();
+        super.addAll(values, refs_);
+        
+        return this;
+      }
+      public Builder clearRefs() {
+        refs_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.Way)
+    }
+    
+    static {
+      defaultInstance = new Way(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.Way)
+  }
+  
+  public interface RelationOrBuilder
+      extends com.google.protobuf.MessageLiteOrBuilder {
+    
+    // required int64 id = 1;
+    boolean hasId();
+    long getId();
+    
+    // repeated uint32 keys = 2 [packed = true];
+    java.util.List<java.lang.Integer> getKeysList();
+    int getKeysCount();
+    int getKeys(int index);
+    
+    // repeated uint32 vals = 3 [packed = true];
+    java.util.List<java.lang.Integer> getValsList();
+    int getValsCount();
+    int getVals(int index);
+    
+    // optional .OSMPBF.Info info = 4;
+    boolean hasInfo();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo();
+    
+    // repeated int32 roles_sid = 8 [packed = true];
+    java.util.List<java.lang.Integer> getRolesSidList();
+    int getRolesSidCount();
+    int getRolesSid(int index);
+    
+    // repeated sint64 memids = 9 [packed = true];
+    java.util.List<java.lang.Long> getMemidsList();
+    int getMemidsCount();
+    long getMemids(int index);
+    
+    // repeated .OSMPBF.Relation.MemberType types = 10 [packed = true];
+    java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> getTypesList();
+    int getTypesCount();
+    org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType getTypes(int index);
+  }
+  public static final class Relation extends
+      com.google.protobuf.GeneratedMessageLite
+      implements RelationOrBuilder {
+    // Use Relation.newBuilder() to construct.
+    private Relation(Builder builder) {
+      super(builder);
+    }
+    private Relation(boolean noInit) {}
+    
+    private static final Relation defaultInstance;
+    public static Relation getDefaultInstance() {
+      return defaultInstance;
+    }
+    
+    public Relation getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+    
+    public enum MemberType
+        implements com.google.protobuf.Internal.EnumLite {
+      NODE(0, 0),
+      WAY(1, 1),
+      RELATION(2, 2),
+      ;
+      
+      public static final int NODE_VALUE = 0;
+      public static final int WAY_VALUE = 1;
+      public static final int RELATION_VALUE = 2;
+      
+      
+      public final int getNumber() { return value; }
+      
+      public static MemberType valueOf(int value) {
+        switch (value) {
+          case 0: return NODE;
+          case 1: return WAY;
+          case 2: return RELATION;
+          default: return null;
+        }
+      }
+      
+      public static com.google.protobuf.Internal.EnumLiteMap<MemberType>
+          internalGetValueMap() {
+        return internalValueMap;
+      }
+      private static com.google.protobuf.Internal.EnumLiteMap<MemberType>
+          internalValueMap =
+            new com.google.protobuf.Internal.EnumLiteMap<MemberType>() {
+              public MemberType findValueByNumber(int number) {
+                return MemberType.valueOf(number);
+              }
+            };
+      
+      private final int value;
+      
+      private MemberType(int index, int value) {
+        this.value = value;
+      }
+      
+      // @@protoc_insertion_point(enum_scope:OSMPBF.Relation.MemberType)
+    }
+    
+    private int bitField0_;
+    // required int64 id = 1;
+    public static final int ID_FIELD_NUMBER = 1;
+    private long id_;
+    public boolean hasId() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    public long getId() {
+      return id_;
+    }
+    
+    // repeated uint32 keys = 2 [packed = true];
+    public static final int KEYS_FIELD_NUMBER = 2;
+    private java.util.List<java.lang.Integer> keys_;
+    public java.util.List<java.lang.Integer>
+        getKeysList() {
+      return keys_;
+    }
+    public int getKeysCount() {
+      return keys_.size();
+    }
+    public int getKeys(int index) {
+      return keys_.get(index);
+    }
+    private int keysMemoizedSerializedSize = -1;
+    
+    // repeated uint32 vals = 3 [packed = true];
+    public static final int VALS_FIELD_NUMBER = 3;
+    private java.util.List<java.lang.Integer> vals_;
+    public java.util.List<java.lang.Integer>
+        getValsList() {
+      return vals_;
+    }
+    public int getValsCount() {
+      return vals_.size();
+    }
+    public int getVals(int index) {
+      return vals_.get(index);
+    }
+    private int valsMemoizedSerializedSize = -1;
+    
+    // optional .OSMPBF.Info info = 4;
+    public static final int INFO_FIELD_NUMBER = 4;
+    private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_;
+    public boolean hasInfo() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+      return info_;
+    }
+    
+    // repeated int32 roles_sid = 8 [packed = true];
+    public static final int ROLES_SID_FIELD_NUMBER = 8;
+    private java.util.List<java.lang.Integer> rolesSid_;
+    public java.util.List<java.lang.Integer>
+        getRolesSidList() {
+      return rolesSid_;
+    }
+    public int getRolesSidCount() {
+      return rolesSid_.size();
+    }
+    public int getRolesSid(int index) {
+      return rolesSid_.get(index);
+    }
+    private int rolesSidMemoizedSerializedSize = -1;
+    
+    // repeated sint64 memids = 9 [packed = true];
+    public static final int MEMIDS_FIELD_NUMBER = 9;
+    private java.util.List<java.lang.Long> memids_;
+    public java.util.List<java.lang.Long>
+        getMemidsList() {
+      return memids_;
+    }
+    public int getMemidsCount() {
+      return memids_.size();
+    }
+    public long getMemids(int index) {
+      return memids_.get(index);
+    }
+    private int memidsMemoizedSerializedSize = -1;
+    
+    // repeated .OSMPBF.Relation.MemberType types = 10 [packed = true];
+    public static final int TYPES_FIELD_NUMBER = 10;
+    private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> types_;
+    public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> getTypesList() {
+      return types_;
+    }
+    public int getTypesCount() {
+      return types_.size();
+    }
+    public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType getTypes(int index) {
+      return types_.get(index);
+    }
+    private int typesMemoizedSerializedSize;
+    
+    private void initFields() {
+      id_ = 0L;
+      keys_ = java.util.Collections.emptyList();;
+      vals_ = java.util.Collections.emptyList();;
+      info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      rolesSid_ = java.util.Collections.emptyList();;
+      memids_ = java.util.Collections.emptyList();;
+      types_ = java.util.Collections.emptyList();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+      
+      if (!hasId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+    
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(1, id_);
+      }
+      if (getKeysList().size() > 0) {
+        output.writeRawVarint32(18);
+        output.writeRawVarint32(keysMemoizedSerializedSize);
+      }
+      for (int i = 0; i < keys_.size(); i++) {
+        output.writeUInt32NoTag(keys_.get(i));
+      }
+      if (getValsList().size() > 0) {
+        output.writeRawVarint32(26);
+        output.writeRawVarint32(valsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < vals_.size(); i++) {
+        output.writeUInt32NoTag(vals_.get(i));
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeMessage(4, info_);
+      }
+      if (getRolesSidList().size() > 0) {
+        output.writeRawVarint32(66);
+        output.writeRawVarint32(rolesSidMemoizedSerializedSize);
+      }
+      for (int i = 0; i < rolesSid_.size(); i++) {
+        output.writeInt32NoTag(rolesSid_.get(i));
+      }
+      if (getMemidsList().size() > 0) {
+        output.writeRawVarint32(74);
+        output.writeRawVarint32(memidsMemoizedSerializedSize);
+      }
+      for (int i = 0; i < memids_.size(); i++) {
+        output.writeSInt64NoTag(memids_.get(i));
+      }
+      if (getTypesList().size() > 0) {
+        output.writeRawVarint32(82);
+        output.writeRawVarint32(typesMemoizedSerializedSize);
+      }
+      for (int i = 0; i < types_.size(); i++) {
+        output.writeEnumNoTag(types_.get(i).getNumber());
+      }
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+    
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(1, id_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < keys_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(keys_.get(i));
+        }
+        size += dataSize;
+        if (!getKeysList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        keysMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < vals_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeUInt32SizeNoTag(vals_.get(i));
+        }
+        size += dataSize;
+        if (!getValsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        valsMemoizedSerializedSize = dataSize;
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(4, info_);
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < rolesSid_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeInt32SizeNoTag(rolesSid_.get(i));
+        }
+        size += dataSize;
+        if (!getRolesSidList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        rolesSidMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < memids_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeSInt64SizeNoTag(memids_.get(i));
+        }
+        size += dataSize;
+        if (!getMemidsList().isEmpty()) {
+          size += 1;
+          size += com.google.protobuf.CodedOutputStream
+              .computeInt32SizeNoTag(dataSize);
+        }
+        memidsMemoizedSerializedSize = dataSize;
+      }
+      {
+        int dataSize = 0;
+        for (int i = 0; i < types_.size(); i++) {
+          dataSize += com.google.protobuf.CodedOutputStream
+            .computeEnumSizeNoTag(types_.get(i).getNumber());
+        }
+        size += dataSize;
+        if (!getTypesList().isEmpty()) {  size += 1;
+          size += com.google.protobuf.CodedOutputStream
+            .computeRawVarint32Size(dataSize);
+        }typesMemoizedSerializedSize = dataSize;
+      }
+      memoizedSerializedSize = size;
+      return size;
+    }
+    
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+    
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return newBuilder().mergeFrom(data, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      Builder builder = newBuilder();
+      if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
+        return builder.buildParsed();
+      } else {
+        return null;
+      }
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input).buildParsed();
+    }
+    public static org.openstreetmap.osmosis.osmbinary.Osmformat.Relation parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return newBuilder().mergeFrom(input, extensionRegistry)
+               .buildParsed();
+    }
+    
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.openstreetmap.osmosis.osmbinary.Osmformat.Relation prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+    
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessageLite.Builder<
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Relation, Builder>
+        implements org.openstreetmap.osmosis.osmbinary.Osmformat.RelationOrBuilder {
+      // Construct using org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+      
+      private void maybeForceBuilderInitialization() {
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+      
+      public Builder clear() {
+        super.clear();
+        id_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        bitField0_ = (bitField0_ & ~0x00000008);
+        rolesSid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        memids_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        types_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000040);
+        return this;
+      }
+      
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation getDefaultInstanceForType() {
+        return org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.getDefaultInstance();
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation build() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Relation result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+      
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Relation buildParsed()
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Relation result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(
+            result).asInvalidProtocolBufferException();
+        }
+        return result;
+      }
+      
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation buildPartial() {
+        org.openstreetmap.osmosis.osmbinary.Osmformat.Relation result = new org.openstreetmap.osmosis.osmbinary.Osmformat.Relation(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.id_ = id_;
+        if (((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = java.util.Collections.unmodifiableList(keys_);
+          bitField0_ = (bitField0_ & ~0x00000002);
+        }
+        result.keys_ = keys_;
+        if (((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = java.util.Collections.unmodifiableList(vals_);
+          bitField0_ = (bitField0_ & ~0x00000004);
+        }
+        result.vals_ = vals_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.info_ = info_;
+        if (((bitField0_ & 0x00000010) == 0x00000010)) {
+          rolesSid_ = java.util.Collections.unmodifiableList(rolesSid_);
+          bitField0_ = (bitField0_ & ~0x00000010);
+        }
+        result.rolesSid_ = rolesSid_;
+        if (((bitField0_ & 0x00000020) == 0x00000020)) {
+          memids_ = java.util.Collections.unmodifiableList(memids_);
+          bitField0_ = (bitField0_ & ~0x00000020);
+        }
+        result.memids_ = memids_;
+        if (((bitField0_ & 0x00000040) == 0x00000040)) {
+          types_ = java.util.Collections.unmodifiableList(types_);
+          bitField0_ = (bitField0_ & ~0x00000040);
+        }
+        result.types_ = types_;
+        result.bitField0_ = to_bitField0_;
+        return result;
+      }
+      
+      public Builder mergeFrom(org.openstreetmap.osmosis.osmbinary.Osmformat.Relation other) {
+        if (other == org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.getDefaultInstance()) return this;
+        if (other.hasId()) {
+          setId(other.getId());
+        }
+        if (!other.keys_.isEmpty()) {
+          if (keys_.isEmpty()) {
+            keys_ = other.keys_;
+            bitField0_ = (bitField0_ & ~0x00000002);
+          } else {
+            ensureKeysIsMutable();
+            keys_.addAll(other.keys_);
+          }
+          
+        }
+        if (!other.vals_.isEmpty()) {
+          if (vals_.isEmpty()) {
+            vals_ = other.vals_;
+            bitField0_ = (bitField0_ & ~0x00000004);
+          } else {
+            ensureValsIsMutable();
+            vals_.addAll(other.vals_);
+          }
+          
+        }
+        if (other.hasInfo()) {
+          mergeInfo(other.getInfo());
+        }
+        if (!other.rolesSid_.isEmpty()) {
+          if (rolesSid_.isEmpty()) {
+            rolesSid_ = other.rolesSid_;
+            bitField0_ = (bitField0_ & ~0x00000010);
+          } else {
+            ensureRolesSidIsMutable();
+            rolesSid_.addAll(other.rolesSid_);
+          }
+          
+        }
+        if (!other.memids_.isEmpty()) {
+          if (memids_.isEmpty()) {
+            memids_ = other.memids_;
+            bitField0_ = (bitField0_ & ~0x00000020);
+          } else {
+            ensureMemidsIsMutable();
+            memids_.addAll(other.memids_);
+          }
+          
+        }
+        if (!other.types_.isEmpty()) {
+          if (types_.isEmpty()) {
+            types_ = other.types_;
+            bitField0_ = (bitField0_ & ~0x00000040);
+          } else {
+            ensureTypesIsMutable();
+            types_.addAll(other.types_);
+          }
+          
+        }
+        return this;
+      }
+      
+      public final boolean isInitialized() {
+        if (!hasId()) {
+          
+          return false;
+        }
+        return true;
+      }
+      
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        while (true) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              
+              return this;
+            default: {
+              if (!parseUnknownField(input, extensionRegistry, tag)) {
+                
+                return this;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              id_ = input.readInt64();
+              break;
+            }
+            case 16: {
+              ensureKeysIsMutable();
+              keys_.add(input.readUInt32());
+              break;
+            }
+            case 18: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addKeys(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 24: {
+              ensureValsIsMutable();
+              vals_.add(input.readUInt32());
+              break;
+            }
+            case 26: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addVals(input.readUInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 34: {
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder subBuilder = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder();
+              if (hasInfo()) {
+                subBuilder.mergeFrom(getInfo());
+              }
+              input.readMessage(subBuilder, extensionRegistry);
+              setInfo(subBuilder.buildPartial());
+              break;
+            }
+            case 64: {
+              ensureRolesSidIsMutable();
+              rolesSid_.add(input.readInt32());
+              break;
+            }
+            case 66: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addRolesSid(input.readInt32());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 72: {
+              ensureMemidsIsMutable();
+              memids_.add(input.readSInt64());
+              break;
+            }
+            case 74: {
+              int length = input.readRawVarint32();
+              int limit = input.pushLimit(length);
+              while (input.getBytesUntilLimit() > 0) {
+                addMemids(input.readSInt64());
+              }
+              input.popLimit(limit);
+              break;
+            }
+            case 80: {
+              int rawValue = input.readEnum();
+              org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType value = org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType.valueOf(rawValue);
+              if (value != null) {
+                addTypes(value);
+              }
+              break;
+            }
+            case 82: {
+              int length = input.readRawVarint32();
+              int oldLimit = input.pushLimit(length);
+              while(input.getBytesUntilLimit() > 0) {
+                int rawValue = input.readEnum();
+                org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType value = org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType.valueOf(rawValue);
+                if (value != null) {
+                  addTypes(value);
+                }
+              }
+              input.popLimit(oldLimit);
+              break;
+            }
+          }
+        }
+      }
+      
+      private int bitField0_;
+      
+      // required int64 id = 1;
+      private long id_ ;
+      public boolean hasId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      public long getId() {
+        return id_;
+      }
+      public Builder setId(long value) {
+        bitField0_ |= 0x00000001;
+        id_ = value;
+        
+        return this;
+      }
+      public Builder clearId() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        id_ = 0L;
+        
+        return this;
+      }
+      
+      // repeated uint32 keys = 2 [packed = true];
+      private java.util.List<java.lang.Integer> keys_ = java.util.Collections.emptyList();;
+      private void ensureKeysIsMutable() {
+        if (!((bitField0_ & 0x00000002) == 0x00000002)) {
+          keys_ = new java.util.ArrayList<java.lang.Integer>(keys_);
+          bitField0_ |= 0x00000002;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getKeysList() {
+        return java.util.Collections.unmodifiableList(keys_);
+      }
+      public int getKeysCount() {
+        return keys_.size();
+      }
+      public int getKeys(int index) {
+        return keys_.get(index);
+      }
+      public Builder setKeys(
+          int index, int value) {
+        ensureKeysIsMutable();
+        keys_.set(index, value);
+        
+        return this;
+      }
+      public Builder addKeys(int value) {
+        ensureKeysIsMutable();
+        keys_.add(value);
+        
+        return this;
+      }
+      public Builder addAllKeys(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureKeysIsMutable();
+        super.addAll(values, keys_);
+        
+        return this;
+      }
+      public Builder clearKeys() {
+        keys_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        
+        return this;
+      }
+      
+      // repeated uint32 vals = 3 [packed = true];
+      private java.util.List<java.lang.Integer> vals_ = java.util.Collections.emptyList();;
+      private void ensureValsIsMutable() {
+        if (!((bitField0_ & 0x00000004) == 0x00000004)) {
+          vals_ = new java.util.ArrayList<java.lang.Integer>(vals_);
+          bitField0_ |= 0x00000004;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getValsList() {
+        return java.util.Collections.unmodifiableList(vals_);
+      }
+      public int getValsCount() {
+        return vals_.size();
+      }
+      public int getVals(int index) {
+        return vals_.get(index);
+      }
+      public Builder setVals(
+          int index, int value) {
+        ensureValsIsMutable();
+        vals_.set(index, value);
+        
+        return this;
+      }
+      public Builder addVals(int value) {
+        ensureValsIsMutable();
+        vals_.add(value);
+        
+        return this;
+      }
+      public Builder addAllVals(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureValsIsMutable();
+        super.addAll(values, vals_);
+        
+        return this;
+      }
+      public Builder clearVals() {
+        vals_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        
+        return this;
+      }
+      
+      // optional .OSMPBF.Info info = 4;
+      private org.openstreetmap.osmosis.osmbinary.Osmformat.Info info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+      public boolean hasInfo() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Info getInfo() {
+        return info_;
+      }
+      public Builder setInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        info_ = value;
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder setInfo(
+          org.openstreetmap.osmosis.osmbinary.Osmformat.Info.Builder builderForValue) {
+        info_ = builderForValue.build();
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder mergeInfo(org.openstreetmap.osmosis.osmbinary.Osmformat.Info value) {
+        if (((bitField0_ & 0x00000008) == 0x00000008) &&
+            info_ != org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance()) {
+          info_ =
+            org.openstreetmap.osmosis.osmbinary.Osmformat.Info.newBuilder(info_).mergeFrom(value).buildPartial();
+        } else {
+          info_ = value;
+        }
+        
+        bitField0_ |= 0x00000008;
+        return this;
+      }
+      public Builder clearInfo() {
+        info_ = org.openstreetmap.osmosis.osmbinary.Osmformat.Info.getDefaultInstance();
+        
+        bitField0_ = (bitField0_ & ~0x00000008);
+        return this;
+      }
+      
+      // repeated int32 roles_sid = 8 [packed = true];
+      private java.util.List<java.lang.Integer> rolesSid_ = java.util.Collections.emptyList();;
+      private void ensureRolesSidIsMutable() {
+        if (!((bitField0_ & 0x00000010) == 0x00000010)) {
+          rolesSid_ = new java.util.ArrayList<java.lang.Integer>(rolesSid_);
+          bitField0_ |= 0x00000010;
+         }
+      }
+      public java.util.List<java.lang.Integer>
+          getRolesSidList() {
+        return java.util.Collections.unmodifiableList(rolesSid_);
+      }
+      public int getRolesSidCount() {
+        return rolesSid_.size();
+      }
+      public int getRolesSid(int index) {
+        return rolesSid_.get(index);
+      }
+      public Builder setRolesSid(
+          int index, int value) {
+        ensureRolesSidIsMutable();
+        rolesSid_.set(index, value);
+        
+        return this;
+      }
+      public Builder addRolesSid(int value) {
+        ensureRolesSidIsMutable();
+        rolesSid_.add(value);
+        
+        return this;
+      }
+      public Builder addAllRolesSid(
+          java.lang.Iterable<? extends java.lang.Integer> values) {
+        ensureRolesSidIsMutable();
+        super.addAll(values, rolesSid_);
+        
+        return this;
+      }
+      public Builder clearRolesSid() {
+        rolesSid_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        
+        return this;
+      }
+      
+      // repeated sint64 memids = 9 [packed = true];
+      private java.util.List<java.lang.Long> memids_ = java.util.Collections.emptyList();;
+      private void ensureMemidsIsMutable() {
+        if (!((bitField0_ & 0x00000020) == 0x00000020)) {
+          memids_ = new java.util.ArrayList<java.lang.Long>(memids_);
+          bitField0_ |= 0x00000020;
+         }
+      }
+      public java.util.List<java.lang.Long>
+          getMemidsList() {
+        return java.util.Collections.unmodifiableList(memids_);
+      }
+      public int getMemidsCount() {
+        return memids_.size();
+      }
+      public long getMemids(int index) {
+        return memids_.get(index);
+      }
+      public Builder setMemids(
+          int index, long value) {
+        ensureMemidsIsMutable();
+        memids_.set(index, value);
+        
+        return this;
+      }
+      public Builder addMemids(long value) {
+        ensureMemidsIsMutable();
+        memids_.add(value);
+        
+        return this;
+      }
+      public Builder addAllMemids(
+          java.lang.Iterable<? extends java.lang.Long> values) {
+        ensureMemidsIsMutable();
+        super.addAll(values, memids_);
+        
+        return this;
+      }
+      public Builder clearMemids() {
+        memids_ = java.util.Collections.emptyList();;
+        bitField0_ = (bitField0_ & ~0x00000020);
+        
+        return this;
+      }
+      
+      // repeated .OSMPBF.Relation.MemberType types = 10 [packed = true];
+      private java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> types_ =
+        java.util.Collections.emptyList();
+      private void ensureTypesIsMutable() {
+        if (!((bitField0_ & 0x00000040) == 0x00000040)) {
+          types_ = new java.util.ArrayList<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType>(types_);
+          bitField0_ |= 0x00000040;
+        }
+      }
+      public java.util.List<org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> getTypesList() {
+        return java.util.Collections.unmodifiableList(types_);
+      }
+      public int getTypesCount() {
+        return types_.size();
+      }
+      public org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType getTypes(int index) {
+        return types_.get(index);
+      }
+      public Builder setTypes(
+          int index, org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureTypesIsMutable();
+        types_.set(index, value);
+        
+        return this;
+      }
+      public Builder addTypes(org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        ensureTypesIsMutable();
+        types_.add(value);
+        
+        return this;
+      }
+      public Builder addAllTypes(
+          java.lang.Iterable<? extends org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType> values) {
+        ensureTypesIsMutable();
+        super.addAll(values, types_);
+        
+        return this;
+      }
+      public Builder clearTypes() {
+        types_ = java.util.Collections.emptyList();
+        bitField0_ = (bitField0_ & ~0x00000040);
+        
+        return this;
+      }
+      
+      // @@protoc_insertion_point(builder_scope:OSMPBF.Relation)
+    }
+    
+    static {
+      defaultInstance = new Relation(true);
+      defaultInstance.initFields();
+    }
+    
+    // @@protoc_insertion_point(class_scope:OSMPBF.Relation)
+  }
+  
+  
+  static {
+  }
+  
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/osmosis-osm-binary/osmosis-protoc.sh b/osmosis-osm-binary/osmosis-protoc.sh
new file mode 100755
index 0000000..26287f7
--- /dev/null
+++ b/osmosis-osm-binary/osmosis-protoc.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+protoc --java_out=gen-src/main/java src/main/protobuf/fileformat.proto src/main/protobuf/osmformat.proto
diff --git a/osmosis-osm-binary/readme.txt b/osmosis-osm-binary/readme.txt
new file mode 100644
index 0000000..bd69ed9
--- /dev/null
+++ b/osmosis-osm-binary/readme.txt
@@ -0,0 +1,23 @@
+This is a customised version of Scott Crosby's PBF library.  The original
+version is here:
+https://github.com/scrosby/OSM-binary
+
+The original library is not available on Maven Central, but Osmosis has a
+dependency on it.  Therefore the original library has been modified to be built
+as part of Osmosis and the code is renamed to live under an Osmosis package
+name.  This re-packaging avoids any conflicts if other versions of the library
+exist on a target classpath.
+
+This codebase is maintained at the following location on the osmosis branch:
+https://github.com/brettch/OSM-binary
+
+The osmosis branch contains a number of customisations including:
+* Change to use an Osmosis package name.
+* Remove all C code, and any other unnecessary files.
+* Add Osmosis compatible Gradle build script.
+
+The Osmosis repository contains a copy of this codebase, but it is not the
+master location.  It is manually updated to remain in sync with the above
+location whenever necessary.  It would have been possible to do a true merge
+into the Osmosis repository, but this would have resulted in full history
+of both repositories being included which may have been confusing.
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinaryParser.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinaryParser.java
new file mode 100644
index 0000000..cdd5ce2
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinaryParser.java
@@ -0,0 +1,139 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary;
+
+
+import java.util.Date;
+import java.util.List;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.openstreetmap.osmosis.osmbinary.Osmformat;
+import org.openstreetmap.osmosis.osmbinary.file.BlockReaderAdapter;
+import org.openstreetmap.osmosis.osmbinary.file.FileBlock;
+import org.openstreetmap.osmosis.osmbinary.file.FileBlockPosition;
+
+public abstract class BinaryParser implements BlockReaderAdapter {
+    protected int granularity;
+    private long lat_offset;
+    private long lon_offset;
+    protected int date_granularity;
+    private String strings[];
+
+    /** Take a Info protocol buffer containing a date and convert it into a java Date object */
+    protected Date getDate(Osmformat.Info info) {
+      if (info.hasTimestamp()) {
+          return new Date(date_granularity * (long) info.getTimestamp());
+      } else
+          return NODATE;
+    }
+    public static final Date NODATE = new Date(-1);
+
+    /** Get a string based on the index used. 
+     * 
+     * Index 0 is reserved to use as a delimiter, therefore, index 1 corresponds to the first string in the table 
+     * @param id
+     * @return
+     */
+    protected String getStringById(int id) {
+      return strings[id];
+    }
+    
+    @Override
+    public void handleBlock(FileBlock message) {
+        // TODO Auto-generated method stub
+        try {
+            if (message.getType().equals("OSMHeader")) {
+                Osmformat.HeaderBlock headerblock = Osmformat.HeaderBlock
+                        .parseFrom(message.getData());
+                parse(headerblock);
+            } else if (message.getType().equals("OSMData")) {
+                Osmformat.PrimitiveBlock primblock = Osmformat.PrimitiveBlock
+                        .parseFrom(message.getData());
+                parse(primblock);
+            }
+        } catch (InvalidProtocolBufferException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            throw new Error("ParseError"); // TODO
+        }
+
+    }
+
+
+    @Override
+    public boolean skipBlock(FileBlockPosition block) {
+        // System.out.println("Seeing block of type: "+block.getType());
+        if (block.getType().equals("OSMData"))
+            return false;
+        if (block.getType().equals("OSMHeader"))
+            return false;
+        System.out.println("Skipped block of type: " + block.getType());
+        return true;
+    }
+    
+    
+    /** Convert a latitude value stored in a protobuf into a double, compensating for granularity and latitude offset */
+    public double parseLat(long degree) {
+      // Support non-zero offsets. (We don't currently generate them)
+      return (granularity * degree + lat_offset) * .000000001;
+    }
+
+    /** Convert a longitude value stored in a protobuf into a double, compensating for granularity and longitude offset */
+    public double parseLon(long degree) {
+      // Support non-zero offsets. (We don't currently generate them)
+       return (granularity * degree + lon_offset) * .000000001;
+    }
+   
+    /** Parse a Primitive block (containing a string table, other paramaters, and PrimitiveGroups */
+    public void parse(Osmformat.PrimitiveBlock block) {
+        Osmformat.StringTable stablemessage = block.getStringtable();
+        strings = new String[stablemessage.getSCount()];
+
+        for (int i = 0; i < strings.length; i++) {
+            strings[i] = stablemessage.getS(i).toStringUtf8();
+        }
+
+        granularity = block.getGranularity();
+        lat_offset = block.getLatOffset();
+        lon_offset = block.getLonOffset();
+        date_granularity = block.getDateGranularity();
+
+        for (Osmformat.PrimitiveGroup groupmessage : block
+                .getPrimitivegroupList()) {
+            // Exactly one of these should trigger on each loop.
+            parseNodes(groupmessage.getNodesList());
+            parseWays(groupmessage.getWaysList());
+            parseRelations(groupmessage.getRelationsList());
+            if (groupmessage.hasDense())
+                parseDense(groupmessage.getDense());
+        }
+    }
+    
+    /** Parse a list of Relation protocol buffers and send the resulting relations to a sink.  */
+    protected abstract void parseRelations(List<Osmformat.Relation> rels);
+    /** Parse a DenseNode protocol buffer and send the resulting nodes to a sink.  */
+    protected abstract void parseDense(Osmformat.DenseNodes nodes);
+    /** Parse a list of Node protocol buffers and send the resulting nodes to a sink.  */
+    protected abstract void parseNodes(List<Osmformat.Node> nodes);
+    /** Parse a list of Way protocol buffers and send the resulting ways to a sink.  */
+    protected abstract void parseWays(List<Osmformat.Way> ways);
+    /** Parse a header message. */
+    protected abstract void parse(Osmformat.HeaderBlock header);
+
+}
\ No newline at end of file
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinarySerializer.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinarySerializer.java
new file mode 100644
index 0000000..6556cbf
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/BinarySerializer.java
@@ -0,0 +1,161 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup;
+import org.openstreetmap.osmosis.osmbinary.file.BlockOutputStream;
+import org.openstreetmap.osmosis.osmbinary.file.FileBlock;
+
+/**
+ * Generic serializer common code
+ * 
+ * Serialize a set of blobs and process them. Subclasses implement handlers for
+ * different API's (osmosis, mkgmap, splitter, etc.)
+ * 
+ * All data is converted into PrimGroupWriterInterface objects, which are then
+ * ordered to process their data at the appropriate time.
+ * */
+
+public class BinarySerializer {
+
+    /**
+     * Interface used to write a group of primitives. One of these for each
+     * group type (Node, Way, Relation, DenseNode, Changeset)
+     */
+    protected interface PrimGroupWriterInterface {
+        /** This callback is invoked on each group that is going into the fileblock in order to give it a chance to 
+         * add to the stringtable pool of strings. */
+        public void addStringsToStringtable();
+
+        /**
+         * This callback is invoked to request that the primgroup serialize itself into the given protocol buffer object.
+         */
+        public Osmformat.PrimitiveGroup serialize();
+    }
+
+    /** Set the granularity (precision of lat/lon, measured in unites of nanodegrees. */
+    public void configGranularity(int granularity) {
+        this.granularity = granularity;
+    }
+
+    /** Set whether metadata is to be omitted */
+    public void configOmit(boolean omit_metadata) {
+        this.omit_metadata = omit_metadata;
+    }
+
+    /** Configure the maximum number of entities in a batch */
+    public void configBatchLimit(int batch_limit) {
+        this.batch_limit = batch_limit;
+    }
+
+    // Paramaters affecting the output size.
+    protected final int MIN_DENSE = 10;
+    protected int batch_limit = 4000;
+
+    // Parmaters affecting the output.
+
+    protected int granularity = 100;
+    protected int date_granularity = 1000;
+    protected boolean omit_metadata = false;
+
+    /** How many primitives have been seen in this batch */
+    protected int batch_size = 0;
+    protected int total_entities = 0;
+    private StringTable stringtable = new StringTable();
+    protected List<PrimGroupWriterInterface> groups = new ArrayList<PrimGroupWriterInterface>();
+    protected BlockOutputStream output;
+
+    public BinarySerializer(BlockOutputStream output) {
+        this.output = output;
+    }
+
+    public StringTable getStringTable() {
+        return stringtable;
+    }
+
+    public void flush() throws IOException {
+        processBatch();
+        output.flush();
+    }
+
+    public void close() throws IOException {
+        flush();
+        output.close();
+    }
+
+    long debug_bytes = 0;
+
+    public void processBatch() {
+        // System.out.format("Batch of %d groups: ",groups.size());
+        if (groups.size() == 0)
+            return;
+        Osmformat.PrimitiveBlock.Builder primblock = Osmformat.PrimitiveBlock
+                .newBuilder();
+        stringtable.clear();
+        // Preprocessing: Figure out the stringtable.
+        for (PrimGroupWriterInterface i : groups)
+            i.addStringsToStringtable();
+
+        stringtable.finish();
+        // Now, start serializing.
+        for (PrimGroupWriterInterface i : groups) {
+         PrimitiveGroup group = i.serialize();
+         if (group != null)
+           primblock.addPrimitivegroup(group);
+        }
+        primblock.setStringtable(stringtable.serialize());
+        primblock.setGranularity(this.granularity);
+        primblock.setDateGranularity(this.date_granularity);
+
+        // Only generate data with offset (0,0)
+        // 
+        Osmformat.PrimitiveBlock message = primblock.build();
+
+        // System.out.println(message);
+        debug_bytes += message.getSerializedSize();
+        // if (message.getSerializedSize() > 1000000)
+        // System.out.println(message);
+
+        try {
+            output.write(FileBlock.newInstance("OSMData", message
+                    .toByteString(), null));
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            throw new Error(e);
+        } finally {
+            batch_size = 0;
+            groups.clear();
+        }
+        // System.out.format("\n");
+    }
+
+    /** Convert from a degrees represented as a double into the serialized offset in nanodegrees.. */
+    public long mapRawDegrees(double degrees) {
+        return (long) ((degrees / .000000001));
+    }
+
+    /** Convert from a degrees represented as a double into the serialized offset. */
+    public int mapDegrees(double degrees) {
+        return (int) ((degrees / .0000001) / (granularity / 100));
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/StringTable.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/StringTable.java
new file mode 100644
index 0000000..dec0671
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/StringTable.java
@@ -0,0 +1,138 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+
+import com.google.protobuf.ByteString;
+
+/**
+ * Class for mapping a set of strings to integers, giving frequently occuring
+ * strings small integers.
+ */
+public class StringTable {
+    public StringTable() {
+        clear();
+    }
+
+    private HashMap<String, Integer> counts;
+    private HashMap<String, Integer> stringmap;
+    private String set[];
+
+    public void incr(String s) {
+        if (counts.containsKey(s)) {
+            counts.put(s, new Integer(counts.get(s).intValue() + 1));
+        } else {
+            counts.put(s, new Integer(1));
+        }
+    }
+
+    /** After the stringtable has been built, return the offset of a string in it.
+     *
+     * Note, value '0' is reserved for use as a delimiter and will not be returned.
+     * @param s
+     * @return
+     */
+    public int getIndex(String s) {
+        return stringmap.get(s).intValue();
+    }
+
+    public void finish() {
+        Comparator<String> comparator = new Comparator<String>() {
+            @Override
+            public int compare(final String s1, String s2) {
+                int diff = counts.get(s2) - counts.get(s1);
+                return diff;
+            }
+        };
+
+        /* Sort the stringtable */
+
+        /*
+        When a string is referenced, strings in the stringtable with indices:
+               0                : Is reserved (used as a delimiter in tags
+         A:  1 to 127          : Uses can be represented with 1 byte
+         B: 128 to 128**2-1 : Uses can be represented with 2 bytes,
+         C: 128*128  to X    : Uses can be represented with 3 bytes in the unlikely case we have >16k strings in a block. No block will contain enough strings that we'll need 4 bytes.
+
+        There are goals that will improve compression:
+          1. I want to use 1 bytes for the most frequently occurring strings, then 2 bytes, then 3 bytes.
+          2. I want to use low integers as frequently as possible (for better
+             entropy encoding out of deflate)
+          3. I want the stringtable to compress as small as possible.
+
+        Condition 1 is obvious. Condition 2 makes deflate compress stringtable references more effectively.
+        When compressing entities, delta coding causes small positive integers to occur more frequently
+        than larger integers. Even though a stringtable references to indices of 1 and 127 both use one
+        byte in a decompressed file, the small integer bias causes deflate to use fewer bits to represent
+        the smaller index when compressed. Condition 3 is most effective when adjacent strings in the
+        stringtable have a lot of common substrings.
+
+        So, when I decide on the master stringtable to use, I put the 127 most frequently occurring
+        strings into A (accomplishing goal 1), and sort them by frequency (to accomplish goal 2), but
+        for B and C, which contain the less progressively less frequently encountered strings, I sort
+        them lexiconographically, to maximize goal 3 and ignoring goal 2.
+
+        Goal 1 is the most important. Goal 2 helped enough to be worth it, and goal 3 was pretty minor,
+        but all should be re-benchmarked.
+
+
+        */
+
+
+
+        set = counts.keySet().toArray(new String[0]);
+        if (set.length > 0) {
+          // Sort based on the frequency.
+          Arrays.sort(set, comparator);
+          // Each group of keys that serializes to the same number of bytes is
+          // sorted lexiconographically.
+          // to maximize deflate compression.
+
+          // Don't sort the first array. There's not likely to be much benefit, and we want frequent values to be small.
+          //Arrays.sort(set, Math.min(0, set.length-1), Math.min(1 << 7, set.length-1));
+
+          Arrays.sort(set, Math.min(1 << 7, set.length-1), Math.min(1 << 14,
+              set.length-1));
+          Arrays.sort(set, Math.min(1 << 14, set.length-1), Math.min(1 << 21,
+              set.length-1), comparator);
+        }
+        stringmap = new HashMap<String, Integer>(2 * set.length);
+        for (int i = 0; i < set.length; i++) {
+            stringmap.put(set[i], new Integer(i+1)); // Index 0 is reserved for use as a delimiter.
+        }
+        counts = null;
+    }
+
+    public void clear() {
+        counts = new HashMap<String, Integer>(100);
+        stringmap = null;
+        set = null;
+    }
+
+    public Osmformat.StringTable.Builder serialize() {
+        Osmformat.StringTable.Builder builder = Osmformat.StringTable
+                .newBuilder();
+        builder.addS(ByteString.copyFromUtf8("")); // Add a unused string at offset 0 which is used as a delimiter.
+        for (int i = 0; i < set.length; i++)
+            builder.addS(ByteString.copyFromUtf8(set[i]));
+        return builder;
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockInputStream.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockInputStream.java
new file mode 100644
index 0000000..0e58b27
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockInputStream.java
@@ -0,0 +1,47 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BlockInputStream {
+    // TODO: Should be seekable input stream!
+    public BlockInputStream(InputStream input, BlockReaderAdapter adaptor) {
+        this.input = input;
+        this.adaptor = adaptor;
+    }
+
+    public void process() throws IOException {
+      try {
+        while (true) {
+          FileBlock.process(input, adaptor);
+        }
+      } catch (EOFException e) {
+        adaptor.complete();
+      }
+    }
+
+    public void close() throws IOException {
+        input.close();
+    }
+
+    InputStream input;
+    BlockReaderAdapter adaptor;
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockOutputStream.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockOutputStream.java
new file mode 100644
index 0000000..c364ddb
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockOutputStream.java
@@ -0,0 +1,74 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+enum CompressFlags {
+    NONE, DEFLATE
+}
+
+public class BlockOutputStream {
+
+    public BlockOutputStream(OutputStream output) {
+        this.outwrite = new DataOutputStream(output);
+        this.compression = CompressFlags.DEFLATE;
+    }
+
+    public void setCompress(CompressFlags flag) {
+        compression = flag;
+    }
+
+    public void setCompress(String s) {
+        if (s.equals("none"))
+            compression = CompressFlags.NONE;
+        else if (s.equals("deflate"))
+            compression = CompressFlags.DEFLATE;
+        else
+            throw new Error("Unknown compression type: " + s);
+    }
+
+    /** Write a block with the stream's default compression flag */
+    public void write(FileBlock block) throws IOException {
+        this.write(block, compression);
+    }
+
+    /** Write a specific block with a specific compression flags */
+    public void write(FileBlock block, CompressFlags compression)
+            throws IOException {
+        FileBlockPosition ref = block.writeTo(outwrite, compression);
+        writtenblocks.add(ref);
+    }
+
+    public void flush() throws IOException {
+        outwrite.flush();
+    }
+
+    public void close() throws IOException {
+        outwrite.flush();
+        outwrite.close();
+    }
+
+    OutputStream outwrite;
+    List<FileBlockPosition> writtenblocks = new ArrayList<FileBlockPosition>();
+    CompressFlags compression;
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockReaderAdapter.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockReaderAdapter.java
new file mode 100644
index 0000000..fdd9f56
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/BlockReaderAdapter.java
@@ -0,0 +1,40 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+/** An adaptor that receives blocks from an input stream */
+public interface BlockReaderAdapter {
+    /**
+     * Does the reader understand this block? Does it want the data in it?
+     * 
+     * A reference contains the metadata about a block and can saved --- or
+     * stored ---- for future random access. However, during a strea read of the
+     * file, does the user want this block?
+     * 
+     * handleBlock will be called on all blocks that are not skipped, in file
+     * order.
+     * 
+     * */
+    boolean skipBlock(FileBlockPosition message);
+
+    /** Called with the data in the block. */
+    void handleBlock(FileBlock message);
+
+    /** Called when the file is fully read. */
+    void complete();
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlock.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlock.java
new file mode 100644
index 0000000..b732913
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlock.java
@@ -0,0 +1,142 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.DataOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.zip.Deflater;
+
+import com.google.protobuf.ByteString;
+
+import org.openstreetmap.osmosis.osmbinary.Fileformat;
+import org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader;
+
+/** A full fileblock object contains both the metadata and data of a fileblock */
+public class FileBlock extends FileBlockBase {
+    /** Contains the contents of a block for use or further processing */
+    ByteString data; // serialized Format.Blob
+
+    /** Don't be noisy unless the warning occurs somewhat often */
+    static int warncount = 0;
+
+    private FileBlock(String type, ByteString blob, ByteString indexdata) {
+        super(type, indexdata);
+        this.data = blob;
+    }
+
+    public static FileBlock newInstance(String type, ByteString blob,
+            ByteString indexdata) {
+      if (blob != null && blob.size() > MAX_BODY_SIZE/2) {
+        System.err.println("Warning: Fileblock has body size too large and may be considered corrupt");
+        if (blob != null && blob.size() > MAX_BODY_SIZE-1024*1024) {
+          throw new Error("This file has too many entities in a block. Parsers will reject it.");
+        }
+      }
+      if (indexdata != null && indexdata.size() > MAX_HEADER_SIZE/2) {
+        System.err.println("Warning: Fileblock has indexdata too large and may be considered corrupt");
+        if (indexdata != null && indexdata.size() > MAX_HEADER_SIZE-512) {
+          throw new Error("This file header is too large. Parsers will reject it.");
+        }
+      }
+      return new FileBlock(type, blob, indexdata);
+    }
+
+     protected void deflateInto(org.openstreetmap.osmosis.osmbinary.Fileformat.Blob.Builder blobbuilder) {
+        int size = data.size();
+        Deflater deflater = new Deflater();
+        deflater.setInput(data.toByteArray());
+        deflater.finish();
+        byte out[] = new byte[size];
+        deflater.deflate(out);
+        
+        if (!deflater.finished()) {
+            // Buffer wasn't long enough. Be noisy.
+          ++warncount;
+          if (warncount > 10 && warncount%100 == 0)
+               System.out.println("Compressed buffers are too short, causing extra copy");
+            out = Arrays.copyOf(out, size + size / 64 + 16);
+            deflater.deflate(out, deflater.getTotalOut(), out.length
+                    - deflater.getTotalOut());
+            if (!deflater.finished()) {
+              throw new Error("Internal error in compressor");
+            }
+        }
+        ByteString compressed = ByteString.copyFrom(out, 0, deflater
+                .getTotalOut());
+        blobbuilder.setZlibData(compressed);
+        deflater.end();
+    }
+
+    public FileBlockPosition writeTo(OutputStream outwrite, CompressFlags flags)
+            throws IOException {
+        BlobHeader.Builder builder = Fileformat.BlobHeader
+                .newBuilder();
+        if (indexdata != null)
+            builder.setIndexdata(indexdata);
+        builder.setType(type);
+
+        Fileformat.Blob.Builder blobbuilder = Fileformat.Blob.newBuilder();
+        if (flags == CompressFlags.NONE) {
+            blobbuilder.setRaw(data);
+            blobbuilder.setRawSize(data.size());
+        } else {
+            blobbuilder.setRawSize(data.size());
+            if (flags == CompressFlags.DEFLATE)
+                deflateInto(blobbuilder);
+            else
+                throw new Error("Compression flag not understood");
+        }
+        Fileformat.Blob blob = blobbuilder.build();
+
+        builder.setDatasize(blob.getSerializedSize());
+        Fileformat.BlobHeader message = builder.build();
+        int size = message.getSerializedSize();
+
+        // System.out.format("Outputed header size %d bytes, header of %d bytes, and blob of %d bytes\n",
+        // size,message.getSerializedSize(),blob.getSerializedSize());
+        (new DataOutputStream(outwrite)).writeInt(size);
+        message.writeTo(outwrite);
+        long offset = -1;
+
+        if (outwrite instanceof FileOutputStream)
+            offset = ((FileOutputStream) outwrite).getChannel().position();
+
+        blob.writeTo(outwrite);
+        return FileBlockPosition.newInstance(this, offset, size);
+    }
+
+    /** Reads or skips a fileblock. */
+    static void process(InputStream input, BlockReaderAdapter callback)
+            throws IOException {
+        FileBlockHead fileblock = FileBlockHead.readHead(input);
+        if (callback.skipBlock(fileblock)) {
+            // System.out.format("Attempt to skip %d bytes\n",header.getDatasize());
+            fileblock.skipContents(input);
+        } else {
+            callback.handleBlock(fileblock.readContents(input));
+        }
+    }
+
+    public ByteString getData() {
+        return data;
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockBase.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockBase.java
new file mode 100644
index 0000000..a9f8a03
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockBase.java
@@ -0,0 +1,58 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import com.google.protobuf.ByteString;
+
+/**
+ * Base class that contains the metadata about a fileblock.
+ * 
+ * Subclasses of this include additional fields, such as byte offsets that let a
+ * fileblock be read in a random-access fashion, or the data itself.
+ * 
+ * @author crosby
+ * 
+ */
+public class FileBlockBase {
+
+    /** If a block header is bigger than this, fail. We use excessively large header size as an indication of corrupt files */
+    static final int MAX_HEADER_SIZE = 64*1024;
+    /** If a block's size is bigger than this, fail. We use excessively large block sizes as an indication of corrupt files */
+    static final int MAX_BODY_SIZE = 32*1024*1024;
+
+    protected FileBlockBase(String type, ByteString indexdata) {
+        this.type = type;
+        this.indexdata = indexdata;
+    }
+
+    /** Identifies the type of the data within a block */
+    protected final String type;
+    /**
+     * Block metadata, stored in the index block and as a prefix for every
+     * block.
+     */
+    protected final ByteString indexdata;
+
+    public String getType() {
+        return type;
+    }
+
+    public ByteString getIndexData() {
+        return indexdata;
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockHead.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockHead.java
new file mode 100644
index 0000000..c757bb3
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockHead.java
@@ -0,0 +1,97 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import com.google.protobuf.ByteString;
+
+import org.openstreetmap.osmosis.osmbinary.Fileformat;
+
+/**
+ * Intermediate representation of the header of a fileblock when a set of
+ * fileblocks is read as in a stream. The data in the fileblock must be either
+ * skipped (where the returned value is a reference to the fileblock) or parsed.
+ * 
+ * @author crosby
+ * 
+ */
+public class FileBlockHead extends FileBlockReference {
+    protected FileBlockHead(String type, ByteString indexdata) {
+        super(type, indexdata);
+    }
+
+    /**
+     * Read the header. After reading the header, either the contents must be
+     * skipped or read
+     */
+    static FileBlockHead readHead(InputStream input) throws IOException {
+        DataInputStream datinput = new DataInputStream(input);
+        int headersize = datinput.readInt();
+        // System.out.format("Header size %d %x\n",headersize,headersize);
+        if (headersize > MAX_HEADER_SIZE) {
+          throw new FileFormatException("Unexpectedly long header "+MAX_HEADER_SIZE+ " bytes. Possibly corrupt file.");
+        }
+        
+        byte buf[] = new byte[headersize];
+        datinput.readFully(buf);
+        // System.out.format("Read buffer for header of %d bytes\n",buf.length);
+        Fileformat.BlobHeader header = Fileformat.BlobHeader
+                .parseFrom(buf);
+        FileBlockHead fileblock = new FileBlockHead(header.getType(), header
+                .getIndexdata());
+
+        fileblock.datasize = header.getDatasize();
+        if (header.getDatasize() > MAX_BODY_SIZE) {
+          throw new FileFormatException("Unexpectedly long body "+MAX_BODY_SIZE+ " bytes. Possibly corrupt file.");
+        }
+        
+        fileblock.input = input;
+        if (input instanceof FileInputStream)
+            fileblock.data_offset = ((FileInputStream) input).getChannel()
+                    .position();
+
+        return fileblock;
+    }
+
+    /**
+     * Assumes the stream is positioned over at the start of the data, skip over
+     * it.
+     * 
+     * @throws IOException
+     */
+    void skipContents(InputStream input) throws IOException {
+        if (input.skip(getDatasize()) != getDatasize())
+            assert false : "SHORT READ";
+    }
+
+    /**
+     * Assumes the stream is positioned over at the start of the data, read it
+     * and return the complete FileBlock
+     * 
+     * @throws IOException
+     */
+    FileBlock readContents(InputStream input) throws IOException {
+        DataInputStream datinput = new DataInputStream(input);
+        byte buf[] = new byte[getDatasize()];
+        datinput.readFully(buf);
+        return parseData(buf);
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockPosition.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockPosition.java
new file mode 100644
index 0000000..a720180
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockPosition.java
@@ -0,0 +1,112 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.openstreetmap.osmosis.osmbinary.Fileformat;
+
+/**
+ * Stores the position in the stream of a fileblock so that it can be easily
+ * read in a random-access fashion.
+ * 
+ * We can turn this into a 'real' block by appropriately seeking into the file
+ * and doing a 'read'.
+ * 
+ * */
+public class FileBlockPosition extends FileBlockBase {
+    protected FileBlockPosition(String type, ByteString indexdata) {
+        super(type, indexdata);
+    }
+
+    /** Parse out and decompress the data part of a fileblock helper function. */
+    FileBlock parseData(byte buf[]) throws InvalidProtocolBufferException {
+        FileBlock out = FileBlock.newInstance(type, null, indexdata);
+        Fileformat.Blob blob = Fileformat.Blob.parseFrom(buf);
+        if (blob.hasRaw()) {
+            out.data = blob.getRaw();
+        } else if (blob.hasZlibData()) {
+            byte buf2[] = new byte[blob.getRawSize()];
+            Inflater decompresser = new Inflater();
+            decompresser.setInput(blob.getZlibData().toByteArray());
+            // decompresser.getRemaining();
+            try {
+                decompresser.inflate(buf2);
+            } catch (DataFormatException e) {
+                e.printStackTrace();
+                throw new Error(e);
+            }
+            assert (decompresser.finished());
+            decompresser.end();
+            out.data = ByteString.copyFrom(buf2);
+        }
+        return out;
+    }
+
+    public int getDatasize() {
+        return datasize;
+    }
+
+    /*
+     * Given any form of fileblock and an offset/length value, return a
+     * reference that can be used to dereference and read the contents.
+     */
+    static FileBlockPosition newInstance(FileBlockBase base, long offset,
+            int length) {
+        FileBlockPosition out = new FileBlockPosition(base.type, base.indexdata);
+        out.datasize = length;
+        out.data_offset = offset;
+        return out;
+    }
+
+    public FileBlock read(InputStream input) throws IOException {
+        if (input instanceof FileInputStream) {
+            ((FileInputStream) input).getChannel().position(data_offset);
+            byte buf[] = new byte[getDatasize()];
+            (new DataInputStream(input)).readFully(buf);
+            return parseData(buf);
+        } else {
+            throw new Error("Random access binary reads require seekability");
+        }
+    }
+
+    /**
+     * TODO: Convert this reference into a serialized representation that can be
+     * stored.
+     */
+    public ByteString serialize() {
+        throw new Error("TODO");
+    }
+
+    /** TODO: Parse a serialized representation of this block reference */
+    static FileBlockPosition parseFrom(ByteString b) {
+      throw new Error("TODO");
+    }
+
+    protected int datasize;
+    /** Offset into the file of the data part of the block */
+    long data_offset;
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockReference.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockReference.java
new file mode 100644
index 0000000..d099c15
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileBlockReference.java
@@ -0,0 +1,54 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.google.protobuf.ByteString;
+
+/**
+ * A FileBlockPosition that remembers what file this is so that it can simply be
+ * dereferenced
+ */
+public class FileBlockReference extends FileBlockPosition {
+
+    /**
+     * Convenience cache for storing the input this reference is contained
+     * within so that it can be cached
+     */
+    protected InputStream input;
+
+    protected FileBlockReference(String type, ByteString indexdata) {
+        super(type, indexdata);
+    }
+
+    public FileBlock read() throws IOException {
+        return read(input);
+    }
+
+    static FileBlockPosition newInstance(FileBlockBase base, InputStream input,
+            long offset, int length) {
+        FileBlockReference out = new FileBlockReference(base.type,
+                base.indexdata);
+        out.datasize = length;
+        out.data_offset = offset;
+        out.input = input;
+        return out;
+    }
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileFormatException.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileFormatException.java
new file mode 100644
index 0000000..fdc062b
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/file/FileFormatException.java
@@ -0,0 +1,33 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.file;
+
+import java.io.IOException;
+
+public class FileFormatException extends IOException {
+
+  public FileFormatException(String string) {
+    super(string);
+  }
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8128010128748910923L;
+
+}
diff --git a/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/test/BuildTestFile.java b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/test/BuildTestFile.java
new file mode 100644
index 0000000..3f0764b
--- /dev/null
+++ b/osmosis-osm-binary/src/main/java/org/openstreetmap/osmosis/osmbinary/test/BuildTestFile.java
@@ -0,0 +1,243 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+package org.openstreetmap.osmosis.osmbinary.test;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+
+import com.google.protobuf.ByteString;
+
+import org.openstreetmap.osmosis.osmbinary.Fileformat.Blob;
+import org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBlock;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Info;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Node;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock.Builder;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Relation;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Way;
+import org.openstreetmap.osmosis.osmbinary.file.BlockOutputStream;
+import org.openstreetmap.osmosis.osmbinary.file.FileBlock;
+
+public class BuildTestFile {
+  BlockOutputStream output;
+  public static final long BILLION = 1000000000L;
+  
+  StringTable makeStringTable(String prefix) {
+    return 
+    StringTable.newBuilder()
+    .addS(ByteString.copyFromUtf8("")) // Never used.
+    .addS(ByteString.copyFromUtf8(prefix+"Offset1"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset2"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset3"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset4"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset5"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset6"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset7"))
+    .addS(ByteString.copyFromUtf8(prefix+"Offset8"))
+    .build();
+  }
+  void makeSimpleFileBlock1() throws IOException {
+    PrimitiveBlock.Builder b1 = PrimitiveBlock.newBuilder();
+    b1.setStringtable(makeStringTable("B1"));
+
+    b1.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .addNodes(Node.newBuilder()
+            .setId(101).setLat(13*10*1000*1000).setLon(-14*10*1000*1000)
+            .addKeys(1).addVals(2))
+        .addNodes(Node.newBuilder()
+            .setId(101).setLat(12345678).setLon(-23456789)) // Should be 1.2345678 degrees lat and -2.3456789 lon.
+    );
+    b1.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .addWays(Way.newBuilder()
+            .setId(201)
+            .addRefs(101).addRefs(1).addRefs(-1).addRefs(10).addRefs(-20) // Delta coded. Should be 101, 102, 101, 111, 91.
+            .addKeys(2).addVals(1).addKeys(3).addVals(4))
+        .addWays(Way.newBuilder()
+            .setId(-301)
+            .addRefs(211).addRefs(1).addRefs(-1).addRefs(10).addRefs(-300) // Delta coded. Should be 211, 212, 211, 221, -79
+            .addKeys(4).addVals(3).addKeys(5).addVals(6))
+        .addWays(Way.newBuilder()
+            .setId(401).addRefs(211).addRefs(1))
+        .addWays(Way.newBuilder()
+            .setId(501))            
+    );
+    
+    b1.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .addRelations(Relation.newBuilder()
+            .setId(601)
+            .addTypes(MemberType.NODE).addMemids(50).addRolesSid(2)
+            .addTypes(MemberType.NODE).addMemids(3).addRolesSid(3)
+            .addTypes(MemberType.WAY).addMemids(3).addRolesSid(4)
+            .addTypes(MemberType.RELATION).addMemids(3).addRolesSid(5))
+        .addRelations(Relation.newBuilder()
+            .setId(701)
+            .addTypes(MemberType.RELATION).addMemids(60).addRolesSid(6)
+            .addTypes(MemberType.RELATION).addMemids(5).addRolesSid(7)
+            .addKeys(1).addVals(2)));
+
+    b1.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .setDense(DenseNodes.newBuilder()
+            .addId(1001).addId(110).addId(-2000).addId(8889)
+            .addLat(12*10000000).addLat(1500000).addLat(-12*10000000).addLat(-12*10000000)
+            .addLon(-12*10000000).addLon(2500000).addLon(13*10000000).addLon(2*10000000)
+            .addKeysVals(1).addKeysVals(2).addKeysVals(0)
+            .addKeysVals(0)
+            .addKeysVals(2).addKeysVals(3).addKeysVals(4).addKeysVals(5).addKeysVals(0)
+            .addKeysVals(3).addKeysVals(3).addKeysVals(0)
+            ));
+
+    output.write(FileBlock.newInstance("OSMData", b1.build().toByteString(),null));
+
+    PrimitiveBlock.Builder b2 = PrimitiveBlock.newBuilder();
+    b2.setLatOffset(10*BILLION + 109208300)
+     .setLonOffset(20*BILLION + 901802700)
+     .setGranularity(1200);
+    b2.setStringtable(makeStringTable("B2"));
+    
+    // Test out granularity stuff.
+    b2.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .addNodes(Node.newBuilder().setId(100000).setLat(0).setLon(0))
+        .addNodes(Node.newBuilder().setId(100001).setLat(1000).setLon(2000))
+        .addNodes(Node.newBuilder().setId(100002).setLat(1001).setLon(2001))
+        .addNodes(Node.newBuilder().setId(100003).setLat(1002).setLon(2002))
+        .addNodes(Node.newBuilder().setId(100004).setLat(1003).setLon(2003))
+        .addNodes(Node.newBuilder().setId(100005).setLat(1004).setLon(2004)));
+    
+    
+    output.write(FileBlock.newInstance("OSMData", b2.build().toByteString(),null));
+  }
+
+  
+  BuildTestFile(String name, String compress) throws IOException {
+    output = new BlockOutputStream(new FileOutputStream(name));
+    output.setCompress(compress);
+    HeaderBlock.Builder b = HeaderBlock.newBuilder();
+    b.addRequiredFeatures("OsmSchema-V0.6").addRequiredFeatures("DenseNodes").setSource("QuickBrownFox");
+    output.write(FileBlock.newInstance("OSMHeader",b.build().toByteString(),null));
+  }
+  
+  
+  public static void main(String [] args) {
+    try {
+      BuildTestFile out1a = new BuildTestFile("TestFile1-deflate.osm.pbf","deflate");
+      out1a.makeSimpleFileBlock1();
+      out1a.output.close();
+
+      BuildTestFile out1b = new BuildTestFile("TestFile1-none.osm.pbf","none");
+      out1b.makeSimpleFileBlock1();
+      out1b.output.close();
+
+      BuildTestFile out2 = new BuildTestFile("TestFile2-uncom.osm.pbf","deflate");
+      out2.makeGranFileBlock1();
+      out2.output.close();
+
+    
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
+  }
+  
+  
+  void makeGranFileBlock1() throws IOException {
+    PrimitiveBlock.Builder b1 = PrimitiveBlock.newBuilder();
+    b1.setLatOffset(10*BILLION + 109208300)
+     .setLonOffset(20*BILLION + 901802700)
+     .setGranularity(1200)
+     .setDateGranularity(2500);
+    b1.setStringtable(makeStringTable("C1"));
+
+    b1.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+            .addNodes(Node.newBuilder()
+            .setId(100001)
+            .setLat(1000).setLon(2000)
+            .setInfo(Info.newBuilder()
+                .setTimestamp(1001)
+                .setChangeset(-12)
+                .setUid(21)
+                .setUserSid(6)
+                .build())
+             .build())
+        .addNodes(Node.newBuilder()
+            .setId(100002)
+            .setLat(1001).setLon(2001)
+            .setInfo(Info.newBuilder()
+                .setVersion(102)
+                .setTimestamp(1002)
+                .setChangeset(12)
+                .setUid(-21)
+                .setUserSid(5)
+                .build())
+            .build())
+        .addNodes(Node.newBuilder()
+            .setId(100003)
+            .setLat(1003).setLon(2003)
+            .setInfo(Info.newBuilder()
+                .setVersion(103)
+                .setUserSid(4)
+                .build())
+           .build())
+    )
+
+    ;
+    
+    // The same, but with different granularities.
+    PrimitiveBlock.Builder b2 = PrimitiveBlock.newBuilder();
+    b2.setLatOffset(12*BILLION + 303)
+     .setLonOffset(22*BILLION + 404)
+     .setGranularity(1401)
+     .setDateGranularity(3003);
+    b2.setStringtable(makeStringTable("C2"));
+    b2.addPrimitivegroup(
+        PrimitiveGroup.newBuilder()
+        .addNodes(Node.newBuilder()
+            .setId(100001)
+            .addKeys(1).addVals(2)
+            .addKeys(1).addVals(3) // Support multiple vals for a key.
+            .addKeys(3).addVals(4)
+            .setLat(1000).setLon(2000)
+            .build())
+        .addNodes(Node.newBuilder()
+            .setId(100002)
+            .setLat(1001).setLon(2001)
+            .build())
+        .addNodes(Node.newBuilder()
+            .setId(100003)
+            .setLat(1003).setLon(2003)
+            .addKeys(5).addVals(6)
+            .build())
+    );
+    output.write(FileBlock.newInstance("OSMData", b1.build().toByteString(),null));
+    output.write(FileBlock.newInstance("OSMData", b2.build().toByteString(),null));
+  }
+  
+}
diff --git a/osmosis-osm-binary/src/main/protobuf/fileformat.proto b/osmosis-osm-binary/src/main/protobuf/fileformat.proto
new file mode 100644
index 0000000..dfc1c3b
--- /dev/null
+++ b/osmosis-osm-binary/src/main/protobuf/fileformat.proto
@@ -0,0 +1,54 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.openstreetmap.osmosis.osmbinary";
+package OSMPBF;
+
+//protoc --java_out=../.. fileformat.proto
+
+
+//
+//  STORAGE LAYER: Storing primitives.
+//
+
+message Blob {
+  optional bytes raw = 1; // No compression
+  optional int32 raw_size = 2; // When compressed, the uncompressed size
+
+  // Possible compressed versions of the data.
+  optional bytes zlib_data = 3;
+
+  // PROPOSED feature for LZMA compressed data. SUPPORT IS NOT REQUIRED.
+  optional bytes lzma_data = 4;
+
+  // Formerly used for bzip2 compressed data. Depreciated in 2010.
+  optional bytes OBSOLETE_bzip2_data = 5 [deprecated=true]; // Don't reuse this tag number.
+}
+
+/* A file contains an sequence of fileblock headers, each prefixed by
+their length in network byte order, followed by a data block
+containing the actual data. types staring with a "_" are reserved.
+*/
+
+message BlobHeader {
+  required string type = 1;
+  optional bytes indexdata = 2;
+  required int32 datasize = 3;
+}
+
+
diff --git a/osmosis-osm-binary/src/main/protobuf/osmformat.proto b/osmosis-osm-binary/src/main/protobuf/osmformat.proto
new file mode 100644
index 0000000..659b8c8
--- /dev/null
+++ b/osmosis-osm-binary/src/main/protobuf/osmformat.proto
@@ -0,0 +1,260 @@
+/** Copyright (c) 2010 Scott A. Crosby. <scott at sacrosby.com>
+
+   This program 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 3 of the 
+   License, or (at your option) any later version.
+
+   This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.openstreetmap.osmosis.osmbinary";
+package OSMPBF;
+
+/* OSM Binary file format 
+
+This is the master schema file of the OSM binary file format. This
+file is designed to support limited random-access and future
+extendability.
+
+A binary OSM file consists of a sequence of FileBlocks (please see
+fileformat.proto). The first fileblock contains a serialized instance
+of HeaderBlock, followed by a sequence of PrimitiveBlock blocks that
+contain the primitives.
+
+Each primitiveblock is designed to be independently parsable. It
+contains a string table storing all strings in that block (keys and
+values in tags, roles in relations, usernames, etc.) as well as
+metadata containing the precision of coordinates or timestamps in that
+block.
+
+A primitiveblock contains a sequence of primitive groups, each
+containing primitives of the same type (nodes, densenodes, ways,
+relations). Coordinates are stored in signed 64-bit integers. Lat&lon
+are measured in units <granularity> nanodegrees. The default of
+granularity of 100 nanodegrees corresponds to about 1cm on the ground,
+and a full lat or lon fits into 32 bits.
+
+Converting an integer to a lattitude or longitude uses the formula:
+$OUT = IN * granularity / 10**9$. Many encoding schemes use delta
+coding when representing nodes and relations.
+
+*/
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+/* Contains the file header. */
+
+message HeaderBlock {
+  optional HeaderBBox bbox = 1;
+  /* Additional tags to aid in parsing this dataset */
+  repeated string required_features = 4;
+  repeated string optional_features = 5;
+
+  optional string writingprogram = 16; 
+  optional string source = 17; // From the bbox field.
+
+  /* Tags that allow continuing an Osmosis replication */
+
+  // replication timestamp, expressed in seconds since the epoch, 
+  // otherwise the same value as in the "timestamp=..." field
+  // in the state.txt file used by Osmosis
+  optional int64 osmosis_replication_timestamp = 32;
+
+  // replication sequence number (sequenceNumber in state.txt)
+  optional int64 osmosis_replication_sequence_number = 33;
+
+  // replication base URL (from Osmosis' configuration.txt file)
+  optional string osmosis_replication_base_url = 34;
+}
+
+
+/** The bounding box field in the OSM header. BBOX, as used in the OSM
+header. Units are always in nanodegrees -- they do not obey
+granularity rules. */
+
+message HeaderBBox {
+   required sint64 left = 1;
+   required sint64 right = 2;
+   required sint64 top = 3;
+   required sint64 bottom = 4;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////
+
+
+message PrimitiveBlock {
+  required StringTable stringtable = 1;
+  repeated PrimitiveGroup primitivegroup = 2;
+
+  // Granularity, units of nanodegrees, used to store coordinates in this block
+  optional int32 granularity = 17 [default=100]; 
+  // Offset value between the output coordinates coordinates and the granularity grid in unites of nanodegrees.
+  optional int64 lat_offset = 19 [default=0];
+  optional int64 lon_offset = 20 [default=0]; 
+
+// Granularity of dates, normally represented in units of milliseconds since the 1970 epoch.
+  optional int32 date_granularity = 18 [default=1000]; 
+
+
+  // Proposed extension:
+  //optional BBox bbox = XX;
+}
+
+// Group of OSMPrimitives. All primitives in a group must be the same type.
+message PrimitiveGroup {
+  repeated Node     nodes = 1;
+  optional DenseNodes dense = 2;
+  repeated Way      ways = 3;
+  repeated Relation relations = 4;
+  repeated ChangeSet changesets = 5;
+}
+
+
+/** String table, contains the common strings in each block.
+
+ Note that we reserve index '0' as a delimiter, so the entry at that
+ index in the table is ALWAYS blank and unused.
+
+ */
+message StringTable {
+   repeated bytes s = 1;
+}
+
+/* Optional metadata that may be included into each primitive. */
+message Info {
+   optional int32 version = 1 [default = -1];
+   optional int64 timestamp = 2;
+   optional int64 changeset = 3;
+   optional int32 uid = 4;
+   optional uint32 user_sid = 5; // String IDs
+
+   // The visible flag is used to store history information. It indicates that
+   // the current object version has been created by a delete operation on the
+   // OSM API.
+   // When a writer sets this flag, it MUST add a required_features tag with
+   // value "HistoricalInformation" to the HeaderBlock.
+   // If this flag is not available for some object it MUST be assumed to be
+   // true if the file has the required_features tag "HistoricalInformation"
+   // set.
+   optional bool visible = 6;
+}
+
+/** Optional metadata that may be included into each primitive. Special dense format used in DenseNodes. */
+message DenseInfo {
+   repeated int32 version = 1 [packed = true]; 
+   repeated sint64 timestamp = 2 [packed = true]; // DELTA coded
+   repeated sint64 changeset = 3 [packed = true]; // DELTA coded
+   repeated sint32 uid = 4 [packed = true]; // DELTA coded
+   repeated sint32 user_sid = 5 [packed = true]; // String IDs for usernames. DELTA coded
+
+   // The visible flag is used to store history information. It indicates that
+   // the current object version has been created by a delete operation on the
+   // OSM API.
+   // When a writer sets this flag, it MUST add a required_features tag with
+   // value "HistoricalInformation" to the HeaderBlock.
+   // If this flag is not available for some object it MUST be assumed to be
+   // true if the file has the required_features tag "HistoricalInformation"
+   // set.
+   repeated bool visible = 6 [packed = true];
+}
+
+
+// THIS IS STUB DESIGN FOR CHANGESETS. NOT USED RIGHT NOW.
+// TODO:    REMOVE THIS?
+message ChangeSet {
+   required int64 id = 1;
+//   
+//   // Parallel arrays.
+//   repeated uint32 keys = 2 [packed = true]; // String IDs.
+//   repeated uint32 vals = 3 [packed = true]; // String IDs.
+//
+//   optional Info info = 4;
+
+//   optional int64 created_at = 8;
+//   optional int64 closetime_delta = 9;
+//   optional bool open = 10;
+//   optional HeaderBBox bbox = 11;
+}
+
+
+message Node {
+   required sint64 id = 1;
+   // Parallel arrays.
+   repeated uint32 keys = 2 [packed = true]; // String IDs.
+   repeated uint32 vals = 3 [packed = true]; // String IDs.
+
+   optional Info info = 4; // May be omitted in omitmeta
+
+   required sint64 lat = 8;
+   required sint64 lon = 9;
+}
+
+/* Used to densly represent a sequence of nodes that do not have any tags.
+
+We represent these nodes columnwise as five columns: ID's, lats, and
+lons, all delta coded. When metadata is not omitted, 
+
+We encode keys & vals for all nodes as a single array of integers
+containing key-stringid and val-stringid, using a stringid of 0 as a
+delimiter between nodes.
+
+   ( (<keyid> <valid>)* '0' )*
+ */
+
+message DenseNodes {
+   repeated sint64 id = 1 [packed = true]; // DELTA coded
+
+   //repeated Info info = 4;
+   optional DenseInfo denseinfo = 5;
+
+   repeated sint64 lat = 8 [packed = true]; // DELTA coded
+   repeated sint64 lon = 9 [packed = true]; // DELTA coded
+
+   // Special packing of keys and vals into one array. May be empty if all nodes in this block are tagless.
+   repeated int32 keys_vals = 10 [packed = true]; 
+}
+
+
+message Way {
+   required int64 id = 1;
+   // Parallel arrays.
+   repeated uint32 keys = 2 [packed = true];
+   repeated uint32 vals = 3 [packed = true];
+
+   optional Info info = 4;
+
+   repeated sint64 refs = 8 [packed = true];  // DELTA coded
+}
+
+message Relation {
+  enum MemberType {
+    NODE = 0;
+    WAY = 1;
+    RELATION = 2;
+  } 
+   required int64 id = 1;
+
+   // Parallel arrays.
+   repeated uint32 keys = 2 [packed = true];
+   repeated uint32 vals = 3 [packed = true];
+
+   optional Info info = 4;
+
+   // Parallel arrays
+   repeated int32 roles_sid = 8 [packed = true];
+   repeated sint64 memids = 9 [packed = true]; // DELTA encoded
+   repeated MemberType types = 10 [packed = true];
+}
+
diff --git a/osmosis-pbf/.checkstyle b/osmosis-pbf/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-pbf/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-pbf/.gitignore b/osmosis-pbf/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-pbf/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-pbf/build.gradle b/osmosis-pbf/build.gradle
new file mode 100644
index 0000000..67b28d6
--- /dev/null
+++ b/osmosis-pbf/build.gradle
@@ -0,0 +1,7 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-osm-binary')
+    compile group: 'com.google.protobuf', name: 'protobuf-java', version: dependencyVersionProtobuf
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
diff --git a/pbf/src/main/java/crosby/binary/osmosis/BinaryPluginLoader.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/BinaryPluginLoader.java
similarity index 100%
rename from pbf/src/main/java/crosby/binary/osmosis/BinaryPluginLoader.java
rename to osmosis-pbf/src/main/java/crosby/binary/osmosis/BinaryPluginLoader.java
diff --git a/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java
new file mode 100644
index 0000000..0745064
--- /dev/null
+++ b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java
@@ -0,0 +1,260 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package crosby.binary.osmosis;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+import org.openstreetmap.osmosis.osmbinary.BinaryParser;
+import org.openstreetmap.osmosis.osmbinary.Osmformat;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo;
+
+/** Class that reads and parses binary files and sends the contained entities to the sink. */
+public class OsmosisBinaryParser extends BinaryParser {
+
+    @Override
+    public void complete() {
+        sink.complete();
+    }
+
+    /** Get the osmosis object representing a the user in a given Info protobuf.
+     * @param info The info protobuf.
+     * @return The OsmUser object */
+    OsmUser getUser(Osmformat.Info info) {
+        // System.out.println(info);
+        if (info.hasUid() && info.hasUserSid()) {
+            if (info.getUid() < 0) {
+              return OsmUser.NONE;
+            }
+            return new OsmUser(info.getUid(), getStringById(info.getUserSid()));
+        } else {
+            return OsmUser.NONE;
+        }
+    }
+
+    /** The magic number used to indicate no version number metadata for this entity. */
+    static final int NOVERSION = -1;
+    /** The magic number used to indicate no changeset metadata for this entity. */
+    static final int NOCHANGESET = -1;
+
+    @Override
+    protected void parseNodes(List<Osmformat.Node> nodes) {
+      for (Osmformat.Node i : nodes) {
+        List<Tag> tags = new ArrayList<Tag>();
+        for (int j = 0; j < i.getKeysCount(); j++) {
+          tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
+        }
+        // long id, int version, Date timestamp, OsmUser user,
+        // long changesetId, Collection<Tag> tags,
+        // double latitude, double longitude
+        Node tmp;
+        long id = i.getId();
+        double latf = parseLat(i.getLat()), lonf = parseLon(i.getLon());
+
+        if (i.hasInfo()) {
+          Osmformat.Info info = i.getInfo();
+          tmp = new Node(new CommonEntityData(id, info.getVersion(), getDate(info),
+              getUser(info), info.getChangeset(), tags), latf, lonf);
+        } else {
+          tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
+              NOCHANGESET, tags), latf, lonf);
+        }
+        sink.process(new NodeContainer(tmp));
+
+      }
+    }
+    
+    @Override
+    protected void parseDense(Osmformat.DenseNodes nodes) {
+        long lastId = 0, lastLat = 0, lastLon = 0;
+        
+        int j = 0; // Index into the keysvals array.
+
+        // Stuff for dense info
+        long lasttimestamp = 0, lastchangeset = 0;
+        int lastuserSid = 0, lastuid = 0;
+        DenseInfo di = null;
+        if (nodes.hasDenseinfo()) {
+          di = nodes.getDenseinfo();
+        }
+        for (int i = 0; i < nodes.getIdCount(); i++) {
+            Node tmp;
+            List<Tag> tags = new ArrayList<Tag>(0);
+            long lat = nodes.getLat(i) + lastLat;
+            lastLat = lat;
+            long lon = nodes.getLon(i) + lastLon;
+            lastLon = lon;
+            long id = nodes.getId(i) + lastId;
+            lastId = id;
+            double latf = parseLat(lat), lonf = parseLon(lon);
+            // If empty, assume that nothing here has keys or vals.
+            if (nodes.getKeysValsCount() > 0) {
+                while (nodes.getKeysVals(j) != 0) {
+                    int keyid = nodes.getKeysVals(j++);
+                    int valid = nodes.getKeysVals(j++);
+                    tags.add(new Tag(getStringById(keyid), getStringById(valid)));
+                }
+                j++; // Skip over the '0' delimiter.
+            }
+            // Handle dense info.
+            if (di != null) {
+              int uid = di.getUid(i) + lastuid; lastuid = uid;
+              int userSid = di.getUserSid(i) + lastuserSid; lastuserSid = userSid;
+              long timestamp = di.getTimestamp(i) + lasttimestamp; lasttimestamp = timestamp;
+              int version = di.getVersion(i); 
+              long changeset = di.getChangeset(i) + lastchangeset; lastchangeset = changeset;
+
+              Date date = new Date(date_granularity * timestamp);
+
+              OsmUser user;
+              if (uid < 0) {
+                user = OsmUser.NONE;
+              } else {
+                user = new OsmUser(uid, getStringById(userSid));
+              }
+              tmp = new Node(new CommonEntityData(id, version, date, user, changeset, tags), latf, lonf);
+            } else {
+                tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
+                        NOCHANGESET, tags), latf, lonf);
+            }
+            sink.process(new NodeContainer(tmp));
+        }
+    }
+
+    @Override
+    protected void parseWays(List<Osmformat.Way> ways) {
+        for (Osmformat.Way i : ways) {
+            List<Tag> tags = new ArrayList<Tag>();
+            for (int j = 0; j < i.getKeysCount(); j++) {
+                tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
+            }
+                
+            long lastId = 0;
+            List<WayNode> nodes = new ArrayList<WayNode>();
+            for (long j : i.getRefsList()) {
+                nodes.add(new WayNode(j + lastId));
+                lastId = j + lastId;
+            }
+
+            long id = i.getId();
+
+            // long id, int version, Date timestamp, OsmUser user,
+            // long changesetId, Collection<Tag> tags,
+            // List<WayNode> wayNodes
+            Way tmp;
+            if (i.hasInfo()) {
+                Osmformat.Info info = i.getInfo();
+                tmp = new Way(new CommonEntityData(id, info.getVersion(), getDate(info),
+                        getUser(info), info.getChangeset(), tags), nodes);
+            } else {
+                tmp = new Way(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE, NOCHANGESET,
+                        tags), nodes);
+            }
+            sink.process(new WayContainer(tmp));
+        }
+    }
+
+    @Override
+    protected void parseRelations(List<Osmformat.Relation> rels) {
+        for (Osmformat.Relation i : rels) {
+            List<Tag> tags = new ArrayList<Tag>();
+            for (int j = 0; j < i.getKeysCount(); j++) {
+                tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
+            }
+
+            long id = i.getId();
+
+            long lastMid = 0;
+            List<RelationMember> nodes = new ArrayList<RelationMember>();
+            for (int j = 0; j < i.getMemidsCount(); j++) {
+                long mid = lastMid + i.getMemids(j);
+                lastMid = mid;
+                String role = getStringById(i.getRolesSid(j));
+                EntityType etype = null;
+
+                if (i.getTypes(j) == Osmformat.Relation.MemberType.NODE) {
+                    etype = EntityType.Node;
+                } else if (i.getTypes(j) == Osmformat.Relation.MemberType.WAY) {
+                    etype = EntityType.Way;
+                } else if (i.getTypes(j) == Osmformat.Relation.MemberType.RELATION) {
+                    etype = EntityType.Relation;
+                } else {
+                    assert false; // TODO; Illegal file?
+                }
+
+                nodes.add(new RelationMember(mid, etype, role));
+            }
+            // long id, int version, TimestampContainer timestampContainer,
+            // OsmUser user,
+            // long changesetId, Collection<Tag> tags,
+            // List<RelationMember> members
+            Relation tmp;
+            if (i.hasInfo()) {
+                Osmformat.Info info = i.getInfo();
+                tmp = new Relation(new CommonEntityData(id, info.getVersion(), getDate(info),
+                        getUser(info), info.getChangeset(), tags), nodes);
+            } else {
+                tmp = new Relation(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
+                        NOCHANGESET, tags), nodes);
+            }
+            sink.process(new RelationContainer(tmp));
+        }
+    }
+
+    @Override
+    public void parse(Osmformat.HeaderBlock block) {
+        for (String s : block.getRequiredFeaturesList()) {
+            if (s.equals("OsmSchema-V0.6")) {
+              continue; // We can parse this.
+            }
+            if (s.equals("DenseNodes")) {
+              continue; // We can parse this.
+            }
+           throw new OsmosisRuntimeException("File requires unknown feature: " + s);
+        }
+        
+        if (block.hasBbox()) {
+            String source = OsmosisConstants.VERSION;
+            if (block.hasSource()) {
+                source = block.getSource();
+            }
+
+            double multiplier = .000000001;
+            double rightf = block.getBbox().getRight() * multiplier;
+            double leftf = block.getBbox().getLeft() * multiplier;
+            double topf = block.getBbox().getTop() * multiplier;
+            double bottomf = block.getBbox().getBottom() * multiplier;
+
+            Bound bounds = new Bound(rightf, leftf, topf, bottomf, source);
+            sink.process(new BoundContainer(bounds));
+        }
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void setSink(Sink sink) {
+       this.sink = sink;
+    }
+
+    private Sink sink;
+}
diff --git a/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java
new file mode 100644
index 0000000..49b4a8d
--- /dev/null
+++ b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java
@@ -0,0 +1,57 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package crosby.binary.osmosis;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+import org.openstreetmap.osmosis.osmbinary.file.BlockInputStream;
+
+/** Glue code that implements a task that connects an InputStream a containing binary-format data to a Sink. 
+ * @author crosby
+ *
+ */
+public class OsmosisReader implements RunnableSource {
+	
+	private Sink sink;
+	
+    /**
+     * Make a reader based on a target input stream. 
+     * @param input The input stream to read from. 
+     */
+    public OsmosisReader(InputStream input) {
+        if (input == null) {
+            throw new Error("Null input");
+        }
+        this.input = input;
+        parser = new OsmosisBinaryParser();
+    }
+
+    @Override
+    public void setSink(Sink sink) {
+    	this.sink = sink;
+        parser.setSink(sink);
+    }
+
+    @Override
+    public void run() {
+        try {
+        	sink.initialize(Collections.<String, Object>emptyMap());
+        	
+            (new BlockInputStream(input, parser)).process();
+            
+        } catch (IOException e) {
+            throw new OsmosisRuntimeException("Unable to process PBF stream", e);
+        } finally {
+        	sink.release();
+        }
+    }
+    /** Store the input stream we're using. */
+    InputStream input;
+    /** The binary parser object. */
+    OsmosisBinaryParser parser;
+}
diff --git a/pbf/src/main/java/crosby/binary/osmosis/OsmosisReaderFactory.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisReaderFactory.java
similarity index 100%
rename from pbf/src/main/java/crosby/binary/osmosis/OsmosisReaderFactory.java
rename to osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisReaderFactory.java
diff --git a/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java
new file mode 100644
index 0000000..71e9617
--- /dev/null
+++ b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java
@@ -0,0 +1,511 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package crosby.binary.osmosis;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+import org.openstreetmap.osmosis.osmbinary.BinarySerializer;
+import org.openstreetmap.osmosis.osmbinary.Osmformat;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo;
+import org.openstreetmap.osmosis.osmbinary.StringTable;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType;
+import org.openstreetmap.osmosis.osmbinary.file.BlockOutputStream;
+import org.openstreetmap.osmosis.osmbinary.file.FileBlock;
+
+/**
+ * Receives data from the Osmosis pipeline and stores it in the PBF format.
+ */
+public class OsmosisSerializer extends BinarySerializer implements Sink {
+	private static final Logger LOG = Logger.getLogger(OsmosisSerializer.class.getName());
+	
+  /** Additional configuration flag for whether to serialize into DenseNodes/DenseInfo? */
+  protected boolean useDense = true;
+
+  /** Has the header been written yet? */
+  protected boolean headerWritten = false;
+  
+  /**
+   * Tracks the number of warnings that have occurred during serialisation.
+   */
+  static int warncount = 0;
+
+	/**
+	 * Construct a serializer that writes to the target BlockOutputStream.
+	 * 
+	 * @param output
+	 *            The PBF block stream to send serialized data.
+	 */
+  public OsmosisSerializer(BlockOutputStream output) {
+	  super(output);
+  }
+
+  /**
+	 * Change the flag of whether to use the dense format.
+	 * 
+	 * @param useDense
+	 *            The new use dense value.
+	 */
+  public void setUseDense(boolean useDense) {
+    this.useDense = useDense;
+  }
+
+  /** Base class containing common code needed for serializing each type of primitives. */
+    private abstract class Prim<T extends Entity> {
+      /** Queue that tracks the list of all primitives. */
+      ArrayList<T> contents = new ArrayList<T>();
+
+      /** Add to the queue.
+       * @param item The entity to add */
+        public void add(T item) {
+            contents.add(item);
+        }
+
+        /** Add all of the tags of all entities in the queue to the stringtable. */
+        public void addStringsToStringtable() {
+            StringTable stable = getStringTable();
+            for (T i : contents) {
+                Collection<Tag> tags = i.getTags();
+                for (Tag tag : tags) {
+                    stable.incr(tag.getKey());
+                    stable.incr(tag.getValue());
+                }
+                if (!omit_metadata) {
+                    stable.incr(i.getUser().getName());
+                }
+            }
+        }
+        private static final int MAXWARN = 100;
+        public void serializeMetadataDense(DenseInfo.Builder b, List<? extends Entity> entities) {
+			if (omit_metadata) {
+				return;
+			}
+
+			long lasttimestamp = 0, lastchangeset = 0;
+			int lastuserSid = 0, lastuid = 0;
+			StringTable stable = getStringTable();
+			for (Entity e : entities) {
+
+            if (e.getUser() == OsmUser.NONE && warncount  < MAXWARN) {
+              LOG.warning("Attention: Data being output lacks metadata. Please use omitmetadata=true");
+              warncount++;
+            }
+				int uid = e.getUser().getId();
+				int userSid = stable.getIndex(e.getUser().getName());
+				int timestamp = (int) (e.getTimestamp().getTime() / date_granularity);
+				int version = e.getVersion();
+				long changeset = e.getChangesetId();
+
+				b.addVersion(version);
+				b.addTimestamp(timestamp - lasttimestamp);
+				lasttimestamp = timestamp;
+				b.addChangeset(changeset - lastchangeset);
+				lastchangeset = changeset;
+				b.addUid(uid - lastuid);
+				lastuid = uid;
+				b.addUserSid(userSid - lastuserSid);
+				lastuserSid = userSid;
+			}
+        }
+         
+        public Osmformat.Info.Builder serializeMetadata(Entity e) {
+            StringTable stable = getStringTable();
+            Osmformat.Info.Builder b = Osmformat.Info.newBuilder();
+            if (!omit_metadata) {
+                if (e.getUser() == OsmUser.NONE && warncount  < MAXWARN) {
+                  LOG.warning("Attention: Data being output lacks metadata. Please use omitmetadata=true");
+                  warncount++;
+                }
+                if (e.getUser() != OsmUser.NONE) {
+                    b.setUid(e.getUser().getId());
+                    b.setUserSid(stable.getIndex(e.getUser().getName()));
+                }
+                b.setTimestamp((int) (e.getTimestamp().getTime() / date_granularity));
+                b.setVersion(e.getVersion());
+                b.setChangeset(e.getChangesetId());
+            }
+            return b;
+        }
+    }
+
+    private class NodeGroup extends Prim<Node> implements PrimGroupWriterInterface {
+
+      public Osmformat.PrimitiveGroup serialize() {
+          if (useDense) {
+            return serializeDense();
+          } else {
+            return serializeNonDense();
+          }
+      }
+        
+        /**
+         *  Serialize all nodes in the 'dense' format.
+         */
+        public Osmformat.PrimitiveGroup serializeDense() {
+            if (contents.size() == 0) {
+              return null;
+            }
+            // System.out.format("%d Dense   ",nodes.size());
+            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
+                    .newBuilder();
+            StringTable stable = getStringTable();
+
+            long lastlat = 0, lastlon = 0, lastid = 0;
+            Osmformat.DenseNodes.Builder bi = Osmformat.DenseNodes.newBuilder();
+            boolean doesBlockHaveTags = false;
+            // Does anything in this block have tags?
+            for (Node i : contents) {
+              doesBlockHaveTags = doesBlockHaveTags || (!i.getTags().isEmpty());
+            }
+            if (!omit_metadata) {
+              Osmformat.DenseInfo.Builder bdi = Osmformat.DenseInfo.newBuilder();
+              serializeMetadataDense(bdi, contents);
+              bi.setDenseinfo(bdi);
+            }
+              
+              for (Node i : contents) {
+                long id = i.getId();
+                int lat = mapDegrees(i.getLatitude());
+                int lon = mapDegrees(i.getLongitude());
+                bi.addId(id - lastid);
+                lastid = id;
+                bi.addLon(lon - lastlon);
+                lastlon = lon;
+                bi.addLat(lat - lastlat);
+                lastlat = lat;
+
+                // Then we must include tag information.
+                if (doesBlockHaveTags) {
+                  for (Tag t : i.getTags()) {
+                      bi.addKeysVals(stable.getIndex(t.getKey()));
+                      bi.addKeysVals(stable.getIndex(t.getValue()));
+                  }
+                  bi.addKeysVals(0); // Add delimiter.
+                }
+            }
+            builder.setDense(bi);
+            return builder.build();
+        }
+        
+        /**
+         *  Serialize all nodes in the non-dense format.
+         * 
+         * @param parentbuilder Add to this PrimitiveBlock.
+         */
+        public Osmformat.PrimitiveGroup serializeNonDense() {
+          if (contents.size() == 0) {
+            return null;
+          }
+          // System.out.format("%d Nodes   ",nodes.size());
+          StringTable stable = getStringTable();
+          Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
+          .newBuilder();
+          for (Node i : contents) {
+            long id = i.getId();
+            int lat = mapDegrees(i.getLatitude());
+            int lon = mapDegrees(i.getLongitude());
+            Osmformat.Node.Builder bi = Osmformat.Node.newBuilder();
+            bi.setId(id);
+            bi.setLon(lon);
+            bi.setLat(lat);
+            for (Tag t : i.getTags()) {
+              bi.addKeys(stable.getIndex(t.getKey()));
+              bi.addVals(stable.getIndex(t.getValue()));
+            }
+            if (!omit_metadata) {
+              bi.setInfo(serializeMetadata(i));
+            }
+            builder.addNodes(bi);
+          }
+          return builder.build();
+        }
+    
+    }
+
+    
+
+    private class WayGroup extends Prim<Way> implements PrimGroupWriterInterface {
+      public Osmformat.PrimitiveGroup serialize() {
+        if (contents.size() == 0) {
+          return null;
+        }
+
+            // System.out.format("%d Ways  ",contents.size());
+            StringTable stable = getStringTable();
+            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
+                    .newBuilder();
+            for (Way i : contents) {
+                Osmformat.Way.Builder bi = Osmformat.Way.newBuilder();
+                bi.setId(i.getId());
+                long lastid = 0;
+                for (WayNode j : i.getWayNodes()) {
+                    long id = j.getNodeId();
+                    bi.addRefs(id - lastid);
+                    lastid = id;
+                }
+                for (Tag t : i.getTags()) {
+                    bi.addKeys(stable.getIndex(t.getKey()));
+                    bi.addVals(stable.getIndex(t.getValue()));
+                }
+                if (!omit_metadata) {
+                    bi.setInfo(serializeMetadata(i));
+                }
+                builder.addWays(bi);
+            }
+            return builder.build();
+        }
+    }
+
+    private class RelationGroup extends Prim<Relation> implements
+            PrimGroupWriterInterface {
+        public void addStringsToStringtable() {
+            StringTable stable = getStringTable();
+            super.addStringsToStringtable();
+            for (Relation i : contents) {
+                for (RelationMember j : i.getMembers()) {
+                    stable.incr(j.getMemberRole());
+                }
+            }
+        }
+
+        public Osmformat.PrimitiveGroup serialize() {
+          if (contents.size() == 0) {
+            return null;
+          }
+
+          // System.out.format("%d Relations  ",contents.size());
+            StringTable stable = getStringTable();
+            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
+                    .newBuilder();
+            for (Relation i : contents) {
+                Osmformat.Relation.Builder bi = Osmformat.Relation.newBuilder();
+                bi.setId(i.getId());
+                RelationMember[] arr = new RelationMember[i.getMembers().size()];
+                i.getMembers().toArray(arr);
+                long lastid = 0;
+                for (RelationMember j : i.getMembers()) {
+                    long id = j.getMemberId();
+                    bi.addMemids(id - lastid);
+                    lastid = id;
+                    if (j.getMemberType() == EntityType.Node) {
+                        bi.addTypes(MemberType.NODE);
+                    } else if (j.getMemberType() == EntityType.Way) {
+                        bi.addTypes(MemberType.WAY);
+                    } else if (j.getMemberType() == EntityType.Relation) {
+                        bi.addTypes(MemberType.RELATION);
+                    } else {
+                        assert (false); // Software bug: Unknown entity.
+                    }
+                    bi.addRolesSid(stable.getIndex(j.getMemberRole()));
+                }
+
+                for (Tag t : i.getTags()) {
+                    bi.addKeys(stable.getIndex(t.getKey()));
+                    bi.addVals(stable.getIndex(t.getValue()));
+                }
+                if (!omit_metadata) {
+                    bi.setInfo(serializeMetadata(i));
+                }
+                builder.addRelations(bi);
+            }
+            return builder.build();
+        }
+    }
+
+    /* One list for each type */
+    private WayGroup ways;
+    private NodeGroup nodes;
+    private RelationGroup relations;
+
+    private Processor processor = new Processor();
+
+    /**
+     * Buffer up events into groups that are all of the same type, or all of the
+     * same length, then process each buffer.
+     */
+    public class Processor implements EntityProcessor {
+        @Override
+        public void process(BoundContainer bound) {
+            // Specialcase this. Assume we only ever get one contigious bound
+            // request.
+            switchTypes();
+            processBounds(bound.getEntity());
+        }
+
+		/**
+		 * Check if we've reached the batch size limit and process the batch if
+		 * we have.
+		 */
+        public void checkLimit() {
+            total_entities++;
+            if (++batch_size < batch_limit) {
+                return;
+            }
+            switchTypes();
+            processBatch();
+        }
+
+        @Override
+        public void process(NodeContainer node) {
+            if (nodes == null) {
+                writeEmptyHeaderIfNeeded();
+                // Need to switch types.
+                switchTypes();
+                nodes = new NodeGroup();
+            }
+            nodes.add(node.getEntity());
+            checkLimit();
+        }
+
+        @Override
+        public void process(WayContainer way) {
+            if (ways == null) {
+                writeEmptyHeaderIfNeeded();
+                switchTypes();
+                ways = new WayGroup();
+            }
+            ways.add(way.getEntity());
+            checkLimit();
+        }
+
+        @Override
+        public void process(RelationContainer relation) {
+            if (relations == null) {
+                writeEmptyHeaderIfNeeded();
+                switchTypes();
+                relations = new RelationGroup();
+            }
+            relations.add(relation.getEntity());
+            checkLimit();
+        }
+    }
+
+    /**
+     * At the end of this function, all of the lists of unprocessed 'things'
+     * must be null
+     */
+    private void switchTypes() {
+        if (nodes != null) {
+            groups.add(nodes);
+            nodes = null;
+        } else if (ways != null) {
+            groups.add(ways);
+            ways = null;
+        } else if (relations != null) {
+            groups.add(relations);
+            relations = null;
+        } else {
+            return; // No data. Is this an empty file?
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void processBounds(Bound entity) {
+        Osmformat.HeaderBlock.Builder headerblock = Osmformat.HeaderBlock
+                .newBuilder();
+        
+        Osmformat.HeaderBBox.Builder bbox = Osmformat.HeaderBBox.newBuilder();
+        bbox.setLeft(mapRawDegrees(entity.getLeft()));
+        bbox.setBottom(mapRawDegrees(entity.getBottom()));
+        bbox.setRight(mapRawDegrees(entity.getRight()));
+        bbox.setTop(mapRawDegrees(entity.getTop()));
+        headerblock.setBbox(bbox);
+
+        if (entity.getOrigin() != null) {
+        	headerblock.setSource(entity.getOrigin());
+        }
+        finishHeader(headerblock);
+    }
+
+    /** Write empty header block when there's no bounds entity. */
+    public void writeEmptyHeaderIfNeeded() {
+      if (headerWritten) {
+        return;
+      }
+      Osmformat.HeaderBlock.Builder headerblock = Osmformat.HeaderBlock.newBuilder();
+      finishHeader(headerblock);
+    }
+
+    /** Write the header fields that are always needed.
+     * 
+     * @param headerblock Incomplete builder to complete and write.
+     * */
+    public void finishHeader(Osmformat.HeaderBlock.Builder headerblock) {
+      headerblock.setWritingprogram(OsmosisConstants.VERSION);
+      headerblock.addRequiredFeatures("OsmSchema-V0.6");
+      if (useDense) {
+        headerblock.addRequiredFeatures("DenseNodes");
+      }
+      Osmformat.HeaderBlock message = headerblock.build();
+      try {
+          output.write(FileBlock.newInstance("OSMHeader", message
+                  .toByteString(), null));
+      } catch (IOException e) {
+          throw new OsmosisRuntimeException("Unable to write OSM header.", e);
+      }
+      headerWritten = true;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+   
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void process(EntityContainer entityContainer) {
+        entityContainer.process(processor);
+    }
+
+    @Override
+    public void complete() {
+        try {
+            switchTypes();
+            processBatch();
+            flush();
+        } catch (IOException e) {
+        	throw new OsmosisRuntimeException("Unable to complete the PBF file.", e);
+        }
+    }
+
+    @Override
+    public void release() {
+        try {
+            close();
+        } catch (IOException e) {
+        	LOG.log(Level.WARNING, "Unable to release PBF file resources during release.", e);
+        }
+
+    }
+}
diff --git a/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java
new file mode 100644
index 0000000..7b030d4
--- /dev/null
+++ b/osmosis-pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java
@@ -0,0 +1,61 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package crosby.binary.osmosis;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
+
+import org.openstreetmap.osmosis.osmbinary.file.BlockOutputStream;
+
+/**
+ * The task manager factory for a binary (PBF) writer.
+ */
+public class OsmosisSerializerFactory extends TaskManagerFactory {
+    private static final String ARG_FILE_NAME = "file";
+    private static final String DEFAULT_FILE_NAME = "dump.osm.pbf";
+
+    @Override
+    protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+        // TODO Auto-generated method stub
+        String fileName;
+        File file;
+        OsmosisSerializer task = null;
+
+        // Get the task arguments.
+        fileName = getStringArgument(taskConfig, ARG_FILE_NAME,
+                getDefaultStringArgument(taskConfig, DEFAULT_FILE_NAME));
+
+        // Create a file object from the file name provided.
+        file = new File(fileName);
+
+        // Build the task object.
+        try {
+            BlockOutputStream output = new BlockOutputStream(
+                    new FileOutputStream(file));
+            task = new OsmosisSerializer(output);
+            task.configBatchLimit(this.getIntegerArgument(taskConfig,
+                    "batchlimit", 8000));
+            task.configOmit(this.getBooleanArgument(taskConfig, "omitmetadata",
+                    false));
+            task.setUseDense(this.getBooleanArgument(taskConfig, "usedense",
+                true));
+            task.configGranularity(this.getIntegerArgument(taskConfig,
+                    "granularity", 100));
+
+            output.setCompress(this.getStringArgument(taskConfig, "compress",
+                    "deflate"));
+
+        } catch (FileNotFoundException e) {
+        	throw new OsmosisRuntimeException("Failed to initialize Osmosis pbf serializer.", e);
+        }
+
+        return new SinkManager(taskConfig.getId(), task, taskConfig
+                .getPipeArgs());
+    }
+}
diff --git a/pbf/src/main/resources/osmosis-plugins.conf b/osmosis-pbf/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from pbf/src/main/resources/osmosis-plugins.conf
rename to osmosis-pbf/src/main/resources/osmosis-plugins.conf
diff --git a/osmosis-pbf/src/test/java/crosby/binary/osmosis/OsmosisReaderAndSerializerTest.java b/osmosis-pbf/src/test/java/crosby/binary/osmosis/OsmosisReaderAndSerializerTest.java
new file mode 100644
index 0000000..7820844
--- /dev/null
+++ b/osmosis-pbf/src/test/java/crosby/binary/osmosis/OsmosisReaderAndSerializerTest.java
@@ -0,0 +1,52 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package crosby.binary.osmosis;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the OsmosisReader and OsmosisSerializer classes.
+ * 
+ * @author Brett Henderson
+ */
+public class OsmosisReaderAndSerializerTest extends AbstractDataTest {
+	/**
+	 * Tests writing to and reading from PBF files.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testWriteAndRead() throws IOException {
+		// Generate data files.
+		File inputXmlFile = dataUtils.createDataFile("v0_6/data-snapshot.osm");
+		File pbfFile = dataUtils.newFile();
+		File outputXmlFile = dataUtils.newFile();
+
+        // Read the XML and write to PBF.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-xml-0.6",
+        		inputXmlFile.getPath(),
+        		"--write-pbf-0.6",
+        		pbfFile.getPath()
+                });
+        
+        // Read the PBF and write to XML.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-pbf-0.6",
+        		pbfFile.getPath(),
+        		"--write-xml-0.6",
+        		outputXmlFile.getPath()
+                });
+
+        // Validate that the output file matches the input file.
+        dataUtils.compareFiles(inputXmlFile, outputXmlFile);
+	}
+}
diff --git a/osmosis-pbf/src/test/resources/data/template/v0_6/data-snapshot.osm b/osmosis-pbf/src/test/resources/data/template/v0_6/data-snapshot.osm
new file mode 100644
index 0000000..fd0631c
--- /dev/null
+++ b/osmosis-pbf/src/test/resources/data/template/v0_6/data-snapshot.osm
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-pbf2/.checkstyle b/osmosis-pbf2/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-pbf2/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-pbf2/.gitignore b/osmosis-pbf2/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-pbf2/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-pbf2/build.gradle b/osmosis-pbf2/build.gradle
new file mode 100644
index 0000000..8035269
--- /dev/null
+++ b/osmosis-pbf2/build.gradle
@@ -0,0 +1,8 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-osm-binary')
+    compile group: 'com.google.protobuf', name: 'protobuf-java', version: dependencyVersionProtobuf
+    testCompile project(':osmosis-pbf')
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/PbfPluginLoader.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/PbfPluginLoader.java
new file mode 100644
index 0000000..e4aef6d
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/PbfPluginLoader.java
@@ -0,0 +1,33 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.pbf2.v0_6.PbfReaderFactory;
+
+
+/**
+ * The plugin loader for the PBF2 tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfPluginLoader implements PluginLoader {
+
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		Map<String, TaskManagerFactory> factoryMap;
+
+		PbfReaderFactory reader = new PbfReaderFactory();
+
+		factoryMap = new HashMap<String, TaskManagerFactory>();
+		factoryMap.put("read-pbf-fast", reader);
+		factoryMap.put("rbf", reader);
+
+		factoryMap.put("read-pbf-fast-0.6", reader);
+
+		return factoryMap;
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReader.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReader.java
new file mode 100644
index 0000000..f3e62b8
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReader.java
@@ -0,0 +1,96 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.pbf2.v0_6.impl.PbfDecoder;
+import org.openstreetmap.osmosis.pbf2.v0_6.impl.PbfStreamSplitter;
+
+
+/**
+ * An OSM data source reading from a PBF file. The entire contents of the file
+ * are read.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfReader implements RunnableSource {
+
+	private File file;
+	private Sink sink;
+	private int workers;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to read.
+	 * @param workers
+	 *            The number of worker threads for decoding PBF blocks.
+	 */
+	public PbfReader(File file, int workers) {
+		this.file = file;
+		this.workers = workers;
+	}
+
+
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+
+
+	@Override
+	public void run() {
+		PbfStreamSplitter streamSplitter = null;
+		ExecutorService executorService = Executors.newFixedThreadPool(workers);
+
+		try {
+			InputStream inputStream;
+
+			sink.initialize(Collections.<String, Object>emptyMap());
+
+			// make "-" an alias for /dev/stdin
+			if (file.getName().equals("-")) {
+				inputStream = System.in;
+			} else {
+				inputStream = new FileInputStream(file);
+			}
+
+			// Create a stream splitter to break the PBF stream into blobs.
+			streamSplitter = new PbfStreamSplitter(new DataInputStream(inputStream));
+
+			// Process all blobs of data in the stream using threads from the
+			// executor service. We allow the decoder to issue an extra blob
+			// than there are workers to ensure there is another blob
+			// immediately ready for processing when a worker thread completes.
+			// The main thread is responsible for splitting blobs from the
+			// request stream, and sending decoded entities to the sink.
+			PbfDecoder pbfDecoder = new PbfDecoder(streamSplitter, executorService, workers + 1, sink);
+			pbfDecoder.run();
+
+			sink.complete();
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read PBF file " + file + ".", e);
+		} finally {
+			sink.release();
+
+			executorService.shutdownNow();
+
+			if (streamSplitter != null) {
+				streamSplitter.release();
+			}
+		}
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReaderFactory.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReaderFactory.java
new file mode 100644
index 0000000..fe2810c
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/PbfReaderFactory.java
@@ -0,0 +1,48 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.RunnableSourceManager;
+
+
+/**
+ * The task manager factory for a PBF reader.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfReaderFactory extends TaskManagerFactory {
+	private static final String ARG_FILE_NAME = "file";
+	private static final String DEFAULT_FILE_NAME = "dump.osm.pbf";
+	private static final String ARG_WORKERS = "workers";
+	private static final int DEFAULT_WORKERS = 1;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String fileName;
+		File file;
+		PbfReader task;
+		int workers;
+
+		// Get the task arguments.
+		fileName = getStringArgument(taskConfig, ARG_FILE_NAME,
+				getDefaultStringArgument(taskConfig, DEFAULT_FILE_NAME));
+		workers = getIntegerArgument(taskConfig, ARG_WORKERS, DEFAULT_WORKERS);
+
+		// Create a file object from the file name provided.
+		file = new File(fileName);
+
+		// Build the task object.
+		task = new PbfReader(file, workers);
+
+		return new RunnableSourceManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
+	}
+
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoder.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoder.java
new file mode 100644
index 0000000..f030a91
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoder.java
@@ -0,0 +1,454 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.osmbinary.Osmformat;
+import org.openstreetmap.osmosis.osmbinary.Fileformat.Blob;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseNodes;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.HeaderBBox;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Info;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Node;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveGroup;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Relation;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Way;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.Relation.MemberType;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+
+/**
+ * Converts PBF block data into decoded entities ready to be passed into an
+ * Osmosis pipeline. This class is designed to be passed into a pool of worker
+ * threads to allow multi-threaded decoding.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfBlobDecoder implements Runnable {
+
+	private static Logger log = Logger.getLogger(PbfBlobDecoder.class.getName());
+
+	private static final double COORDINATE_SCALING_FACTOR = 0.000000001;
+	private static final int EMPTY_VERSION = -1;
+	private static final Date EMPTY_TIMESTAMP = new Date(0);
+	private static final long EMPTY_CHANGESET = -1;
+
+	private String blobType;
+	private byte[] rawBlob;
+	private PbfBlobDecoderListener listener;
+	private List<EntityContainer> decodedEntities;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param blobType
+	 *            The type of blob.
+	 * @param rawBlob
+	 *            The raw data of the blob.
+	 * @param listener
+	 *            The listener for receiving decoding results.
+	 */
+	public PbfBlobDecoder(String blobType, byte[] rawBlob, PbfBlobDecoderListener listener) {
+		this.blobType = blobType;
+		this.rawBlob = rawBlob;
+		this.listener = listener;
+	}
+
+
+	private byte[] readBlobContent() throws IOException {
+		Blob blob = Blob.parseFrom(rawBlob);
+		byte[] blobData;
+
+		if (blob.hasRaw()) {
+			blobData = blob.getRaw().toByteArray();
+		} else if (blob.hasZlibData()) {
+			Inflater inflater = new Inflater();
+			inflater.setInput(blob.getZlibData().toByteArray());
+			blobData = new byte[blob.getRawSize()];
+			try {
+				inflater.inflate(blobData);
+			} catch (DataFormatException e) {
+				throw new OsmosisRuntimeException("Unable to decompress PBF blob.", e);
+			}
+			if (!inflater.finished()) {
+				throw new OsmosisRuntimeException("PBF blob contains incomplete compressed data.");
+			}
+		} else {
+			throw new OsmosisRuntimeException("PBF blob uses unsupported compression, only raw or zlib may be used.");
+		}
+
+		return blobData;
+	}
+
+
+	private void processOsmHeader(byte[] data) throws InvalidProtocolBufferException {
+		Osmformat.HeaderBlock header = Osmformat.HeaderBlock.parseFrom(data);
+
+		// Build the list of active and unsupported features in the file.
+		List<String> supportedFeatures = Arrays.asList("OsmSchema-V0.6", "DenseNodes");
+		List<String> activeFeatures = new ArrayList<String>();
+		List<String> unsupportedFeatures = new ArrayList<String>();
+		for (String feature : header.getRequiredFeaturesList()) {
+			if (supportedFeatures.contains(feature)) {
+				activeFeatures.add(feature);
+			} else {
+				unsupportedFeatures.add(feature);
+			}
+		}
+
+		// We can't continue if there are any unsupported features. We wait
+		// until now so that we can display all unsupported features instead of
+		// just the first one we encounter.
+		if (unsupportedFeatures.size() > 0) {
+			throw new OsmosisRuntimeException("PBF file contains unsupported features " + unsupportedFeatures);
+		}
+
+		// Build a new bound object which corresponds to the header.
+		Bound bound;
+		if (header.hasBbox()) {
+			HeaderBBox bbox = header.getBbox();
+			bound = new Bound(bbox.getRight() * COORDINATE_SCALING_FACTOR, bbox.getLeft() * COORDINATE_SCALING_FACTOR,
+					bbox.getTop() * COORDINATE_SCALING_FACTOR, bbox.getBottom() * COORDINATE_SCALING_FACTOR,
+					header.getSource());
+		} else {
+			bound = new Bound(header.getSource());
+		}
+
+		// Add the bound object to the results.
+		decodedEntities.add(new BoundContainer(bound));
+	}
+
+
+	private void buildTags(CommonEntityData entityData, List<Integer> keys, List<Integer> values,
+			PbfFieldDecoder fieldDecoder) {
+		Collection<Tag> tags = entityData.getTags();
+
+		// Ensure parallel lists are of equal size.
+		if (keys.size() != values.size()) {
+			throw new OsmosisRuntimeException("Number of tag keys (" + keys.size() + ") and tag values ("
+					+ values.size() + ") don't match");
+		}
+
+		Iterator<Integer> keyIterator = keys.iterator();
+		Iterator<Integer> valueIterator = values.iterator();
+		while (keyIterator.hasNext()) {
+			String key = fieldDecoder.decodeString(keyIterator.next());
+			String value = fieldDecoder.decodeString(valueIterator.next());
+			Tag tag = new Tag(key, value);
+			tags.add(tag);
+		}
+	}
+
+
+	private CommonEntityData buildCommonEntityData(long entityId, List<Integer> keys, List<Integer> values, Info info,
+			PbfFieldDecoder fieldDecoder) {
+		OsmUser user;
+		CommonEntityData entityData;
+
+		// Build the user, but only if one exists.
+		if (info.hasUid() && info.getUid() >= 0 && info.hasUserSid()) {
+			user = new OsmUser(info.getUid(), fieldDecoder.decodeString(info.getUserSid()));
+		} else {
+			user = OsmUser.NONE;
+		}
+
+		entityData = new CommonEntityData(entityId, info.getVersion(),
+				fieldDecoder.decodeTimestamp(info.getTimestamp()), user, info.getChangeset());
+
+		buildTags(entityData, keys, values, fieldDecoder);
+
+		return entityData;
+	}
+
+
+	private CommonEntityData buildCommonEntityData(long entityId, List<Integer> keys, List<Integer> values,
+			PbfFieldDecoder fieldDecoder) {
+		CommonEntityData entityData;
+
+		entityData = new CommonEntityData(entityId, EMPTY_VERSION, EMPTY_TIMESTAMP, OsmUser.NONE, EMPTY_CHANGESET);
+
+		buildTags(entityData, keys, values, fieldDecoder);
+
+		return entityData;
+	}
+
+
+	private void processNodes(List<Node> nodes, PbfFieldDecoder fieldDecoder) {
+		for (Node node : nodes) {
+			org.openstreetmap.osmosis.core.domain.v0_6.Node osmNode;
+			CommonEntityData entityData;
+
+			if (node.hasInfo()) {
+				entityData = buildCommonEntityData(node.getId(), node.getKeysList(), node.getValsList(),
+						node.getInfo(), fieldDecoder);
+
+			} else {
+				entityData = buildCommonEntityData(node.getId(), node.getKeysList(), node.getValsList(), fieldDecoder);
+			}
+
+			osmNode = new org.openstreetmap.osmosis.core.domain.v0_6.Node(entityData, fieldDecoder.decodeLatitude(node
+					.getLat()), fieldDecoder.decodeLatitude(node.getLon()));
+
+			// Add the bound object to the results.
+			decodedEntities.add(new NodeContainer(osmNode));
+		}
+	}
+
+
+	private void processNodes(DenseNodes nodes, PbfFieldDecoder fieldDecoder) {
+		List<Long> idList = nodes.getIdList();
+		List<Long> latList = nodes.getLatList();
+		List<Long> lonList = nodes.getLonList();
+
+		// Ensure parallel lists are of equal size.
+		if ((idList.size() != latList.size()) || (idList.size() != lonList.size())) {
+			throw new OsmosisRuntimeException("Number of ids (" + idList.size() + "), latitudes (" + latList.size()
+					+ "), and longitudes (" + lonList.size() + ") don't match");
+		}
+
+		Iterator<Integer> keysValuesIterator = nodes.getKeysValsList().iterator();
+
+		DenseInfo denseInfo;
+		if (nodes.hasDenseinfo()) {
+			denseInfo = nodes.getDenseinfo();
+		} else {
+			denseInfo = null;
+		}
+
+		long nodeId = 0;
+		long latitude = 0;
+		long longitude = 0;
+		int userId = 0;
+		int userSid = 0;
+		long timestamp = 0;
+		long changesetId = 0;
+		for (int i = 0; i < idList.size(); i++) {
+			CommonEntityData entityData;
+			org.openstreetmap.osmosis.core.domain.v0_6.Node node;
+
+			// Delta decode node fields.
+			nodeId += idList.get(i);
+			latitude += latList.get(i);
+			longitude += lonList.get(i);
+
+			if (denseInfo != null) {
+				// Delta decode dense info fields.
+				userId += denseInfo.getUid(i);
+				userSid += denseInfo.getUserSid(i);
+				timestamp += denseInfo.getTimestamp(i);
+				changesetId += denseInfo.getChangeset(i);
+
+				// Build the user, but only if one exists.
+				OsmUser user;
+				if (userId >= 0) {
+					user = new OsmUser(userId, fieldDecoder.decodeString(userSid));
+				} else {
+					user = OsmUser.NONE;
+				}
+
+				entityData = new CommonEntityData(nodeId, denseInfo.getVersion(i),
+						fieldDecoder.decodeTimestamp(timestamp), user, changesetId);
+			} else {
+				entityData = new CommonEntityData(nodeId, EMPTY_VERSION, EMPTY_TIMESTAMP, OsmUser.NONE,
+						EMPTY_CHANGESET);
+			}
+
+			// Build the tags. The key and value string indexes are sequential
+			// in the same PBF array. Each set of tags is delimited by an index
+			// with a value of 0.
+			Collection<Tag> tags = entityData.getTags();
+			while (keysValuesIterator.hasNext()) {
+				int keyIndex = keysValuesIterator.next();
+				if (keyIndex == 0) {
+					break;
+				}
+				if (!keysValuesIterator.hasNext()) {
+					throw new OsmosisRuntimeException(
+							"The PBF DenseInfo keys/values list contains a key with no corresponding value.");
+				}
+				int valueIndex = keysValuesIterator.next();
+
+				Tag tag = new Tag(fieldDecoder.decodeString(keyIndex), fieldDecoder.decodeString(valueIndex));
+				tags.add(tag);
+			}
+
+			node = new org.openstreetmap.osmosis.core.domain.v0_6.Node(entityData,
+					fieldDecoder.decodeLatitude(latitude), fieldDecoder.decodeLongitude(longitude));
+
+			// Add the bound object to the results.
+			decodedEntities.add(new NodeContainer(node));
+		}
+	}
+
+
+	private void processWays(List<Way> ways, PbfFieldDecoder fieldDecoder) {
+		for (Way way : ways) {
+			org.openstreetmap.osmosis.core.domain.v0_6.Way osmWay;
+			CommonEntityData entityData;
+
+			if (way.hasInfo()) {
+				entityData = buildCommonEntityData(way.getId(), way.getKeysList(), way.getValsList(), way.getInfo(),
+						fieldDecoder);
+
+			} else {
+				entityData = buildCommonEntityData(way.getId(), way.getKeysList(), way.getValsList(), fieldDecoder);
+			}
+
+			osmWay = new org.openstreetmap.osmosis.core.domain.v0_6.Way(entityData);
+
+			// Build up the list of way nodes for the way. The node ids are
+			// delta encoded meaning that each id is stored as a delta against
+			// the previous one.
+			long nodeId = 0;
+			List<WayNode> wayNodes = osmWay.getWayNodes();
+			for (long nodeIdOffset : way.getRefsList()) {
+				nodeId += nodeIdOffset;
+				wayNodes.add(new WayNode(nodeId));
+			}
+
+			decodedEntities.add(new WayContainer(osmWay));
+		}
+	}
+
+
+	private void buildRelationMembers(org.openstreetmap.osmosis.core.domain.v0_6.Relation relation,
+			List<Long> memberIds, List<Integer> memberRoles, List<MemberType> memberTypes,
+			PbfFieldDecoder fieldDecoder) {
+
+		List<RelationMember> members = relation.getMembers();
+
+		// Ensure parallel lists are of equal size.
+		if ((memberIds.size() != memberRoles.size()) || (memberIds.size() != memberTypes.size())) {
+			throw new OsmosisRuntimeException("Number of member ids (" + memberIds.size() + "), member roles ("
+					+ memberRoles.size() + "), and member types (" + memberTypes.size() + ") don't match");
+		}
+
+		Iterator<Long> memberIdIterator = memberIds.iterator();
+		Iterator<Integer> memberRoleIterator = memberRoles.iterator();
+		Iterator<MemberType> memberTypeIterator = memberTypes.iterator();
+
+		// Build up the list of relation members for the way. The member ids are
+		// delta encoded meaning that each id is stored as a delta against
+		// the previous one.
+		long memberId = 0;
+		while (memberIdIterator.hasNext()) {
+			MemberType memberType = memberTypeIterator.next();
+			memberId += memberIdIterator.next();
+			EntityType entityType;
+			RelationMember member;
+
+			if (memberType == MemberType.NODE) {
+				entityType = EntityType.Node;
+			} else if (memberType == MemberType.WAY) {
+				entityType = EntityType.Way;
+			} else if (memberType == MemberType.RELATION) {
+				entityType = EntityType.Relation;
+			} else {
+				throw new OsmosisRuntimeException("Member type of " + memberType + " is not supported.");
+			}
+
+			member = new RelationMember(memberId, entityType, fieldDecoder.decodeString(memberRoleIterator.next()));
+
+			members.add(member);
+		}
+	}
+
+
+	private void processRelations(List<Relation> relations, PbfFieldDecoder fieldDecoder) {
+		for (Relation relation : relations) {
+			org.openstreetmap.osmosis.core.domain.v0_6.Relation osmRelation;
+			CommonEntityData entityData;
+
+			if (relation.hasInfo()) {
+				entityData = buildCommonEntityData(relation.getId(), relation.getKeysList(), relation.getValsList(),
+						relation.getInfo(), fieldDecoder);
+
+			} else {
+				entityData = buildCommonEntityData(relation.getId(), relation.getKeysList(), relation.getValsList(),
+						fieldDecoder);
+			}
+
+			osmRelation = new org.openstreetmap.osmosis.core.domain.v0_6.Relation(entityData);
+
+			buildRelationMembers(osmRelation, relation.getMemidsList(), relation.getRolesSidList(),
+					relation.getTypesList(), fieldDecoder);
+
+			// Add the bound object to the results.
+			decodedEntities.add(new RelationContainer(osmRelation));
+		}
+	}
+
+
+	private void processOsmPrimitives(byte[] data) throws InvalidProtocolBufferException {
+		Osmformat.PrimitiveBlock block = Osmformat.PrimitiveBlock.parseFrom(data);
+		PbfFieldDecoder fieldDecoder = new PbfFieldDecoder(block);
+
+		for (PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) {
+			log.finer("Processing OSM primitive group.");
+			processNodes(primitiveGroup.getDense(), fieldDecoder);
+			processNodes(primitiveGroup.getNodesList(), fieldDecoder);
+			processWays(primitiveGroup.getWaysList(), fieldDecoder);
+			processRelations(primitiveGroup.getRelationsList(), fieldDecoder);
+		}
+	}
+
+
+	private void runAndTrapExceptions() {
+		try {
+			decodedEntities = new ArrayList<EntityContainer>();
+
+			if ("OSMHeader".equals(blobType)) {
+				processOsmHeader(readBlobContent());
+
+			} else if ("OSMData".equals(blobType)) {
+				processOsmPrimitives(readBlobContent());
+
+			} else {
+				if (log.isLoggable(Level.FINER)) {
+					log.finer("Skipping unrecognised blob type " + blobType);
+				}
+			}
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to process PBF blob", e);
+		}
+	}
+
+
+	@Override
+	public void run() {
+		try {
+			runAndTrapExceptions();
+
+			listener.complete(decodedEntities);
+
+		} catch (RuntimeException e) {
+			listener.error();
+		}
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoderListener.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoderListener.java
new file mode 100644
index 0000000..c11740a
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobDecoderListener.java
@@ -0,0 +1,28 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+
+
+/**
+ * Instances of this interface are used to receive results from PBFBlobDecoder.
+ * 
+ * @author Brett Henderson
+ */
+public interface PbfBlobDecoderListener {
+	/**
+	 * Provides the listener with the list of decoded entities.
+	 * 
+	 * @param decodedEntities
+	 *            The decoded entities.
+	 */
+	void complete(List<EntityContainer> decodedEntities);
+
+
+	/**
+	 * Notifies the listener that an error occurred during processing.
+	 */
+	void error();
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobResult.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobResult.java
new file mode 100644
index 0000000..4b3892f
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfBlobResult.java
@@ -0,0 +1,80 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+
+
+/**
+ * Stores the results for a decoded Blob.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfBlobResult {
+	private List<EntityContainer> entities;
+	private boolean complete;
+	private boolean success;
+
+
+	/**
+	 * Creates a new instance.
+	 */
+	public PbfBlobResult() {
+		complete = false;
+		success = false;
+	}
+
+
+	/**
+	 * Stores the results of a successful blob decoding operation.
+	 * 
+	 * @param decodedEntities
+	 *            The entities from the blob.
+	 */
+	public void storeSuccessResult(List<EntityContainer> decodedEntities) {
+		entities = decodedEntities;
+		complete = true;
+		success = true;
+	}
+
+
+	/**
+	 * Stores a failure result for a blob decoding operation.
+	 */
+	public void storeFailureResult() {
+		complete = true;
+		success = false;
+	}
+
+
+	/**
+	 * Gets the complete flag.
+	 * 
+	 * @return True if complete.
+	 */
+	public boolean isComplete() {
+		return complete;
+	}
+
+
+	/**
+	 * Gets the success flag. This is only valid after complete becomes true.
+	 * 
+	 * @return True if successful.
+	 */
+	public boolean isSuccess() {
+		return success;
+	}
+
+
+	/**
+	 * Gets the entities decoded from the blob. This is only valid after
+	 * complete becomes true, and if success is true.
+	 * 
+	 * @return The list of decoded entities.
+	 */
+	public List<EntityContainer> getEntities() {
+		return entities;
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfDecoder.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfDecoder.java
new file mode 100644
index 0000000..cfc6c4e
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfDecoder.java
@@ -0,0 +1,181 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * Decodes all blocks from a PBF stream using worker threads, and passes the
+ * results to the downstream sink.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfDecoder implements Runnable {
+	private PbfStreamSplitter streamSplitter;
+	private ExecutorService executorService;
+	private int maxPendingBlobs;
+	private Sink sink;
+	private Lock lock;
+	private Condition dataWaitCondition;
+	private Queue<PbfBlobResult> blobResults;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param streamSplitter
+	 *            The PBF stream splitter providing the source of blobs to be
+	 *            decoded.
+	 * @param executorService
+	 *            The executor service managing the thread pool.
+	 * @param maxPendingBlobs
+	 *            The maximum number of blobs to have in progress at any point
+	 *            in time.
+	 * @param sink
+	 *            The sink to send all decoded entities to.
+	 */
+	public PbfDecoder(PbfStreamSplitter streamSplitter, ExecutorService executorService, int maxPendingBlobs,
+			Sink sink) {
+		this.streamSplitter = streamSplitter;
+		this.executorService = executorService;
+		this.maxPendingBlobs = maxPendingBlobs;
+		this.sink = sink;
+
+		// Create the thread synchronisation primitives.
+		lock = new ReentrantLock();
+		dataWaitCondition = lock.newCondition();
+
+		// Create the queue of blobs being decoded.
+		blobResults = new LinkedList<PbfBlobResult>();
+	}
+
+
+	/**
+	 * Any thread can call this method when they wish to wait until an update
+	 * has been performed by another thread.
+	 */
+	private void waitForUpdate() {
+		try {
+			dataWaitCondition.await();
+
+		} catch (InterruptedException e) {
+			throw new OsmosisRuntimeException("Thread was interrupted.", e);
+		}
+	}
+
+
+	/**
+	 * Any thread can call this method when they wish to signal another thread
+	 * that an update has occurred.
+	 */
+	private void signalUpdate() {
+		dataWaitCondition.signal();
+	}
+
+
+	private void sendResultsToSink(int targetQueueSize) {
+		while (blobResults.size() > targetQueueSize) {
+			// Get the next result from the queue and wait for it to complete.
+			PbfBlobResult blobResult = blobResults.remove();
+			while (!blobResult.isComplete()) {
+				// The thread hasn't finished processing yet so wait for an
+				// update from another thread before checking again.
+				waitForUpdate();
+			}
+
+			if (!blobResult.isSuccess()) {
+				throw new OsmosisRuntimeException("A PBF decoding worker thread failed, aborting.");
+			}
+
+			// Send the processed entities to the sink. We can release the lock
+			// for the duration of processing to allow worker threads to post
+			// their results.
+			lock.unlock();
+			try {
+				for (EntityContainer entity : blobResult.getEntities()) {
+					sink.process(entity);
+				}
+			} finally {
+				lock.lock();
+			}
+		}
+	}
+
+
+	private void processBlobs() {
+		// Process until the PBF stream is exhausted.
+		while (streamSplitter.hasNext()) {
+			// Obtain the next raw blob from the PBF stream.
+			PbfRawBlob rawBlob = streamSplitter.next();
+
+			// Create the result object to capture the results of the decoded
+			// blob and add it to the blob results queue.
+			final PbfBlobResult blobResult = new PbfBlobResult();
+			blobResults.add(blobResult);
+
+			// Create the listener object that will update the blob results
+			// based on an event fired by the blob decoder.
+			PbfBlobDecoderListener decoderListener = new PbfBlobDecoderListener() {
+
+				@Override
+				public void error() {
+					lock.lock();
+					try {
+						blobResult.storeFailureResult();
+						signalUpdate();
+
+					} finally {
+						lock.unlock();
+					}
+				}
+
+
+				@Override
+				public void complete(List<EntityContainer> decodedEntities) {
+					lock.lock();
+					try {
+						blobResult.storeSuccessResult(decodedEntities);
+						signalUpdate();
+
+					} finally {
+						lock.unlock();
+					}
+				}
+			};
+
+			// Create the blob decoder itself and execute it on a worker thread.
+			PbfBlobDecoder blobDecoder = new PbfBlobDecoder(rawBlob.getType(), rawBlob.getData(), decoderListener);
+			executorService.execute(blobDecoder);
+
+			// If the number of pending blobs has reached capacity we must begin
+			// sending results to the sink. This method will block until blob
+			// decoding is complete.
+			sendResultsToSink(maxPendingBlobs - 1);
+		}
+
+		// There are no more entities available in the PBF stream, so send all remaining data to the sink.
+		sendResultsToSink(0);
+	}
+
+
+	@Override
+	public void run() {
+		lock.lock();
+		try {
+			processBlobs();
+
+		} finally {
+			lock.unlock();
+		}
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfFieldDecoder.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfFieldDecoder.java
new file mode 100644
index 0000000..2b1b4d5
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfFieldDecoder.java
@@ -0,0 +1,93 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.util.Date;
+
+import org.openstreetmap.osmosis.osmbinary.Osmformat.PrimitiveBlock;
+import org.openstreetmap.osmosis.osmbinary.Osmformat.StringTable;
+
+
+
+/**
+ * Manages decoding of the lower level PBF data structures.
+ * 
+ * @author Brett Henderson
+ * 
+ */
+public class PbfFieldDecoder {
+	private static final double COORDINATE_SCALING_FACTOR = 0.000000001;
+
+	private String[] strings;
+	private int coordGranularity;
+	private long coordLatitudeOffset;
+	private long coordLongitudeOffset;
+	private int dateGranularity;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param primitiveBlock
+	 *            The primitive block containing the fields to be decoded.
+	 */
+	public PbfFieldDecoder(PrimitiveBlock primitiveBlock) {
+		this.coordGranularity = primitiveBlock.getGranularity();
+		this.coordLatitudeOffset = primitiveBlock.getLatOffset();
+		this.coordLongitudeOffset = primitiveBlock.getLonOffset();
+		this.dateGranularity = primitiveBlock.getDateGranularity();
+
+		StringTable stringTable = primitiveBlock.getStringtable();
+		strings = new String[stringTable.getSCount()];
+		for (int i = 0; i < strings.length; i++) {
+			strings[i] = stringTable.getS(i).toStringUtf8();
+		}
+	}
+
+
+	/**
+	 * Decodes a raw latitude value into degrees.
+	 * 
+	 * @param rawLatitude
+	 *            The PBF encoded value.
+	 * @return The latitude in degrees.
+	 */
+	public double decodeLatitude(long rawLatitude) {
+		return COORDINATE_SCALING_FACTOR * (coordLatitudeOffset + (coordGranularity * rawLatitude));
+	}
+
+
+	/**
+	 * Decodes a raw longitude value into degrees.
+	 * 
+	 * @param rawLongitude
+	 *            The PBF encoded value.
+	 * @return The longitude in degrees.
+	 */
+	public double decodeLongitude(long rawLongitude) {
+		return COORDINATE_SCALING_FACTOR * (coordLongitudeOffset + (coordGranularity * rawLongitude));
+	}
+
+
+	/**
+	 * Decodes a raw timestamp value into a Date.
+	 * 
+	 * @param rawTimestamp
+	 *            The PBF encoded timestamp.
+	 * @return The timestamp as a Date.
+	 */
+	public Date decodeTimestamp(long rawTimestamp) {
+		return new Date(dateGranularity * rawTimestamp);
+	}
+
+
+	/**
+	 * Decodes a raw string into a String.
+	 * 
+	 * @param rawString
+	 *            The PBF encoding string.
+	 * @return The string as a String.
+	 */
+	public String decodeString(int rawString) {
+		return strings[rawString];
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfRawBlob.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfRawBlob.java
new file mode 100644
index 0000000..f0be473
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfRawBlob.java
@@ -0,0 +1,49 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+/**
+ * Represents a single piece of raw blob data extracted from the PBF stream. It
+ * has not yet been decoded into a PBF blob object.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfRawBlob {
+	private String type;
+	private byte[] data;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param type
+	 *            The type of data represented by this blob. This corresponds to
+	 *            the type field in the blob header.
+	 * @param data
+	 *            The raw contents of the blob in binary undecoded form.
+	 */
+	public PbfRawBlob(String type, byte[] data) {
+		this.type = type;
+		this.data = data;
+	}
+
+
+	/**
+	 * Gets the type of data represented by this blob. This corresponds to the
+	 * type field in the blob header.
+	 * 
+	 * @return The blob type.
+	 */
+	public String getType() {
+		return type;
+	}
+
+
+	/**
+	 * Gets the raw contents of the blob in binary undecoded form.
+	 * 
+	 * @return The raw blob data.
+	 */
+	public byte[] getData() {
+		return data;
+	}
+}
diff --git a/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfStreamSplitter.java b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfStreamSplitter.java
new file mode 100644
index 0000000..67a14ee
--- /dev/null
+++ b/osmosis-pbf2/src/main/java/org/openstreetmap/osmosis/pbf2/v0_6/impl/PbfStreamSplitter.java
@@ -0,0 +1,132 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6.impl;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.lifecycle.Releasable;
+import org.openstreetmap.osmosis.osmbinary.Fileformat;
+import org.openstreetmap.osmosis.osmbinary.Fileformat.BlobHeader;
+
+
+/**
+ * Parses a PBF data stream and extracts the raw data of each blob in sequence
+ * until the end of the stream is reached.
+ * 
+ * @author Brett Henderson
+ */
+public class PbfStreamSplitter implements Iterator<PbfRawBlob>, Releasable {
+
+	private static Logger log = Logger.getLogger(PbfStreamSplitter.class.getName());
+
+	private DataInputStream dis;
+	private int dataBlockCount;
+	private boolean eof;
+	private PbfRawBlob nextBlob;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param pbfStream
+	 *            The PBF data stream to be parsed.
+	 */
+	public PbfStreamSplitter(DataInputStream pbfStream) {
+		dis = pbfStream;
+		dataBlockCount = 0;
+		eof = false;
+	}
+
+
+	private BlobHeader readHeader(int headerLength) throws IOException {
+		byte[] headerBuffer = new byte[headerLength];
+		dis.readFully(headerBuffer);
+
+		BlobHeader blobHeader = Fileformat.BlobHeader.parseFrom(headerBuffer);
+
+		return blobHeader;
+	}
+
+
+	private byte[] readRawBlob(BlobHeader blobHeader) throws IOException {
+		byte[] rawBlob = new byte[blobHeader.getDatasize()];
+
+		dis.readFully(rawBlob);
+
+		return rawBlob;
+	}
+
+
+	private void getNextBlob() {
+		try {
+			// Read the length of the next header block. This is the only time
+			// we should expect to encounter an EOF exception. In all other
+			// cases it indicates a corrupt or truncated file.
+			int headerLength;
+			try {
+				headerLength = dis.readInt();
+			} catch (EOFException e) {
+				eof = true;
+				return;
+			}
+
+			if (log.isLoggable(Level.FINER)) {
+				log.finer("Reading header for blob " + dataBlockCount++);
+			}
+			BlobHeader blobHeader = readHeader(headerLength);
+
+			if (log.isLoggable(Level.FINER)) {
+				log.finer("Processing blob of type " + blobHeader.getType() + ".");
+			}
+			byte[] blobData = readRawBlob(blobHeader);
+
+			nextBlob = new PbfRawBlob(blobHeader.getType(), blobData);
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to get next blob from PBF stream.", e);
+		}
+	}
+
+
+	@Override
+	public boolean hasNext() {
+		if (nextBlob == null && !eof) {
+			getNextBlob();
+		}
+
+		return nextBlob != null;
+	}
+
+
+	@Override
+	public PbfRawBlob next() {
+		PbfRawBlob result = nextBlob;
+		nextBlob = null;
+
+		return result;
+	}
+
+
+	@Override
+	public void remove() {
+		throw new UnsupportedOperationException();
+	}
+
+
+	@Override
+	public void release() {
+		if (dis != null) {
+			try {
+				dis.close();
+			} catch (IOException e) {
+				log.log(Level.SEVERE, "Unable to close PBF stream.", e);
+			}
+		}
+		dis = null;
+	}
+}
diff --git a/osmosis-pbf2/src/main/resources/osmosis-plugins.conf b/osmosis-pbf2/src/main/resources/osmosis-plugins.conf
new file mode 100644
index 0000000..f6f7697
--- /dev/null
+++ b/osmosis-pbf2/src/main/resources/osmosis-plugins.conf
@@ -0,0 +1 @@
+org.openstreetmap.osmosis.pbf2.PbfPluginLoader
\ No newline at end of file
diff --git a/osmosis-pbf2/src/test/java/org/openstreetmap/osmosis/pbf2/v0_6/OsmosisReaderTest.java b/osmosis-pbf2/src/test/java/org/openstreetmap/osmosis/pbf2/v0_6/OsmosisReaderTest.java
new file mode 100644
index 0000000..38a277f
--- /dev/null
+++ b/osmosis-pbf2/src/test/java/org/openstreetmap/osmosis/pbf2/v0_6/OsmosisReaderTest.java
@@ -0,0 +1,52 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pbf2.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the OsmosisReader and OsmosisSerializer classes.
+ * 
+ * @author Brett Henderson
+ */
+public class OsmosisReaderTest extends AbstractDataTest {
+	/**
+	 * Tests writing to and reading from PBF files.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testWriteAndRead() throws IOException {
+		// Generate data files.
+		File inputXmlFile = dataUtils.createDataFile("v0_6/data-snapshot.osm");
+		File pbfFile = dataUtils.newFile();
+		File outputXmlFile = dataUtils.newFile();
+
+        // Read the XML and write to PBF using the standard PBF writer.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-xml-0.6",
+        		inputXmlFile.getPath(),
+        		"--write-pbf-0.6",
+        		pbfFile.getPath()
+                });
+        
+        // Read the PBF using the PBF2 reader and write to XML.
+        Osmosis.run(new String[] {
+        		"-q",
+        		"--read-pbf-fast-0.6",
+        		pbfFile.getPath(),
+        		"--write-xml-0.6",
+        		outputXmlFile.getPath()
+                });
+
+        // Validate that the output file matches the input file.
+        dataUtils.compareFiles(inputXmlFile, outputXmlFile);
+	}
+}
diff --git a/osmosis-pbf2/src/test/resources/data/template/v0_6/data-snapshot.osm b/osmosis-pbf2/src/test/resources/data/template/v0_6/data-snapshot.osm
new file mode 100644
index 0000000..fd0631c
--- /dev/null
+++ b/osmosis-pbf2/src/test/resources/data/template/v0_6/data-snapshot.osm
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-pgsimple/.checkstyle b/osmosis-pgsimple/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-pgsimple/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-pgsimple/.gitignore b/osmosis-pgsimple/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-pgsimple/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-pgsimple/build.gradle b/osmosis-pgsimple/build.gradle
new file mode 100644
index 0000000..bed83bb
--- /dev/null
+++ b/osmosis-pgsimple/build.gradle
@@ -0,0 +1,16 @@
+configurations {
+    // Exclude unnecessary postgis stub classes.
+    all*.exclude group: 'org.postgis', module: 'postgis-stubs'
+}
+
+dependencies {
+    compile project(':osmosis-core')
+    compile group: 'org.postgis', name: 'postgis-jdbc', version: dependencyVersionPostGis
+    compile group: 'postgresql', name: 'postgresql', version: dependencyVersionPostgreSql
+    testCompile project(':osmosis-dataset')
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
+
+// Disable tests until continuous integration has a database configured.
+test.enabled = false
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/PgSimplePluginLoader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/PgSimplePluginLoader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/PgSimplePluginLoader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/PgSimplePluginLoader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseDao.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseTableReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseTableReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseTableReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/BaseTableReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocation.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocation.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocation.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocation.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocationStore.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocationStore.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocationStore.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CompactPersistentNodeLocationStore.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CopyFileWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CopyFileWriter.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CopyFileWriter.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/CopyFileWriter.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/DatabaseContext.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/DatabaseContext.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/DatabaseContext.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/DatabaseContext.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/InMemoryNodeLocationStore.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/InMemoryNodeLocationStore.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/InMemoryNodeLocationStore.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/InMemoryNodeLocationStore.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NoSuchRecordException.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NoSuchRecordException.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NoSuchRecordException.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NoSuchRecordException.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocation.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocation.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocation.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocation.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStore.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStore.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStore.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStore.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStoreType.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStoreType.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStoreType.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/NodeLocationStoreType.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PersistentNodeLocationStore.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PersistentNodeLocationStore.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PersistentNodeLocationStore.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PersistentNodeLocationStore.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PointBuilder.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PointBuilder.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PointBuilder.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PointBuilder.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PolygonBuilder.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PolygonBuilder.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PolygonBuilder.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/PolygonBuilder.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/SchemaVersionValidator.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/SchemaVersionValidator.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/SchemaVersionValidator.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/common/SchemaVersionValidator.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java
new file mode 100644
index 0000000..47c61a0
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java
@@ -0,0 +1,101 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsimple.common.SchemaVersionValidator;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ActionChangeWriter;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ChangeWriter;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * A change sink writing to database tables. This aims to be suitable for
+ * running at regular intervals with database overhead proportional to changeset
+ * size.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlChangeWriter implements ChangeSink {
+	
+	private ChangeWriter changeWriter;
+	private Map<ChangeAction, ActionChangeWriter> actionWriterMap;
+	private DatabaseContext dbCtx;
+	private SchemaVersionValidator schemaVersionValidator;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 */
+	public PostgreSqlChangeWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
+		dbCtx = new DatabaseContext(loginCredentials);
+		changeWriter = new ChangeWriter(dbCtx);
+		actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
+		actionWriterMap.put(ChangeAction.Create, new ActionChangeWriter(changeWriter, ChangeAction.Create));
+		actionWriterMap.put(ChangeAction.Modify, new ActionChangeWriter(changeWriter, ChangeAction.Modify));
+		actionWriterMap.put(ChangeAction.Delete, new ActionChangeWriter(changeWriter, ChangeAction.Delete));
+		
+		schemaVersionValidator = new SchemaVersionValidator(dbCtx, preferences);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer change) {
+		ChangeAction action;
+		
+		// Verify that the schema version is supported.
+		schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
+		
+		action = change.getAction();
+		
+		if (!actionWriterMap.containsKey(action)) {
+			throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
+		}
+		
+		// Process the entity using the action writer appropriate for the change
+		// action.
+		change.getEntityContainer().process(actionWriterMap.get(action));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		changeWriter.complete();
+		
+		dbCtx.commit();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeWriter.release();
+		
+		dbCtx.release();
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriterFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriterFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriterFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriterFactory.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java
new file mode 100644
index 0000000..48d2649
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java
@@ -0,0 +1,133 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6;
+
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetLoader;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.DatabaseCapabilityChecker;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetBuilder;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.TempCopyFileset;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink for storing all data to a database using the COPY command.
+ * This task is intended for writing to an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlCopyWriter implements Sink {
+	
+	private static final Logger LOG = Logger.getLogger(PostgreSqlCopyWriter.class.getName());
+	
+	private CopyFilesetBuilder copyFilesetBuilder;
+	private CopyFilesetLoader copyFilesetLoader;
+	private TempCopyFileset copyFileset;
+	private DatabaseLoginCredentials loginCredentials;
+	private DatabasePreferences preferences;
+	private NodeLocationStoreType storeType;
+	private boolean populateBbox;
+	private boolean populateLinestring;
+	private boolean initialized;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 */
+	public PostgreSqlCopyWriter(
+			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+			NodeLocationStoreType storeType) {
+		this.loginCredentials = loginCredentials;
+		this.preferences = preferences;
+		this.storeType = storeType;
+		
+		copyFileset = new TempCopyFileset();
+	}
+	
+	
+	private void initialize() {
+		if (!initialized) {
+			DatabaseContext dbCtx;
+			DatabaseCapabilityChecker capabilityChecker;
+			
+			LOG.fine("Initializing the database and temporary processing files.");
+			
+			dbCtx = new DatabaseContext(loginCredentials);
+			
+			try {
+				capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+
+				populateBbox = capabilityChecker.isWayBboxSupported();
+				populateLinestring = capabilityChecker.isWayLinestringSupported();				
+
+				copyFilesetBuilder =
+					new CopyFilesetBuilder(copyFileset, populateBbox, populateLinestring, storeType);
+				
+				copyFilesetLoader = new CopyFilesetLoader(loginCredentials, preferences, copyFileset);
+				
+				LOG.fine("Processing input data, building geometries and creating database load files.");
+				
+			} finally {
+				dbCtx.release();
+			}
+			
+			initialized = true;
+		}
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		initialize();
+		
+		copyFilesetBuilder.process(entityContainer);
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the files, then loads the files into the database. 
+	 */
+	public void complete() {
+		initialize();
+		
+		LOG.fine("All data has been received, beginning database load.");
+		copyFilesetBuilder.complete();
+		copyFilesetLoader.run();
+		
+		LOG.fine("Processing complete.");
+	}
+	
+	
+	/**
+	 * Releases all database resources.
+	 */
+	public void release() {
+		copyFilesetBuilder.release();
+		copyFileset.release();
+		
+		initialized = false;
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriterFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriterFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriterFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriterFactory.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReaderFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReaderFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReaderFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDatasetReaderFactory.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java
new file mode 100644
index 0000000..2b1bd61
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java
@@ -0,0 +1,85 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6;
+
+import java.io.File;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.DirectoryCopyFileset;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetBuilder;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink for storing all data to database dump files. This task is
+ * intended for populating an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlDumpWriter implements Sink {
+	
+	private CopyFilesetBuilder copyFilesetBuilder;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param filePrefix
+	 *            The prefix to prepend to all generated file names.
+	 * @param enableBboxBuilder
+	 *            If true, the way bbox geometry is built during processing
+	 *            instead of relying on the database to build them after import.
+	 *            This increases processing but is faster than relying on the
+	 *            database.
+	 * @param enableLinestringBuilder
+	 *            If true, the way linestring geometry is built during
+	 *            processing instead of relying on the database to build them
+	 *            after import. This increases processing but is faster than
+	 *            relying on the database.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 */
+	public PostgreSqlDumpWriter(
+			File filePrefix, boolean enableBboxBuilder,
+			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
+		DirectoryCopyFileset copyFileset;
+		
+		copyFileset = new DirectoryCopyFileset(filePrefix);
+		
+		copyFilesetBuilder =
+			new CopyFilesetBuilder(copyFileset, enableBboxBuilder, enableLinestringBuilder, storeType);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		copyFilesetBuilder.process(entityContainer);
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the database and commits. 
+	 */
+	public void complete() {
+		copyFilesetBuilder.complete();
+	}
+	
+	
+	/**
+	 * Releases all database resources.
+	 */
+	public void release() {
+		copyFilesetBuilder.release();
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriterFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriterFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriterFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriterFactory.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncator.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncator.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncator.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncator.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncatorFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncatorFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncatorFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTruncatorFactory.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlVersionConstants.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlVersionConstants.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlVersionConstants.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlVersionConstants.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java
new file mode 100644
index 0000000..fa5a082
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java
@@ -0,0 +1,838 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.database.DbFeature;
+import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
+import org.openstreetmap.osmosis.core.database.ReleasableStatementContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsimple.common.SchemaVersionValidator;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ActionDao;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.IndexManager;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.NodeMapper;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.RelationMapper;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.RelationMemberMapper;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.TagMapper;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.UserDao;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayGeometryBuilder;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayMapper;
+import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayNodeMapper;
+import org.postgis.Geometry;
+
+
+/**
+ * An OSM data sink for storing all data to a database. This task is intended
+ * for writing to an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlWriter implements Sink, EntityProcessor {
+	
+	private static final Logger LOG = Logger.getLogger(PostgreSqlWriter.class.getName());
+	
+	// These constants define how many rows of each data type will be inserted
+	// with single insert statements.
+	private static final int INSERT_BULK_ROW_COUNT_NODE = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_NODE_TAG = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_WAY = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_WAY_TAG = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_WAY_NODE = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_RELATION = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_RELATION_TAG = 1000;
+	private static final int INSERT_BULK_ROW_COUNT_RELATION_MEMBER = 1000;
+	
+	
+	private DatabaseContext dbCtx;
+	private boolean enableBboxBuilder;
+	private boolean enableLinestringBuilder;
+	private SchemaVersionValidator schemaVersionValidator;
+	private IndexManager indexManager;
+	private List<Node> nodeBuffer;
+	private List<DbFeature<Tag>> nodeTagBuffer;
+	private List<Way> wayBuffer;
+	private List<DbFeature<Tag>> wayTagBuffer;
+	private List<DbOrderedFeature<WayNode>> wayNodeBuffer;
+	private List<Relation> relationBuffer;
+	private List<DbFeature<Tag>> relationTagBuffer;
+	private List<DbOrderedFeature<RelationMember>> relationMemberBuffer;
+	private boolean initialized;
+	private HashSet<Integer> userSet;
+	private ActionDao actionDao;
+	private UserDao userDao;
+	private ReleasableStatementContainer statementContainer;
+	private PreparedStatement singleNodeStatement;
+	private PreparedStatement bulkNodeStatement;
+	private PreparedStatement singleNodeTagStatement;
+	private PreparedStatement bulkNodeTagStatement;
+	private PreparedStatement singleWayStatement;
+	private PreparedStatement bulkWayStatement;
+	private PreparedStatement singleWayTagStatement;
+	private PreparedStatement bulkWayTagStatement;
+	private PreparedStatement singleWayNodeStatement;
+	private PreparedStatement bulkWayNodeStatement;
+	private PreparedStatement singleRelationStatement;
+	private PreparedStatement bulkRelationStatement;
+	private PreparedStatement singleRelationTagStatement;
+	private PreparedStatement bulkRelationTagStatement;
+	private PreparedStatement singleRelationMemberStatement;
+	private PreparedStatement bulkRelationMemberStatement;
+	private NodeMapper nodeBuilder;
+	private WayMapper wayBuilder;
+	private RelationMapper relationBuilder;
+	private TagMapper nodeTagBuilder;
+	private TagMapper wayTagBuilder;
+	private TagMapper relationTagBuilder;
+	private WayNodeMapper wayNodeBuilder;
+	private RelationMemberMapper relationMemberBuilder;
+	private WayGeometryBuilder wayGeometryBuilder;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param enableBboxBuilder
+	 *            If true, the way bbox geometry is built during processing
+	 *            instead of relying on the database to build them after import.
+	 *            This increases processing but is faster than relying on the
+	 *            database.
+	 * @param enableLinestringBuilder
+	 *            If true, the way linestring geometry is built during
+	 *            processing instead of relying on the database to build them
+	 *            after import. This increases processing but is faster than
+	 *            relying on the database.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 */
+	public PostgreSqlWriter(
+			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+			boolean enableBboxBuilder, boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
+		dbCtx = new DatabaseContext(loginCredentials);
+		
+		this.enableBboxBuilder = enableBboxBuilder;
+		this.enableLinestringBuilder = enableLinestringBuilder;
+		
+		schemaVersionValidator = new SchemaVersionValidator(dbCtx, preferences);
+		indexManager = new IndexManager(dbCtx, !enableBboxBuilder, !enableLinestringBuilder);
+		
+		nodeBuffer = new ArrayList<Node>();
+		nodeTagBuffer = new ArrayList<DbFeature<Tag>>();
+		wayBuffer = new ArrayList<Way>();
+		wayTagBuffer = new ArrayList<DbFeature<Tag>>();
+		wayNodeBuffer = new ArrayList<DbOrderedFeature<WayNode>>();
+		relationBuffer = new ArrayList<Relation>();
+		relationTagBuffer = new ArrayList<DbFeature<Tag>>();
+		relationMemberBuffer = new ArrayList<DbOrderedFeature<RelationMember>>();
+		
+		// Create an action dao but disable it so that no records will be written.
+		actionDao = new ActionDao(dbCtx, false);
+		
+		userSet = new HashSet<Integer>();
+		userDao = new UserDao(dbCtx, actionDao);
+		
+		nodeBuilder = new NodeMapper();
+		wayBuilder = new WayMapper(enableBboxBuilder, enableLinestringBuilder);
+		relationBuilder = new RelationMapper();
+		nodeTagBuilder = new TagMapper(nodeBuilder.getEntityName());
+		wayTagBuilder = new TagMapper(wayBuilder.getEntityName());
+		relationTagBuilder = new TagMapper(relationBuilder.getEntityName());
+		wayNodeBuilder = new WayNodeMapper();
+		relationMemberBuilder = new RelationMemberMapper();
+		wayGeometryBuilder = new WayGeometryBuilder(storeType);
+		
+		statementContainer = new ReleasableStatementContainer();
+		
+		initialized = false;
+	}
+	
+	
+	/**
+	 * Initialises prepared statements and obtains database locks. Can be called
+	 * multiple times.
+	 */
+	private void initialize() {
+		if (!initialized) {
+			schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
+			
+			bulkNodeStatement = statementContainer.add(
+					dbCtx.prepareStatement(nodeBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_NODE)));
+			singleNodeStatement = statementContainer.add(
+					dbCtx.prepareStatement(nodeBuilder.getSqlInsert(1)));
+			bulkNodeTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(nodeTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_NODE_TAG)));
+			singleNodeTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(nodeTagBuilder.getSqlInsert(1)));
+			bulkWayStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY)));
+			singleWayStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayBuilder.getSqlInsert(1)));
+			bulkWayTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY_TAG)));
+			singleWayTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayTagBuilder.getSqlInsert(1)));
+			bulkWayNodeStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayNodeBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY_NODE)));
+			singleWayNodeStatement = statementContainer.add(
+					dbCtx.prepareStatement(wayNodeBuilder.getSqlInsert(1)));
+			bulkRelationStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION)));
+			singleRelationStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationBuilder.getSqlInsert(1)));
+			bulkRelationTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION_TAG)));
+			singleRelationTagStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationTagBuilder.getSqlInsert(1)));
+			bulkRelationMemberStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationMemberBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION_MEMBER)));
+			singleRelationMemberStatement = statementContainer.add(
+					dbCtx.prepareStatement(relationMemberBuilder.getSqlInsert(1)));
+			
+			// Drop all constraints and indexes.
+			indexManager.prepareForLoad();
+			
+			LOG.fine("Loading data.");
+			
+			initialized = true;
+		}
+	}
+	
+	
+	/**
+	 * Writes the specified user to the database if it hasn't already been.
+	 * 
+	 * @param user
+	 *            The user to add.
+	 */
+	private void writeUser(OsmUser user) {
+		// Write the user to the database if it hasn't already been.
+		if (!user.equals(OsmUser.NONE)) {
+			if (!userSet.contains(user.getId())) {
+				userDao.addUser(user);
+				
+				userSet.add(user.getId());
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes nodes to the database. If complete is false, this will only write
+	 * nodes until the remaining node count is less than the multi-row insert
+	 * statement row count. If complete is true, all remaining rows will be
+	 * written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushNodes(boolean complete) {
+		while (nodeBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE) {
+			List<Node> processedNodes;
+			int prmIndex;
+			
+			processedNodes = new ArrayList<Node>(INSERT_BULK_ROW_COUNT_NODE);
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE; i++) {
+				Node node;
+				
+				node = nodeBuffer.remove(0);
+				processedNodes.add(node);
+				
+				prmIndex = nodeBuilder.populateEntityParameters(bulkNodeStatement, prmIndex, node);
+			}
+			
+			try {
+				bulkNodeStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert nodes into the database.", e);
+			}
+			
+			for (Node node : processedNodes) {
+				addNodeTags(node);
+			}
+		}
+		
+		if (complete) {
+			while (nodeBuffer.size() > 0) {
+				Node node;
+				
+				node = nodeBuffer.remove(0);
+				
+				nodeBuilder.populateEntityParameters(singleNodeStatement, 1, node);
+				
+				try {
+					singleNodeStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a node into the database.", e);
+				}
+				
+				addNodeTags(node);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Process the node tags.
+	 * 
+	 * @param node
+	 *            The node to be processed.
+	 */
+	private void addNodeTags(Node node) {
+		for (Tag tag : node.getTags()) {
+			nodeTagBuffer.add(new DbFeature<Tag>(node.getId(), tag));
+		}
+		
+		flushNodeTags(false);
+	}
+	
+	
+	/**
+	 * Flushes node tags to the database. If complete is false, this will only
+	 * write node tags until the remaining node tag count is less than the
+	 * multi-row insert statement row count. If complete is true, all remaining
+	 * rows will be written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushNodeTags(boolean complete) {
+		while (nodeTagBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE_TAG) {
+			int prmIndex;
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE_TAG; i++) {
+				prmIndex = nodeTagBuilder.populateEntityParameters(
+						bulkNodeTagStatement, prmIndex, nodeTagBuffer.remove(0));
+			}
+			
+			try {
+				bulkNodeTagStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert node tags into the database.", e);
+			}
+		}
+		
+		if (complete) {
+			while (nodeTagBuffer.size() > 0) {
+				nodeTagBuilder.populateEntityParameters(singleNodeTagStatement, 1, nodeTagBuffer.remove(0));
+				
+				try {
+					singleNodeTagStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a node tag into the database.", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes ways to the database. If complete is false, this will only write
+	 * ways until the remaining way count is less than the multi-row insert
+	 * statement row count. If complete is true, all remaining rows will be
+	 * written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushWays(boolean complete) {
+		while (wayBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY) {
+			List<Way> processedWays;
+			int prmIndex;
+			
+			processedWays = new ArrayList<Way>(INSERT_BULK_ROW_COUNT_WAY);
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY; i++) {
+				Way way;
+				List<Geometry> geometries;
+				
+				way = wayBuffer.remove(0);
+				processedWays.add(way);
+				
+				geometries = new ArrayList<Geometry>();
+				if (enableBboxBuilder) {
+					geometries.add(wayGeometryBuilder.createWayBbox(way));
+				}
+				if (enableLinestringBuilder) {
+					geometries.add(wayGeometryBuilder.createWayLinestring(way));
+				}
+				prmIndex = wayBuilder.populateEntityParameters(bulkWayStatement, prmIndex, way, geometries);
+			}
+			
+			try {
+				bulkWayStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert ways into the database.", e);
+			}
+			
+			for (Way way : processedWays) {
+				addWayTags(way);
+				addWayNodes(way);
+			}
+		}
+		
+		if (complete) {
+			while (wayBuffer.size() > 0) {
+				Way way;
+				List<Geometry> geometries;
+				
+				way = wayBuffer.remove(0);
+				
+				geometries = new ArrayList<Geometry>();
+				if (enableBboxBuilder) {
+					geometries.add(wayGeometryBuilder.createWayBbox(way));
+				}
+				if (enableLinestringBuilder) {
+					geometries.add(wayGeometryBuilder.createWayLinestring(way));
+				}
+				wayBuilder.populateEntityParameters(singleWayStatement, 1, way, geometries);
+				
+				try {
+					singleWayStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a way into the database.", e);
+				}
+				
+				addWayTags(way);
+				addWayNodes(way);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Process the way tags.
+	 * 
+	 * @param way
+	 *            The way to be processed.
+	 */
+	private void addWayTags(Way way) {
+		for (Tag tag : way.getTags()) {
+			wayTagBuffer.add(new DbFeature<Tag>(way.getId(), tag));
+		}
+		
+		flushWayTags(false);
+	}
+	
+	
+	/**
+	 * Process the way nodes.
+	 * 
+	 * @param way
+	 *            The way to be processed.
+	 */
+	private void addWayNodes(Way way) {
+		int sequenceId;
+		
+		sequenceId = 0;
+		for (WayNode wayNode : way.getWayNodes()) {
+			wayNodeBuffer.add(new DbOrderedFeature<WayNode>(way.getId(), wayNode, sequenceId++));
+		}
+		
+		flushWayNodes(false);
+	}
+	
+	
+	/**
+	 * Flushes way tags to the database. If complete is false, this will only
+	 * write way tags until the remaining way tag count is less than the
+	 * multi-row insert statement row count. If complete is true, all remaining
+	 * rows will be written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushWayTags(boolean complete) {
+		while (wayTagBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_TAG) {
+			int prmIndex;
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_TAG; i++) {
+				prmIndex = wayTagBuilder.populateEntityParameters(
+						bulkWayTagStatement, prmIndex, wayTagBuffer.remove(0));
+			}
+			
+			try {
+				bulkWayTagStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert way tags into the database.", e);
+			}
+		}
+		
+		if (complete) {
+			while (wayTagBuffer.size() > 0) {
+				wayTagBuilder.populateEntityParameters(singleWayTagStatement, 1, wayTagBuffer.remove(0));
+				
+				try {
+					singleWayTagStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a way tag into the database.", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes way nodes to the database. If complete is false, this will only
+	 * write way nodes until the remaining way node count is less than the
+	 * multi-row insert statement row count. If complete is true, all remaining
+	 * rows will be written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushWayNodes(boolean complete) {
+		while (wayNodeBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_NODE) {
+			int prmIndex;
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_NODE; i++) {
+				prmIndex = wayNodeBuilder.populateEntityParameters(
+						bulkWayNodeStatement, prmIndex, wayNodeBuffer.remove(0));
+			}
+			
+			try {
+				bulkWayNodeStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert way nodes into the database.", e);
+			}
+		}
+		
+		if (complete) {
+			while (wayNodeBuffer.size() > 0) {
+				wayNodeBuilder.populateEntityParameters(singleWayNodeStatement, 1, wayNodeBuffer.remove(0));
+				
+				try {
+					singleWayNodeStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a way node into the database.", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes relations to the database. If complete is false, this will only write
+	 * relations until the remaining relation count is less than the multi-row insert
+	 * statement row count. If complete is true, all remaining rows will be
+	 * written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushRelations(boolean complete) {
+		while (relationBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION) {
+			List<Relation> processedRelations;
+			int prmIndex;
+			
+			processedRelations = new ArrayList<Relation>(INSERT_BULK_ROW_COUNT_RELATION);
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION; i++) {
+				Relation relation;
+				
+				relation = relationBuffer.remove(0);
+				processedRelations.add(relation);
+				
+				prmIndex = relationBuilder.populateEntityParameters(bulkRelationStatement, prmIndex, relation);
+			}
+			
+			try {
+				bulkRelationStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert relations into the database.", e);
+			}
+			
+			for (Relation relation : processedRelations) {
+				addRelationTags(relation);
+				addRelationMembers(relation);
+			}
+		}
+		
+		if (complete) {
+			while (relationBuffer.size() > 0) {
+				Relation relation;
+				
+				relation = relationBuffer.remove(0);
+				
+				relationBuilder.populateEntityParameters(singleRelationStatement, 1, relation);
+				
+				try {
+					singleRelationStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a relation into the database.", e);
+				}
+				
+				addRelationTags(relation);
+				addRelationMembers(relation);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Process the relation tags.
+	 * 
+	 * @param relation
+	 *            The relation to be processed.
+	 */
+	private void addRelationTags(Relation relation) {
+		for (Tag tag : relation.getTags()) {
+			relationTagBuffer.add(new DbFeature<Tag>(relation.getId(), tag));
+		}
+		
+		flushRelationTags(false);
+	}
+	
+	
+	/**
+	 * Process the relation members.
+	 * 
+	 * @param relation
+	 *            The relation to be processed.
+	 */
+	private void addRelationMembers(Relation relation) {
+		int sequenceId;
+		
+		sequenceId = 0;
+		for (RelationMember relationMember : relation.getMembers()) {
+			relationMemberBuffer.add(
+					new DbOrderedFeature<RelationMember>(relation.getId(), relationMember, sequenceId++));
+		}
+		
+		flushRelationMembers(false);
+	}
+	
+	
+	/**
+	 * Flushes relation tags to the database. If complete is false, this will only
+	 * write relation tags until the remaining relation tag count is less than the
+	 * multi-row insert statement row count. If complete is true, all remaining
+	 * rows will be written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushRelationTags(boolean complete) {
+		while (relationTagBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_TAG) {
+			int prmIndex;
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_TAG; i++) {
+				prmIndex = relationTagBuilder.populateEntityParameters(
+						bulkRelationTagStatement, prmIndex, relationTagBuffer.remove(0));
+			}
+			
+			try {
+				bulkRelationTagStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert relation tags into the database.", e);
+			}
+		}
+		
+		if (complete) {
+			while (relationTagBuffer.size() > 0) {
+				relationTagBuilder.populateEntityParameters(singleRelationTagStatement, 1, relationTagBuffer.remove(0));
+				
+				try {
+					singleRelationTagStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a relation tag into the database.", e);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * Flushes relation members to the database. If complete is false, this will only
+	 * write relation members until the remaining relation member count is less than the
+	 * multi-row insert statement row count. If complete is true, all remaining
+	 * rows will be written using single row insert statements.
+	 * 
+	 * @param complete
+	 *            If true, all data will be written to the database. If false,
+	 *            some data may be left until more data is available.
+	 */
+	private void flushRelationMembers(boolean complete) {
+		while (relationMemberBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_MEMBER) {
+			int prmIndex;
+			
+			prmIndex = 1;
+			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_MEMBER; i++) {
+				prmIndex = relationMemberBuilder.populateEntityParameters(
+						bulkRelationMemberStatement, prmIndex, relationMemberBuffer.remove(0));
+			}
+			
+			try {
+				bulkRelationMemberStatement.executeUpdate();
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Unable to bulk insert relation members into the database.", e);
+			}
+		}
+		
+		if (complete) {
+			while (relationMemberBuffer.size() > 0) {
+				relationMemberBuilder.populateEntityParameters(
+						singleRelationMemberStatement, 1, relationMemberBuffer.remove(0));
+				
+				try {
+					singleRelationMemberStatement.executeUpdate();
+				} catch (SQLException e) {
+					throw new OsmosisRuntimeException("Unable to insert a relation member into the database.", e);
+				}
+			}
+		}
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the database and commits. 
+	 */
+	public void complete() {
+		initialize();
+		
+		LOG.fine("Flushing buffers.");
+		
+		flushNodes(true);
+		flushNodeTags(true);
+		flushWays(true);
+		flushWayTags(true);
+		flushWayNodes(true);
+		flushRelations(true);
+		flushRelationTags(true);
+		flushRelationMembers(true);
+		
+		LOG.fine("Data load complete.");
+		
+		// Add all constraints and indexes.
+		indexManager.completeAfterLoad();
+		
+		LOG.fine("Committing changes.");
+		dbCtx.commit();
+		
+		LOG.fine("Vacuuming database.");
+		dbCtx.setAutoCommit(true);
+		dbCtx.executeStatement("VACUUM ANALYZE");
+		
+		LOG.fine("Complete.");
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		initialize();
+		
+		// Write the user to the database if required.
+		writeUser(entityContainer.getEntity().getUser());
+		
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(BoundContainer bound) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer nodeContainer) {
+		if (enableBboxBuilder || enableLinestringBuilder) {
+			wayGeometryBuilder.addNodeLocation(nodeContainer.getEntity());
+		}
+		
+		nodeBuffer.add(nodeContainer.getEntity());
+		
+		flushNodes(false);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		// Ignore ways with a single node because they can't be loaded into postgis.
+		if (wayContainer.getEntity().getWayNodes().size() > 1) {
+			wayBuffer.add(wayContainer.getEntity());
+		}
+		
+		flushWays(false);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		relationBuffer.add(relationContainer.getEntity());
+		
+		flushRelations(false);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		statementContainer.release();
+		wayGeometryBuilder.release();
+		
+		dbCtx.release();
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriterFactory.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriterFactory.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriterFactory.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriterFactory.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionChangeWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionChangeWriter.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionChangeWriter.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionChangeWriter.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDao.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDataType.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDataType.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDataType.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ActionDataType.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangeWriter.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangeWriter.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangeWriter.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangeWriter.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangesetAction.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangesetAction.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangesetAction.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/ChangesetAction.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFileset.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFileset.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFileset.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFileset.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java
new file mode 100644
index 0000000..b099559
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java
@@ -0,0 +1,259 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
+import org.openstreetmap.osmosis.pgsimple.common.CopyFileWriter;
+import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsimple.common.PointBuilder;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink for storing all data to a set of database dump files. These
+ * files can be used for populating an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class CopyFilesetBuilder implements Sink, EntityProcessor {
+	
+	private boolean enableBboxBuilder;
+	private boolean enableLinestringBuilder;
+	private WayGeometryBuilder wayGeometryBuilder;
+	private CompletableContainer writerContainer;
+	private MemberTypeValueMapper memberTypeValueMapper;
+	private CopyFileWriter userWriter;
+	private CopyFileWriter nodeWriter;
+	private CopyFileWriter nodeTagWriter;
+	private CopyFileWriter wayWriter;
+	private CopyFileWriter wayTagWriter;
+	private CopyFileWriter wayNodeWriter;
+	private CopyFileWriter relationWriter;
+	private CopyFileWriter relationTagWriter;
+	private CopyFileWriter relationMemberWriter;
+	private PointBuilder pointBuilder;
+	private Set<Integer> userSet;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param copyFileset
+	 *            The set of COPY files to be populated.
+	 * @param enableBboxBuilder
+	 *            If true, the way bbox geometry is built during processing
+	 *            instead of relying on the database to build them after import.
+	 *            This increases processing but is faster than relying on the
+	 *            database.
+	 * @param enableLinestringBuilder
+	 *            If true, the way linestring geometry is built during
+	 *            processing instead of relying on the database to build them
+	 *            after import. This increases processing but is faster than
+	 *            relying on the database.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 */
+	public CopyFilesetBuilder(
+			CopyFileset copyFileset, boolean enableBboxBuilder,
+			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
+		this.enableBboxBuilder = enableBboxBuilder;
+		this.enableLinestringBuilder = enableLinestringBuilder;
+		
+		writerContainer = new CompletableContainer();
+		
+		userWriter = writerContainer.add(new CopyFileWriter(copyFileset.getUserFile()));
+		nodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeFile()));
+		nodeTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeTagFile()));
+		wayWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayFile()));
+		wayTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayTagFile()));
+		wayNodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayNodeFile()));
+		relationWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationFile()));
+		relationTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationTagFile()));
+		relationMemberWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationMemberFile()));
+		
+		pointBuilder = new PointBuilder();
+		wayGeometryBuilder = new WayGeometryBuilder(storeType);
+		memberTypeValueMapper = new MemberTypeValueMapper();
+		memberTypeValueMapper = new MemberTypeValueMapper();
+		
+		userSet = new HashSet<Integer>();
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		OsmUser user;
+		
+		// Write a user entry if the user doesn't already exist.
+		user = entityContainer.getEntity().getUser();
+		if (!user.equals(OsmUser.NONE)) {
+			if (!userSet.contains(user.getId())) {
+				userWriter.writeField(user.getId());
+				userWriter.writeField(user.getName());
+				userWriter.endRecord();
+				
+				userSet.add(user.getId());
+			}
+		}
+		
+		// Process the entity itself.
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer nodeContainer) {
+		Node node;
+		
+		node = nodeContainer.getEntity();
+		
+		nodeWriter.writeField(node.getId());
+		nodeWriter.writeField(node.getVersion());
+		nodeWriter.writeField(node.getUser().getId());
+		nodeWriter.writeField(node.getTimestamp());
+		nodeWriter.writeField(node.getChangesetId());
+		nodeWriter.writeField(pointBuilder.createPoint(node.getLatitude(), node.getLongitude()));
+		nodeWriter.endRecord();
+		
+		for (Tag tag : node.getTags()) {
+			nodeTagWriter.writeField(node.getId());
+			nodeTagWriter.writeField(tag.getKey());
+			nodeTagWriter.writeField(tag.getValue());
+			nodeTagWriter.endRecord();
+		}
+		
+		if (enableBboxBuilder || enableLinestringBuilder) {
+			wayGeometryBuilder.addNodeLocation(node);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		Way way;
+		int sequenceId;
+		
+		way = wayContainer.getEntity();
+		
+		// Ignore ways with a single node because they can't be loaded into postgis.
+		if (way.getWayNodes().size() > 1) {
+			wayWriter.writeField(way.getId());
+			wayWriter.writeField(way.getVersion());
+			wayWriter.writeField(way.getUser().getId());
+			wayWriter.writeField(way.getTimestamp());
+			wayWriter.writeField(way.getChangesetId());
+			if (enableBboxBuilder) {
+				wayWriter.writeField(wayGeometryBuilder.createWayBbox(way));
+			}
+			if (enableLinestringBuilder) {
+				wayWriter.writeField(wayGeometryBuilder.createWayLinestring(way));
+			}
+			wayWriter.endRecord();
+			
+			for (Tag tag : way.getTags()) {
+				wayTagWriter.writeField(way.getId());
+				wayTagWriter.writeField(tag.getKey());
+				wayTagWriter.writeField(tag.getValue());
+				wayTagWriter.endRecord();
+			}
+			
+			sequenceId = 0;
+			for (WayNode wayNode : way.getWayNodes()) {
+				wayNodeWriter.writeField(way.getId());
+				wayNodeWriter.writeField(wayNode.getNodeId());
+				wayNodeWriter.writeField(sequenceId++);
+				wayNodeWriter.endRecord();
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		Relation relation;
+		int memberSequenceId;
+		
+		relation = relationContainer.getEntity();
+		
+		relationWriter.writeField(relation.getId());
+		relationWriter.writeField(relation.getVersion());
+		relationWriter.writeField(relation.getUser().getId());
+		relationWriter.writeField(relation.getTimestamp());
+		relationWriter.writeField(relation.getChangesetId());
+		relationWriter.endRecord();
+		
+		for (Tag tag : relation.getTags()) {
+			relationTagWriter.writeField(relation.getId());
+			relationTagWriter.writeField(tag.getKey());
+			relationTagWriter.writeField(tag.getValue());
+			relationTagWriter.endRecord();
+		}
+		
+		memberSequenceId = 0;
+		for (RelationMember member : relation.getMembers()) {
+			relationMemberWriter.writeField(relation.getId());
+			relationMemberWriter.writeField(member.getMemberId());
+			relationMemberWriter.writeField(memberTypeValueMapper.getMemberType(member.getMemberType()));
+			relationMemberWriter.writeField(member.getMemberRole());
+			relationMemberWriter.writeField(memberSequenceId++);
+			relationMemberWriter.endRecord();
+		}
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the database and commits. 
+	 */
+	public void complete() {
+		writerContainer.complete();
+	}
+	
+	
+	/**
+	 * Releases all resources.
+	 */
+	public void release() {
+		writerContainer.release();
+		wayGeometryBuilder.release();
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetLoader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetLoader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetLoader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetLoader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DatabaseCapabilityChecker.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DatabaseCapabilityChecker.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DatabaseCapabilityChecker.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DatabaseCapabilityChecker.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DbOrderedFeatureComparator.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DbOrderedFeatureComparator.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DbOrderedFeatureComparator.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DbOrderedFeatureComparator.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DirectoryCopyFileset.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DirectoryCopyFileset.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DirectoryCopyFileset.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/DirectoryCopyFileset.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityDao.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureDao.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureTableReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureTableReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureTableReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityFeatureTableReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityTableReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityTableReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityTableReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/EntityTableReader.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java
new file mode 100644
index 0000000..965abe6
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java
@@ -0,0 +1,158 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
+
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+
+
+/**
+ * Drops and creates indexes in support of bulk load activities.
+ * 
+ * @author Brett Henderson
+ */
+public class IndexManager {
+	
+	private static final Logger LOG = Logger.getLogger(IndexManager.class.getName());
+	
+	
+	private static final String[] PRE_LOAD_SQL = {
+		"ALTER TABLE users DROP CONSTRAINT pk_users",
+		"ALTER TABLE nodes DROP CONSTRAINT pk_nodes",
+		"ALTER TABLE ways DROP CONSTRAINT pk_ways",
+		"ALTER TABLE way_nodes DROP CONSTRAINT pk_way_nodes",
+		"ALTER TABLE relations DROP CONSTRAINT pk_relations",
+		"ALTER TABLE relation_members DROP CONSTRAINT pk_relation_members",
+		"DROP INDEX idx_node_tags_node_id",
+		"DROP INDEX idx_nodes_geom",
+		"DROP INDEX idx_way_tags_way_id",
+		"DROP INDEX idx_relation_tags_relation_id",
+		"DROP INDEX idx_way_nodes_node_id"
+	};
+	private static final String[] PRE_LOAD_SQL_WAY_BBOX = {
+		"DROP INDEX idx_ways_bbox"
+	};
+	private static final String[] PRE_LOAD_SQL_WAY_LINESTRING = {
+		"DROP INDEX idx_ways_linestring"
+	};
+	
+	private static final String[] POST_LOAD_SQL = {
+		"ALTER TABLE ONLY users ADD CONSTRAINT pk_users PRIMARY KEY (id)",
+		"ALTER TABLE ONLY nodes ADD CONSTRAINT pk_nodes PRIMARY KEY (id)",
+		"ALTER TABLE ONLY ways ADD CONSTRAINT pk_ways PRIMARY KEY (id)",
+		"ALTER TABLE ONLY way_nodes ADD CONSTRAINT pk_way_nodes PRIMARY KEY (way_id, sequence_id)",
+		"ALTER TABLE ONLY relations ADD CONSTRAINT pk_relations PRIMARY KEY (id)",
+		"ALTER TABLE ONLY relation_members ADD CONSTRAINT pk_relation_members PRIMARY KEY (relation_id, sequence_id)",
+		"CREATE INDEX idx_node_tags_node_id ON node_tags USING btree (node_id)",
+		"CREATE INDEX idx_nodes_geom ON nodes USING gist (geom)",
+		"CREATE INDEX idx_way_tags_way_id ON way_tags USING btree (way_id)",
+		"CREATE INDEX idx_relation_tags_relation_id ON relation_tags USING btree (relation_id)",
+		"CREATE INDEX idx_way_nodes_node_id ON way_nodes USING btree (node_id)"
+	};
+	private static final String[] POST_LOAD_SQL_WAY_BBOX = {
+		"CREATE INDEX idx_ways_bbox ON ways USING gist (bbox)"
+	};
+	private static final String[] POST_LOAD_SQL_WAY_LINESTRING = {
+		"CREATE INDEX idx_ways_linestring ON ways USING gist (linestring)"
+	};
+	private static final String POST_LOAD_SQL_POPULATE_WAY_BBOX =
+		"UPDATE ways SET bbox = ("
+		+ "SELECT ST_Envelope(ST_Collect(geom)) FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
+		+ " WHERE way_nodes.way_id = ways.id"
+		+ ")";
+	private static final String POST_LOAD_SQL_POPULATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ "SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ "SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ ") c"
+		+ ")";
+	
+	
+	private DatabaseContext dbCtx;
+	private DatabaseCapabilityChecker capabilityChecker;
+	private boolean populateBbox;
+	private boolean populateLinestring;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            Provides access to the database.
+	 * @param populateBbox
+	 *            If true, the bbox colum on the way table will be populated
+	 *            after load.
+	 * @param populateLinestring
+	 *            If true, the linestring column on the way table will be
+	 *            populated after load.
+	 */
+	public IndexManager(DatabaseContext dbCtx, boolean populateBbox, boolean populateLinestring) {
+		this.dbCtx = dbCtx;
+		this.populateBbox = populateBbox;
+		this.populateLinestring = populateLinestring;
+		
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+	}
+	
+	
+	/**
+	 * Drops indexes and constraints in the database.
+	 */
+	public void prepareForLoad() {
+		LOG.fine("Running pre-load SQL statements.");
+		for (int i = 0; i < PRE_LOAD_SQL.length; i++) {
+			LOG.finer("SQL: " + PRE_LOAD_SQL[i]);
+			dbCtx.executeStatement(PRE_LOAD_SQL[i]);
+		}
+		if (capabilityChecker.isWayBboxSupported()) {
+			LOG.fine("Running pre-load bbox SQL statements.");
+			for (int i = 0; i < PRE_LOAD_SQL_WAY_BBOX.length; i++) {
+				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_BBOX[i]);
+				dbCtx.executeStatement(PRE_LOAD_SQL_WAY_BBOX[i]);
+			}
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			LOG.fine("Running pre-load linestring SQL statements.");
+			for (int i = 0; i < PRE_LOAD_SQL_WAY_LINESTRING.length; i++) {
+				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_LINESTRING[i]);
+				dbCtx.executeStatement(PRE_LOAD_SQL_WAY_LINESTRING[i]);
+			}
+		}
+		LOG.fine("Pre-load SQL statements complete.");
+	}
+	
+	
+	/**
+	 * Creates indexes in the database and populates derived columns.
+	 */
+	public void completeAfterLoad() {
+		LOG.fine("Running post-load SQL.");
+		for (int i = 0; i < POST_LOAD_SQL.length; i++) {
+			LOG.finer("SQL: " + POST_LOAD_SQL[i]);
+			dbCtx.executeStatement(POST_LOAD_SQL[i]);
+		}
+		if (capabilityChecker.isWayBboxSupported()) {
+			LOG.fine("Running post-load bbox SQL statements.");
+			if (populateBbox) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_BBOX);
+				dbCtx.executeStatement(POST_LOAD_SQL_POPULATE_WAY_BBOX);
+			}
+			for (int i = 0; i < POST_LOAD_SQL_WAY_BBOX.length; i++) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_BBOX[i]);
+				dbCtx.executeStatement(POST_LOAD_SQL_WAY_BBOX[i]);
+			}
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			LOG.fine("Running post-load linestring SQL statements.");
+			if (populateLinestring) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
+				dbCtx.executeStatement(POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
+			}
+			for (int i = 0; i < POST_LOAD_SQL_WAY_LINESTRING.length; i++) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_LINESTRING[i]);
+				dbCtx.executeStatement(POST_LOAD_SQL_WAY_LINESTRING[i]);
+			}
+		}
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/MemberTypeValueMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/MemberTypeValueMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/MemberTypeValueMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/MemberTypeValueMapper.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java
new file mode 100644
index 0000000..77f814b
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java
@@ -0,0 +1,119 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+
+
+/**
+ * Performs all node-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class NodeDao extends EntityDao<Node> {
+	private static final String SQL_UPDATE_WAY_BBOX =
+		"UPDATE ways w SET bbox = ("
+		+ " SELECT ST_Envelope(ST_Collect(n.geom))"
+		+ " FROM nodes n INNER JOIN way_nodes wn ON wn.node_id = n.id"
+		+ " WHERE wn.way_id = w.id"
+		+ " )"
+		+ " WHERE w.id IN ("
+		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
+		+ " )";
+	private static final String SQL_UPDATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ " ) c"
+		+ " )"
+		+ " WHERE w.id IN ("
+		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
+		+ " )";
+	
+	
+	private DatabaseCapabilityChecker capabilityChecker;
+	private PreparedStatement updateWayBboxStatement;
+	private PreparedStatement updateWayLinestringStatement;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public NodeDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		super(dbCtx, new NodeMapper(), actionDao);
+		
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void loadFeatures(long entityId, Node entity) {
+		// Nodes have no additional features.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void modifyEntity(Node entity) {
+		super.modifyEntity(entity);
+		
+		if (capabilityChecker.isWayBboxSupported()) {
+			if (updateWayBboxStatement == null) {
+				updateWayBboxStatement = prepareStatement(SQL_UPDATE_WAY_BBOX);
+			}
+			
+			try {
+				int prmIndex;
+				
+				prmIndex = 1;
+				updateWayBboxStatement.setLong(prmIndex++, entity.getId());
+				updateWayBboxStatement.executeUpdate();
+				
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Update bbox failed for node " + entity.getId() + ".");
+			}
+		}
+		
+		if (capabilityChecker.isWayLinestringSupported()) {
+			if (updateWayLinestringStatement == null) {
+				updateWayLinestringStatement = prepareStatement(SQL_UPDATE_WAY_LINESTRING);
+			}
+			
+			try {
+				int prmIndex;
+				
+				prmIndex = 1;
+				updateWayLinestringStatement.setLong(prmIndex++, entity.getId());
+				updateWayLinestringStatement.executeUpdate();
+				
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Update linestring failed for node " + entity.getId() + ".");
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public ReleasableIterator<Node> iterate() {
+		return new NodeReader(getDatabaseContext());
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlDatasetContext.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlDatasetContext.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlDatasetContext.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlDatasetContext.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlEntityManager.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlEntityManager.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlEntityManager.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/PostgreSqlEntityManager.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationDao.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMemberMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMemberMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMemberMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationMemberMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/RelationReader.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TagMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TagMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TagMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TagMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TempCopyFileset.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TempCopyFileset.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TempCopyFileset.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/TempCopyFileset.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/UserDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/UserDao.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/UserDao.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/UserDao.java
diff --git a/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java
new file mode 100644
index 0000000..3fbfffa
--- /dev/null
+++ b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java
@@ -0,0 +1,182 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
+
+
+/**
+ * Performs all way-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class WayDao extends EntityDao<Way> {
+	
+	private static final String SQL_UPDATE_WAY_BBOX =
+		"UPDATE ways SET bbox = ("
+		+ " SELECT ST_Envelope(ST_Collect(geom))"
+		+ " FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
+		+ " WHERE way_nodes.way_id = ways.id"
+		+ " )"
+		+ " WHERE ways.id = ?";
+	private static final String SQL_UPDATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ " ) c"
+		+ " )"
+		+ " WHERE w.id  = ?";
+	
+	private DatabaseCapabilityChecker capabilityChecker;
+	private EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>> wayNodeDao;
+	private PreparedStatement updateWayBboxStatement;
+	private PreparedStatement updateWayLinestringStatement;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public WayDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		super(dbCtx, new WayMapper(), actionDao);
+		
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+		wayNodeDao = new EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>>(dbCtx, new WayNodeMapper());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void loadFeatures(long entityId, Way entity) {
+		entity.getWayNodes().addAll(wayNodeDao.getAllRaw(entityId));
+	}
+
+
+	/**
+	 * Adds the specified way node list to the database.
+	 * 
+	 * @param entityId
+	 *            The identifier of the entity to add these features to.
+	 * @param wayNodeList
+	 *            The list of features to add.
+	 */
+	private void addWayNodeList(long entityId, List<WayNode> wayNodeList) {
+		List<DbOrderedFeature<WayNode>> dbList;
+		
+		dbList = new ArrayList<DbOrderedFeature<WayNode>>(wayNodeList.size());
+		
+		for (int i = 0; i < wayNodeList.size(); i++) {
+			dbList.add(new DbOrderedFeature<WayNode>(entityId, wayNodeList.get(i), i));
+		}
+		
+		wayNodeDao.addAll(dbList);
+	}
+	
+	
+	/**
+	 * Updates the bounding box column for the specified way.
+	 * 
+	 * @param wayId
+	 *            The way bounding box.
+	 */
+	private void updateWayGeometries(long wayId) {
+		if (capabilityChecker.isWayBboxSupported()) {
+			if (updateWayBboxStatement == null) {
+				updateWayBboxStatement = prepareStatement(SQL_UPDATE_WAY_BBOX);
+			}
+			
+			try {
+				int prmIndex;
+				
+				prmIndex = 1;
+				updateWayBboxStatement.setLong(prmIndex++, wayId);
+				updateWayBboxStatement.executeUpdate();
+				
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Update bbox failed for way " + wayId + ".");
+			}
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			if (updateWayLinestringStatement == null) {
+				updateWayLinestringStatement = prepareStatement(SQL_UPDATE_WAY_LINESTRING);
+			}
+			
+			try {
+				int prmIndex;
+				
+				prmIndex = 1;
+				updateWayLinestringStatement.setLong(prmIndex++, wayId);
+				updateWayLinestringStatement.executeUpdate();
+				
+			} catch (SQLException e) {
+				throw new OsmosisRuntimeException("Update linestring failed for way " + wayId + ".");
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addEntity(Way entity) {
+		super.addEntity(entity);
+		
+		addWayNodeList(entity.getId(), entity.getWayNodes());
+		
+		updateWayGeometries(entity.getId());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void modifyEntity(Way entity) {
+		long wayId;
+		
+		super.modifyEntity(entity);
+		
+		wayId = entity.getId();
+		wayNodeDao.removeList(wayId);
+		addWayNodeList(entity.getId(), entity.getWayNodes());
+		
+		updateWayGeometries(entity.getId());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeEntity(long entityId) {
+		wayNodeDao.removeList(entityId);
+		
+		super.removeEntity(entityId);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public ReleasableIterator<Way> iterate() {
+		return new WayReader(getDatabaseContext());
+	}
+}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayGeometryBuilder.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayGeometryBuilder.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayGeometryBuilder.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayGeometryBuilder.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayNodeMapper.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayNodeMapper.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayNodeMapper.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayNodeMapper.java
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayReader.java b/osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayReader.java
similarity index 100%
rename from pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayReader.java
rename to osmosis-pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayReader.java
diff --git a/pgsimple/src/main/resources/osmosis-plugins.conf b/osmosis-pgsimple/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from pgsimple/src/main/resources/osmosis-plugins.conf
rename to osmosis-pgsimple/src/main/resources/osmosis-plugins.conf
diff --git a/pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriver.java b/osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriver.java
similarity index 100%
rename from pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriver.java
rename to osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriver.java
diff --git a/pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverFactory.java b/osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverFactory.java
similarity index 100%
rename from pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverFactory.java
rename to osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverFactory.java
diff --git a/pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverPlugin.java b/osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverPlugin.java
similarity index 100%
rename from pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverPlugin.java
rename to osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/DatasetDriverPlugin.java
diff --git a/pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTest.java b/osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTest.java
similarity index 100%
rename from pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTest.java
rename to osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlTest.java
diff --git a/pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeLocationStoreTest.java b/osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeLocationStoreTest.java
similarity index 100%
rename from pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeLocationStoreTest.java
rename to osmosis-pgsimple/src/test/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeLocationStoreTest.java
diff --git a/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm
new file mode 100644
index 0000000..92765e7
--- /dev/null
+++ b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1-revised"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <member type="node" ref="7" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/pgsimple/src/test/resources/data/template/v0_6/db-changeset.osc b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-changeset.osc
similarity index 100%
rename from pgsimple/src/test/resources/data/template/v0_6/db-changeset.osc
rename to osmosis-pgsimple/src/test/resources/data/template/v0_6/db-changeset.osc
diff --git a/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm
new file mode 100644
index 0000000..e371168
--- /dev/null
+++ b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm
@@ -0,0 +1,48 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
+    <tag k="change" v="new tag"/>
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="7" version="16" timestamp="2008-01-02T18:19:20Z" changeset="93" lat="-11" lon="-12">
+    <tag k="change" v="new node"/>
+    <tag k="created_by" v="Me7"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm
new file mode 100644
index 0000000..fd0631c
--- /dev/null
+++ b/osmosis-pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm
@@ -0,0 +1,46 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/pgsimple/src/test/resources/data/template/v0_6/pgsql-authfile.txt b/osmosis-pgsimple/src/test/resources/data/template/v0_6/pgsql-authfile.txt
similarity index 100%
rename from pgsimple/src/test/resources/data/template/v0_6/pgsql-authfile.txt
rename to osmosis-pgsimple/src/test/resources/data/template/v0_6/pgsql-authfile.txt
diff --git a/osmosis-pgsnapshot/.checkstyle b/osmosis-pgsnapshot/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-pgsnapshot/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-pgsnapshot/.gitignore b/osmosis-pgsnapshot/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-pgsnapshot/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-pgsnapshot/build.gradle b/osmosis-pgsnapshot/build.gradle
new file mode 100644
index 0000000..d4a2f8b
--- /dev/null
+++ b/osmosis-pgsnapshot/build.gradle
@@ -0,0 +1,16 @@
+configurations {
+    // Exclude unnecessary postgis stub classes.
+    all*.exclude group: 'org.postgis', module: 'postgis-stubs'
+}
+
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-hstore-jdbc')
+    compile group: 'commons-dbcp', name: 'commons-dbcp', version: dependencyVersionCommonsDbcp
+    compile group: 'org.postgis', name: 'postgis-jdbc', version: dependencyVersionPostGis
+    compile group: 'org.springframework', name: 'spring-jdbc', version: dependencyVersionSpring
+    compile group: 'postgresql', name: 'postgresql', version: dependencyVersionPostgreSql
+    testCompile project(':osmosis-dataset')
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/PgSnapshotPluginLoader.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/PgSnapshotPluginLoader.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/PgSnapshotPluginLoader.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/PgSnapshotPluginLoader.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocation.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocation.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocation.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocation.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocationStore.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocationStore.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocationStore.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CompactPersistentNodeLocationStore.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CopyFileWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CopyFileWriter.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CopyFileWriter.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/CopyFileWriter.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DataSourceManager.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DataSourceManager.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DataSourceManager.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DataSourceManager.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java
new file mode 100644
index 0000000..5fd58b5
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java
@@ -0,0 +1,307 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.common;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.postgresql.copy.CopyManager;
+import org.postgresql.core.BaseConnection;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionTemplate;
+
+
+/**
+ * This class manages the lifecycle of JDBC objects to minimise the risk of connection leaks and to
+ * support a consistent approach to database access.
+ * 
+ * @author Brett Henderson
+ */
+public class DatabaseContext {
+
+    private static final Logger LOG = Logger.getLogger(DatabaseContext.class.getName());
+
+    private DataSourceManager dataSourceManager;
+    private DataSource dataSource;
+    private PlatformTransactionManager txnManager;
+    private TransactionTemplate txnTemplate;
+    private TransactionStatus transaction;
+    private JdbcTemplate jdbcTemplate;
+    
+
+    /**
+     * Creates a new instance.
+     * 
+     * @param loginCredentials Contains all information required to connect to the database.
+     */
+    public DatabaseContext(DatabaseLoginCredentials loginCredentials) {
+    	dataSourceManager = new DataSourceManager(loginCredentials);
+    	dataSource = dataSourceManager.getDataSource();
+    	txnManager = new DataSourceTransactionManager(dataSource);
+    	txnTemplate = new TransactionTemplate(txnManager);
+    	jdbcTemplate = new JdbcTemplate(dataSource);
+    	
+    	setStatementFetchSizeForStreaming();
+    }
+
+
+	/**
+	 * Begins a new database transaction. This is not required if
+	 * executeWithinTransaction is being used.
+	 */
+    public void beginTransaction() {
+    	if (transaction != null) {
+    		throw new OsmosisRuntimeException("A transaction is already active.");
+    	}
+    	
+    	transaction = txnManager.getTransaction(new DefaultTransactionDefinition());
+    }
+    
+    
+    /**
+     * Commits an existing database transaction.
+     */
+    public void commitTransaction() {
+    	if (transaction == null) {
+    		throw new OsmosisRuntimeException("No transaction is currently active.");
+    	}
+    	
+    	try {
+    		txnManager.commit(transaction);
+    	} finally {
+    		transaction = null;
+    	}
+    }
+    
+    
+    /**
+     * Gets the jdbc template which provides access to database functions.
+     * 
+     * @return The jdbc template.
+     */
+    public JdbcTemplate getJdbcTemplate() {
+    	return jdbcTemplate;
+    }
+    
+    
+	/**
+	 * Invokes the provided callback code within a transaction.
+	 * 
+	 * @param txnCallback
+	 *            The logic to be invoked within a transaction.
+	 * @param <T>
+	 *            The return type of the transaction callback.
+	 * 
+	 * @return The result.
+	 */
+    public <T> Object executeWithinTransaction(TransactionCallback<T> txnCallback) {
+    	return txnTemplate.execute(txnCallback);
+    }
+    
+    
+    private void setStatementFetchSizeForStreaming() {
+        jdbcTemplate.setFetchSize(10000);
+    }
+	
+
+    /**
+     * Releases all database resources. This method is guaranteed not to throw transactions and
+     * should always be called in a finally block whenever this class is used.
+     */
+    public void release() {
+    	if (transaction != null) {
+    		try {
+    			txnManager.rollback(transaction);
+    		} finally {
+    			transaction = null;
+    		}
+    	}
+    	
+    	dataSourceManager.release();
+    }
+    
+
+    /**
+     * Indicates if the specified column exists in the database.
+     * 
+     * @param tableName The table to check for.
+     * @param columnName The column to check for.
+     * @return True if the column exists, false otherwise.
+     */
+    public boolean doesColumnExist(String tableName, String columnName) {
+        ResultSet resultSet = null;
+        boolean result;
+
+        try {
+        	Connection connection;
+        	
+            LOG.finest("Checking if column {" + columnName + "} in table {" + tableName + "} exists.");
+
+            // This connection may not be freed if an exception occurs. It's a small chance and the
+			// additional code to avoid it is cumbersome.
+            connection = DataSourceUtils.getConnection(dataSource);
+            
+            resultSet = connection.getMetaData().getColumns(null, null, tableName, columnName);
+            result = resultSet.next();
+            resultSet.close();
+            resultSet = null;
+            
+            DataSourceUtils.releaseConnection(connection, dataSource);
+
+            return result;
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to check for the existence of column " + tableName + "."
+                    + columnName + ".", e);
+        } finally {
+            if (resultSet != null) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    // We are already in an error condition so log and continue.
+                    LOG.log(Level.WARNING, "Unable to close column existence result set.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Indicates if the specified table exists in the database.
+     * 
+     * @param tableName The table to check for.
+     * @return True if the table exists, false otherwise.
+     */
+    public boolean doesTableExist(String tableName) {
+        ResultSet resultSet = null;
+        boolean result;
+
+        try {
+        	Connection connection;
+        	
+            LOG.finest("Checking if table {" + tableName + "} exists.");
+
+            // This connection may not be freed if an exception occurs. It's a small chance and the
+			// additional code to avoid it is cumbersome.
+            connection = DataSourceUtils.getConnection(dataSource);
+
+            resultSet = connection.getMetaData().getTables(null, null, tableName, new String[] {"TABLE"});
+            result = resultSet.next();
+            resultSet.close();
+            resultSet = null;
+            
+            DataSourceUtils.releaseConnection(connection, dataSource);
+
+            return result;
+
+        } catch (SQLException e) {
+            throw new OsmosisRuntimeException("Unable to check for the existence of table " + tableName + ".", e);
+        } finally {
+            if (resultSet != null) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    // We are already in an error condition so log and continue.
+                    LOG.log(Level.WARNING, "Unable to close table existence result set.", e);
+                }
+            }
+        }
+    }
+
+
+	/**
+	 * Loads a table from a COPY file.
+	 * 
+	 * @param copyFile
+	 *            The file to be loaded.
+	 * @param tableName
+	 *            The table to load the data into.
+	 * @param columns
+	 *            The columns to be loaded (optional).
+	 */
+    public void loadCopyFile(File copyFile, String tableName, String ... columns) {
+    	CopyManager copyManager;
+    	InputStream inStream = null;
+    	
+    	try {
+    		StringBuilder copyStatement;
+    		InputStream bufferedInStream;
+    		Connection conn;
+    		
+    		copyStatement = new StringBuilder();
+    		copyStatement.append("COPY ");
+    		copyStatement.append(tableName);
+    		if (columns.length > 0) {
+    			copyStatement.append('(');
+    			for (int i = 0; i < columns.length; i++) {
+    				if (i > 0) {
+    					copyStatement.append(',');
+    				}
+    				copyStatement.append(columns[i]);
+    			}
+    			copyStatement.append(')');
+    		}
+    		copyStatement.append(" FROM STDIN");
+    		
+    		inStream = new FileInputStream(copyFile);
+    		bufferedInStream = new BufferedInputStream(inStream, 65536);
+    		
+    		conn = DataSourceUtils.getConnection(dataSource);
+    		try {
+	    		copyManager = new CopyManager(conn.unwrap(BaseConnection.class));
+	    		
+	    		copyManager.copyIn(copyStatement.toString(), bufferedInStream);
+    		} finally {
+    			DataSourceUtils.releaseConnection(conn, dataSource);
+    		}
+			
+    		inStream.close();
+			inStream = null;
+			
+    	} catch (IOException e) {
+    		throw new OsmosisRuntimeException("Unable to process COPY file " + copyFile + ".", e);
+    	} catch (SQLException e) {
+    		throw new OsmosisRuntimeException("Unable to process COPY file " + copyFile + ".", e);
+    	} finally {
+    		if (inStream != null) {
+				try {
+					inStream.close();
+				} catch (IOException e) {
+					LOG.log(Level.SEVERE, "Unable to close COPY file.", e);
+				}
+				inStream = null;
+			}
+    	}
+    }
+    
+
+    /**
+     * Enforces cleanup of any remaining resources during garbage collection. This is a safeguard
+     * and should not be required if release is called appropriately.
+     * 
+     * @throws Throwable If a problem occurs during finalization.
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        release();
+
+        super.finalize();
+    }
+
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/InMemoryNodeLocationStore.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/InMemoryNodeLocationStore.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/InMemoryNodeLocationStore.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/InMemoryNodeLocationStore.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NoSuchRecordException.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NoSuchRecordException.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NoSuchRecordException.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NoSuchRecordException.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocation.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocation.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocation.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocation.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStore.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStore.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStore.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStore.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStoreType.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStoreType.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStoreType.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/NodeLocationStoreType.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PersistentNodeLocationStore.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PersistentNodeLocationStore.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PersistentNodeLocationStore.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PersistentNodeLocationStore.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PointBuilder.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PointBuilder.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PointBuilder.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PointBuilder.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PolygonBuilder.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PolygonBuilder.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PolygonBuilder.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/PolygonBuilder.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/RowMapperRowCallbackListener.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/RowMapperRowCallbackListener.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/RowMapperRowCallbackListener.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/RowMapperRowCallbackListener.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java
new file mode 100644
index 0000000..93bacbd
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java
@@ -0,0 +1,74 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.common;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Reads the version number stored in the schema_info table and verifies that it
+ * matches the expected version.
+ * 
+ * @author Brett Henderson
+ */
+public class SchemaVersionValidator {
+	private static final String SELECT_SQL = "SELECT version FROM schema_info";
+	
+	private DatabasePreferences preferences;
+	private JdbcTemplate jdbcTemplate;
+	private boolean validated;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param jdbcTemplate
+	 *            Provides access to the database.
+	 * @param preferences
+	 *            The database preferences.
+	 */
+	public SchemaVersionValidator(JdbcTemplate jdbcTemplate, DatabasePreferences preferences) {
+		this.jdbcTemplate = jdbcTemplate;
+		this.preferences = preferences;
+	}
+	
+	
+	/**
+	 * Validates that the version number of the schema matches the expected
+	 * version. This method caches the result allowing it to be called multiple
+	 * times without a performance penalty.
+	 * 
+	 * @param expectedVersion
+	 *            The expected version number.
+	 */
+	public void validateVersion(int expectedVersion) {
+		if (!validated) {
+			validateDBVersion(expectedVersion);
+			
+			validated = true;
+		}
+	}
+	
+	
+	/**
+	 * Performs the database lookup and validates the expected version.
+	 * 
+	 * @param expectedVersion
+	 *            The expected version number.
+	 */
+	private void validateDBVersion(int expectedVersion) {
+		if (preferences.getValidateSchemaVersion()) {
+			int dbVersion;
+			
+			dbVersion = jdbcTemplate.queryForInt(SELECT_SQL);
+			
+			if (dbVersion != expectedVersion) {
+				throw new OsmosisRuntimeException(
+					"The database schema version of " + dbVersion
+					+ " does not match the expected version of " + expectedVersion + "."
+				);
+			}
+		}
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java
new file mode 100644
index 0000000..7bbfab4
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java
@@ -0,0 +1,125 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.ActionChangeWriter;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.ChangeWriter;
+
+
+/**
+ * A change sink writing to database tables. This aims to be suitable for
+ * running at regular intervals with database overhead proportional to changeset
+ * size.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlChangeWriter implements ChangeSink {
+	
+	private ChangeWriter changeWriter;
+	private Map<ChangeAction, ActionChangeWriter> actionWriterMap;
+	private DatabaseContext dbCtx;
+	private SchemaVersionValidator schemaVersionValidator;
+	private boolean initialized;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public PostgreSqlChangeWriter(DatabaseLoginCredentials loginCredentials, 
+			DatabasePreferences preferences, boolean keepInvalidWays) {
+		dbCtx = new DatabaseContext(loginCredentials);
+		changeWriter = new ChangeWriter(dbCtx);
+		actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
+		actionWriterMap.put(ChangeAction.Create, 
+				new ActionChangeWriter(changeWriter, ChangeAction.Create, keepInvalidWays));
+		actionWriterMap.put(ChangeAction.Modify, 
+				new ActionChangeWriter(changeWriter, ChangeAction.Modify, keepInvalidWays));
+		actionWriterMap.put(ChangeAction.Delete, 
+				new ActionChangeWriter(changeWriter, ChangeAction.Delete, keepInvalidWays));
+		
+		schemaVersionValidator = new SchemaVersionValidator(dbCtx.getJdbcTemplate(), preferences);
+		
+		initialized = false;
+	}
+	
+	
+	private void initialize() {
+		if (!initialized) {
+			dbCtx.beginTransaction();
+			
+			initialized = true;
+		}
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer change) {
+		ChangeAction action;
+		
+		initialize();
+		
+		// Verify that the schema version is supported.
+		schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
+		
+		action = change.getAction();
+		
+		if (!actionWriterMap.containsKey(action)) {
+			throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
+		}
+		
+		// Process the entity using the action writer appropriate for the change
+		// action.
+		change.getEntityContainer().process(actionWriterMap.get(action));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		initialize();
+		
+		changeWriter.complete();
+		
+		dbCtx.commitTransaction();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		changeWriter.release();
+		
+		dbCtx.release();
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java
new file mode 100644
index 0000000..b4be103
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java
@@ -0,0 +1,46 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkManager;
+
+
+/**
+ * The task manager factory for a database change writer.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlChangeWriterFactory extends DatabaseTaskManagerFactory {
+	
+	private static final String ARG_KEEP_INVALID_WAYS = "keepInvalidWays";
+	private static final boolean DEFAULT_KEEP_INVALID_WAYS = true;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		DatabaseLoginCredentials loginCredentials;
+		DatabasePreferences preferences;
+		
+		// Get the task arguments.
+		loginCredentials = getDatabaseLoginCredentials(taskConfig);
+		preferences = getDatabasePreferences(taskConfig);
+		
+		boolean keepInvalidWays = getBooleanArgument(taskConfig, ARG_KEEP_INVALID_WAYS, DEFAULT_KEEP_INVALID_WAYS);
+		
+		return new ChangeSinkManager(
+			taskConfig.getId(),
+			new PostgreSqlChangeWriter(
+				loginCredentials,
+				preferences,
+				keepInvalidWays
+			),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java
new file mode 100644
index 0000000..11d58db
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java
@@ -0,0 +1,141 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetBuilder;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetLoader;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.DatabaseCapabilityChecker;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.TempCopyFileset;
+
+
+/**
+ * An OSM data sink for storing all data to a database using the COPY command.
+ * This task is intended for writing to an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlCopyWriter implements Sink {
+	
+	private static final Logger LOG = Logger.getLogger(PostgreSqlCopyWriter.class.getName());
+	
+	private CopyFilesetBuilder copyFilesetBuilder;
+	private CopyFilesetLoader copyFilesetLoader;
+	private TempCopyFileset copyFileset;
+	private DatabaseLoginCredentials loginCredentials;
+	private DatabasePreferences preferences;
+	private NodeLocationStoreType storeType;
+	private boolean populateBbox;
+	private boolean populateLinestring;
+	private boolean keepInvalidWays;
+	private boolean initialized;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public PostgreSqlCopyWriter(
+			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+			NodeLocationStoreType storeType, boolean keepInvalidWays) {
+		this.loginCredentials = loginCredentials;
+		this.preferences = preferences;
+		this.storeType = storeType;
+		this.keepInvalidWays = keepInvalidWays;
+		
+		copyFileset = new TempCopyFileset();
+	}
+	
+	
+	private void initialize() {
+		if (!initialized) {
+			DatabaseContext dbCtx;
+			DatabaseCapabilityChecker capabilityChecker;
+			
+			LOG.fine("Initializing the database and temporary processing files.");
+			
+			dbCtx = new DatabaseContext(loginCredentials);
+			try {
+				capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+
+				populateBbox = capabilityChecker.isWayBboxSupported();
+				populateLinestring = capabilityChecker.isWayLinestringSupported();
+			} finally {
+				dbCtx.release();
+			}
+
+			copyFilesetBuilder =
+				new CopyFilesetBuilder(copyFileset, populateBbox, populateLinestring, storeType, keepInvalidWays);
+			
+			copyFilesetLoader = new CopyFilesetLoader(loginCredentials, preferences, copyFileset);
+			
+			LOG.fine("Processing input data, building geometries and creating database load files.");
+			
+			initialized = true;
+		}
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		initialize();
+		
+		copyFilesetBuilder.process(entityContainer);
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the files, then loads the files into the database. 
+	 */
+	public void complete() {
+		initialize();
+		
+		copyFilesetBuilder.complete();
+		
+		LOG.fine("All data has been received, beginning database load.");
+		copyFilesetLoader.run();
+		
+		LOG.fine("Processing complete.");
+	}
+	
+	
+	/**
+	 * Releases all database resources.
+	 */
+	public void release() {
+		if (copyFilesetBuilder != null) {
+			copyFilesetBuilder.release();
+			copyFilesetBuilder = null;
+		}
+		copyFileset.release();
+		
+		initialized = false;
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java
new file mode 100644
index 0000000..bdbddce
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java
@@ -0,0 +1,48 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
+
+
+/**
+ * The task manager factory for a database writer using the PostgreSQL COPY method.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlCopyWriterFactory extends DatabaseTaskManagerFactory {
+	private static final String ARG_NODE_LOCATION_STORE_TYPE = "nodeLocationStoreType";
+	private static final String DEFAULT_NODE_LOCATION_STORE_TYPE = "CompactTempFile";
+	private static final String ARG_KEEP_INVALID_WAYS = "keepInvalidWays";
+	private static final boolean DEFAULT_KEEP_INVALID_WAYS = true;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		DatabaseLoginCredentials loginCredentials;
+		DatabasePreferences preferences;
+		NodeLocationStoreType storeType;
+		boolean keepInvalidWays;
+		
+		// Get the task arguments.
+		loginCredentials = getDatabaseLoginCredentials(taskConfig);
+		preferences = getDatabasePreferences(taskConfig);
+		storeType = Enum.valueOf(
+				NodeLocationStoreType.class,
+				getStringArgument(taskConfig, ARG_NODE_LOCATION_STORE_TYPE, DEFAULT_NODE_LOCATION_STORE_TYPE));
+		keepInvalidWays = getBooleanArgument(taskConfig, ARG_KEEP_INVALID_WAYS, DEFAULT_KEEP_INVALID_WAYS);
+		
+		return new SinkManager(
+			taskConfig.getId(),
+			new PostgreSqlCopyWriter(loginCredentials, preferences,	storeType, keepInvalidWays),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReader.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReader.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReader.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReader.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReaderFactory.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReaderFactory.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReaderFactory.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDatasetReaderFactory.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java
new file mode 100644
index 0000000..8c39ddf
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.io.File;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.DirectoryCopyFileset;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetBuilder;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * An OSM data sink for storing all data to database dump files. This task is
+ * intended for populating an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlDumpWriter implements Sink {
+	
+	private CopyFilesetBuilder copyFilesetBuilder;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param filePrefix
+	 *            The prefix to prepend to all generated file names.
+	 * @param enableBboxBuilder
+	 *            If true, the way bbox geometry is built during processing
+	 *            instead of relying on the database to build them after import.
+	 *            This increases processing but is faster than relying on the
+	 *            database.
+	 * @param enableLinestringBuilder
+	 *            If true, the way linestring geometry is built during
+	 *            processing instead of relying on the database to build them
+	 *            after import. This increases processing but is faster than
+	 *            relying on the database.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public PostgreSqlDumpWriter(
+			File filePrefix, boolean enableBboxBuilder,
+			boolean enableLinestringBuilder, NodeLocationStoreType storeType, 
+			boolean keepInvalidWays) {
+		DirectoryCopyFileset copyFileset;
+		
+		copyFileset = new DirectoryCopyFileset(filePrefix);
+		
+		copyFilesetBuilder =
+			new CopyFilesetBuilder(copyFileset, enableBboxBuilder, enableLinestringBuilder, storeType, keepInvalidWays);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		copyFilesetBuilder.process(entityContainer);
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the database and commits. 
+	 */
+	public void complete() {
+		copyFilesetBuilder.complete();
+	}
+	
+	
+	/**
+	 * Releases all database resources.
+	 */
+	public void release() {
+		copyFilesetBuilder.release();
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java
new file mode 100644
index 0000000..0955f8d
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java
@@ -0,0 +1,65 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
+
+
+/**
+ * The task manager factory for a database dump writer.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlDumpWriterFactory extends TaskManagerFactory {
+	private static final String ARG_ENABLE_BBOX_BUILDER = "enableBboxBuilder";
+	private static final String ARG_ENABLE_LINESTRING_BUILDER = "enableLinestringBuilder";
+	private static final String ARG_KEEP_INVALID_WAYS = "keepInvalidWays";
+	private static final String ARG_FILE_NAME = "directory";
+	private static final String ARG_NODE_LOCATION_STORE_TYPE = "nodeLocationStoreType";
+	private static final boolean DEFAULT_ENABLE_BBOX_BUILDER = false;
+	private static final boolean DEFAULT_ENABLE_LINESTRING_BUILDER = false;
+	private static final boolean DEFAULT_KEEP_INVALID_WAYS = true;
+	private static final String DEFAULT_FILE_PREFIX = "pgimport";
+	private static final String DEFAULT_NODE_LOCATION_STORE_TYPE = "CompactTempFile";
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String filePrefixString;
+		File filePrefix;
+		boolean enableBboxBuilder;
+		boolean enableLinestringBuilder;
+		boolean keepInvalidWays;
+		NodeLocationStoreType storeType;
+		
+		// Get the task arguments.
+		filePrefixString = getStringArgument(
+				taskConfig, ARG_FILE_NAME, DEFAULT_FILE_PREFIX);
+		enableBboxBuilder = getBooleanArgument(
+				taskConfig, ARG_ENABLE_BBOX_BUILDER, DEFAULT_ENABLE_BBOX_BUILDER);
+		enableLinestringBuilder = getBooleanArgument(
+				taskConfig, ARG_ENABLE_LINESTRING_BUILDER, DEFAULT_ENABLE_LINESTRING_BUILDER);
+		keepInvalidWays = getBooleanArgument(taskConfig, ARG_KEEP_INVALID_WAYS, DEFAULT_KEEP_INVALID_WAYS);
+		storeType = Enum.valueOf(
+				NodeLocationStoreType.class,
+				getStringArgument(taskConfig, ARG_NODE_LOCATION_STORE_TYPE, DEFAULT_NODE_LOCATION_STORE_TYPE));
+		
+		// Create a file object representing the directory from the file name provided.
+		filePrefix = new File(filePrefixString);
+		
+		return new SinkManager(
+			taskConfig.getId(),
+			new PostgreSqlDumpWriter(
+					filePrefix, enableBboxBuilder, enableLinestringBuilder, storeType, keepInvalidWays),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java
new file mode 100644
index 0000000..ed4341d
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java
@@ -0,0 +1,83 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.task.common.RunnableTask;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
+
+
+/**
+ * A standalone OSM task with no inputs or outputs that truncates tables in a
+ * PostgreSQL database. This is used for removing all existing data from tables.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlTruncator implements RunnableTask {
+	
+	private static final Logger LOG = Logger.getLogger(PostgreSqlTruncator.class.getName());
+	
+	
+	// These tables will be truncated.
+	private static final String[] SQL_TABLE_NAMES = {
+		"actions",
+		"users",
+		"nodes", "node_tags",
+		"ways", "way_tags", "way_nodes",
+		"relations", "relation_tags", "relation_members"
+	};
+	
+	
+	private DatabaseContext dbCtx;
+	private SchemaVersionValidator schemaVersionValidator;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 */
+	public PostgreSqlTruncator(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
+		dbCtx = new DatabaseContext(loginCredentials);
+		
+		schemaVersionValidator = new SchemaVersionValidator(dbCtx.getJdbcTemplate(), preferences);
+	}
+	
+	
+	/**
+	 * Truncates all data from the database.
+	 */
+	public void run() {
+		try {
+			schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
+			
+			dbCtx.beginTransaction();
+			
+			LOG.fine("Truncating tables.");
+			for (int i = 0; i < SQL_TABLE_NAMES.length; i++) {
+				if (dbCtx.doesTableExist(SQL_TABLE_NAMES[i])) {
+					LOG.finer("Truncating table " + SQL_TABLE_NAMES[i] + ".");
+					dbCtx.getJdbcTemplate().update("TRUNCATE " + SQL_TABLE_NAMES[i]);
+				} else {
+					LOG.finer("Skipping table " + SQL_TABLE_NAMES[i] + " which doesn't exist in the current schema.");
+				}
+			}
+			
+			LOG.fine("Committing changes.");
+			dbCtx.commitTransaction();
+			
+			LOG.fine("Vacuuming database.");
+			dbCtx.getJdbcTemplate().update("VACUUM ANALYZE");
+			LOG.fine("Complete.");
+			
+		} finally {
+			dbCtx.release();
+		}
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncatorFactory.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncatorFactory.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncatorFactory.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncatorFactory.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlVersionConstants.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlVersionConstants.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlVersionConstants.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlVersionConstants.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java
new file mode 100644
index 0000000..cc629b6
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java
@@ -0,0 +1,72 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+
+
+/**
+ * Writes entities to a database according to a specific action.
+ * 
+ * @author Brett Henderson
+ */
+public class ActionChangeWriter implements EntityProcessor {
+	private ChangeWriter changeWriter;
+	private ChangeAction action;
+	private boolean keepInvalidWays;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param changeWriter
+	 *            The underlying change writer.
+	 * @param action
+	 *            The action to apply to all writes.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public ActionChangeWriter(ChangeWriter changeWriter, ChangeAction action, boolean keepInvalidWays) {
+		this.changeWriter = changeWriter;
+		this.action = action;
+		this.keepInvalidWays = keepInvalidWays;
+	}
+	
+	
+	/**
+     * {@inheritDoc}
+     */
+    public void process(BoundContainer bound) {
+        // Do nothing.
+    }
+    
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer nodeContainer) {
+		changeWriter.write(nodeContainer.getEntity(), action);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		changeWriter.write(wayContainer.getEntity(), action, keepInvalidWays);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		changeWriter.write(relationContainer.getEntity(), action);
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java
new file mode 100644
index 0000000..28ea662
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java
@@ -0,0 +1,56 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Performs all action db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class ActionDao {
+	private static final String SQL_INSERT = "INSERT INTO actions(data_type, action, id) VALUES(?, ?, ?)";
+	private static final String SQL_TRUNCATE = "TRUNCATE actions";
+	
+	private JdbcTemplate jdbcTemplate;
+	private DatabaseCapabilityChecker capabilityChecker;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 */
+	public ActionDao(DatabaseContext dbCtx) {
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+	}
+	
+	
+	/**
+	 * Adds the specified action to the database.
+	 * 
+	 * @param dataType The type of data being represented by this action. 
+	 * @param action The action being performed on the data.
+	 * @param id The identifier of the data. 
+	 */
+	public void addAction(ActionDataType dataType, ChangesetAction action, long id) {
+		if (capabilityChecker.isActionSupported()) {
+			jdbcTemplate.update(SQL_INSERT, dataType.getDatabaseValue(), action.getDatabaseValue(), id);
+		}
+	}
+	
+	
+	/**
+	 * Removes all action records.
+	 */
+	public void truncate() {
+		if (capabilityChecker.isActionSupported()) {
+			jdbcTemplate.update(SQL_TRUNCATE);
+		}
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDataType.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDataType.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDataType.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDataType.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java
new file mode 100644
index 0000000..95f3e9e
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java
@@ -0,0 +1,231 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
+import org.springframework.jdbc.core.CallableStatementCreator;
+import org.springframework.jdbc.core.SqlParameter;
+
+
+/**
+ * Writes changes to a database.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeWriter {
+	
+	private DatabaseContext dbCtx;
+	private ActionDao actionDao;
+	private UserDao userDao;
+	private NodeDao nodeDao;
+	private WayDao wayDao;
+	private RelationDao relationDao;
+	private Set<Integer> userSet;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 */
+	public ChangeWriter(DatabaseContext dbCtx) {
+		this.dbCtx = dbCtx;
+		
+		actionDao = new ActionDao(dbCtx);
+		userDao = new UserDao(dbCtx, actionDao);
+		nodeDao = new NodeDao(dbCtx, actionDao);
+		wayDao = new WayDao(dbCtx, actionDao);
+		relationDao = new RelationDao(dbCtx, actionDao);
+		
+		userSet = new HashSet<Integer>();
+	}
+
+
+	/**
+	 * Writes the specified user to the database.
+	 * 
+	 * @param user
+	 *            The user to write.
+	 */
+	private void writeUser(OsmUser user) {
+		// Entities without a user assigned should not be written.
+		if (!OsmUser.NONE.equals(user)) {
+			// Users will only be updated in the database once per changeset
+			// run.
+			if (!userSet.contains(user.getId())) {
+				int userId;
+				OsmUser existingUser;
+
+				userId = user.getId();
+
+				try {
+					existingUser = userDao.getUser(userId);
+
+					if (!user.equals(existingUser)) {
+						userDao.updateUser(user);
+					}
+
+				} catch (NoSuchRecordException e) {
+					userDao.addUser(user);
+				}
+
+				userSet.add(user.getId());
+			}
+		}
+	}
+
+
+	/**
+	 * Performs any validation and pre-processing required for all entity types.
+	 */
+	private void processEntityPrerequisites(Entity entity) {
+		// We can't write an entity with a null timestamp.
+		if (entity.getTimestamp() == null) {
+			throw new OsmosisRuntimeException("Entity(" + entity.getType()
+					+ ") " + entity.getId() + " does not have a timestamp set.");
+		}
+		
+		// Process the user data.
+		writeUser(entity.getUser());
+	}
+
+
+	/**
+	 * Writes the specified node change to the database.
+	 * 
+	 * @param node
+	 *            The node to be written.
+	 * @param action
+	 *            The change to be applied.
+	 */
+	public void write(Node node, ChangeAction action) {
+		processEntityPrerequisites(node);
+
+		// If this is a create or modify, we must create or modify the records
+		// in the database. Note that we don't use the input source to
+		// distinguish between create and modify, we make this determination
+		// based on our current data set.
+		if (ChangeAction.Create.equals(action)
+				|| ChangeAction.Modify.equals(action)) {
+			if (nodeDao.exists(node.getId())) {
+				nodeDao.modifyEntity(node);
+			} else {
+				nodeDao.addEntity(node);
+			}
+
+		} else {
+			// Remove the node from the database.
+			nodeDao.removeEntity(node.getId());
+		}
+	}
+
+
+	/**
+	 * Writes the specified way change to the database.
+	 * 
+	 * @param way
+	 *            The way to be written.
+	 * @param action
+	 *            The change to be applied.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public void write(Way way, ChangeAction action, boolean keepInvalidWays) {
+		processEntityPrerequisites(way);
+		
+		// If this is a create or modify, we must create or modify the records
+		// in the database. Note that we don't use the input source to
+		// distinguish between create and modify, we make this determination
+		// based on our current data set.
+		if (ChangeAction.Create.equals(action) || ChangeAction.Modify.equals(action)) {
+			if (wayDao.exists(way.getId())) {
+				if (way.getWayNodes().size() >= 2 || keepInvalidWays) {
+					wayDao.modifyEntity(way);
+				} else {
+					wayDao.removeEntity(way.getId());
+				}
+				
+			} else {
+				if (way.getWayNodes().size() >= 2 || keepInvalidWays) {
+					wayDao.addEntity(way);
+				}
+			}
+
+		} else {
+			// Remove the way from the database.
+			wayDao.removeEntity(way.getId());
+		}
+	}
+
+
+	/**
+	 * Writes the specified relation change to the database.
+	 * 
+	 * @param relation
+	 *            The relation to be written.
+	 * @param action
+	 *            The change to be applied.
+	 */
+	public void write(Relation relation, ChangeAction action) {
+		processEntityPrerequisites(relation);
+
+		// If this is a create or modify, we must create or modify the records
+		// in the database. Note that we don't use the input source to
+		// distinguish between create and modify, we make this determination
+		// based on our current data set.
+		if (ChangeAction.Create.equals(action)
+				|| ChangeAction.Modify.equals(action)) {
+			if (relationDao.exists(relation.getId())) {
+				relationDao.modifyEntity(relation);
+			} else {
+				relationDao.addEntity(relation);
+			}
+
+		} else {
+			// Remove the relation from the database.
+			relationDao.removeEntity(relation.getId());
+		}
+	}
+
+
+	/**
+	 * Performs post-change database updates.
+	 */
+	public void complete() {
+		dbCtx.getJdbcTemplate().call(
+				new CallableStatementCreator() {
+					@Override
+					public CallableStatement createCallableStatement(Connection con) throws SQLException {
+						return con.prepareCall("{call osmosisUpdate()}");
+					}
+				}, new ArrayList<SqlParameter>());
+		
+		// Clear all action records.
+		actionDao.truncate();
+	}
+
+
+	/**
+	 * Releases all resources.
+	 */
+	public void release() {
+		// Nothing to do.
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangesetAction.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangesetAction.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangesetAction.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangesetAction.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFileset.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFileset.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFileset.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFileset.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java
new file mode 100644
index 0000000..f67b6a6
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java
@@ -0,0 +1,265 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
+import org.openstreetmap.osmosis.pgsnapshot.common.CopyFileWriter;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsnapshot.common.PointBuilder;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.hstore.PGHStore;
+
+
+/**
+ * An OSM data sink for storing all data to a set of database dump files. These
+ * files can be used for populating an empty database.
+ * 
+ * @author Brett Henderson
+ */
+public class CopyFilesetBuilder implements Sink, EntityProcessor {
+	
+	private boolean enableBboxBuilder;
+	private boolean enableLinestringBuilder;
+	private boolean keepInvalidWays;
+	private WayGeometryBuilder wayGeometryBuilder;
+	private CompletableContainer writerContainer;
+	private MemberTypeValueMapper memberTypeValueMapper;
+	private CopyFileWriter userWriter;
+	private CopyFileWriter nodeWriter;
+	private CopyFileWriter wayWriter;
+	private CopyFileWriter wayNodeWriter;
+	private CopyFileWriter relationWriter;
+	private CopyFileWriter relationMemberWriter;
+	private PointBuilder pointBuilder;
+	private Set<Integer> userSet;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param copyFileset
+	 *            The set of COPY files to be populated.
+	 * @param enableBboxBuilder
+	 *            If true, the way bbox geometry is built during processing
+	 *            instead of relying on the database to build them after import.
+	 *            This increases processing but is faster than relying on the
+	 *            database.
+	 * @param enableLinestringBuilder
+	 *            If true, the way linestring geometry is built during
+	 *            processing instead of relying on the database to build them
+	 *            after import. This increases processing but is faster than
+	 *            relying on the database.
+	 * @param storeType
+	 *            The node location storage type used by the geometry builders.
+	 * @param keepInvalidWays
+	 *            If true, zero and single node ways are kept. Otherwise they are
+	 *            silently dropped to avoid putting invalid geometries into the 
+	 *            database which can cause problems with postgis functions.
+	 */
+	public CopyFilesetBuilder(
+			CopyFileset copyFileset, boolean enableBboxBuilder,
+			boolean enableLinestringBuilder, NodeLocationStoreType storeType,
+			boolean keepInvalidWays) {
+		this.enableBboxBuilder = enableBboxBuilder;
+		this.enableLinestringBuilder = enableLinestringBuilder;
+		this.keepInvalidWays = keepInvalidWays;
+		
+		writerContainer = new CompletableContainer();
+		
+		userWriter = writerContainer.add(new CopyFileWriter(copyFileset.getUserFile()));
+		nodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeFile()));
+		wayWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayFile()));
+		wayNodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayNodeFile()));
+		relationWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationFile()));
+		relationMemberWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationMemberFile()));
+		
+		pointBuilder = new PointBuilder();
+		wayGeometryBuilder = new WayGeometryBuilder(storeType);
+		memberTypeValueMapper = new MemberTypeValueMapper();
+		memberTypeValueMapper = new MemberTypeValueMapper();
+		
+		userSet = new HashSet<Integer>();
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		OsmUser user;
+		
+		// Write a user entry if the user doesn't already exist.
+		user = entityContainer.getEntity().getUser();
+		if (!user.equals(OsmUser.NONE)) {
+			if (!userSet.contains(user.getId())) {
+				userWriter.writeField(user.getId());
+				userWriter.writeField(user.getName());
+				userWriter.endRecord();
+				
+				userSet.add(user.getId());
+			}
+		}
+		
+		// Process the entity itself.
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// Do nothing.
+	}
+	
+	
+	private PGHStore buildTags(Entity entity) {
+		PGHStore tags;
+		
+		tags = new PGHStore();
+		for (Tag tag : entity.getTags()) {
+			tags.put(tag.getKey(), tag.getValue());
+		}
+		
+		return tags;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer nodeContainer) {
+		Node node;
+		
+		node = nodeContainer.getEntity();
+		
+		nodeWriter.writeField(node.getId());
+		nodeWriter.writeField(node.getVersion());
+		nodeWriter.writeField(node.getUser().getId());
+		nodeWriter.writeField(node.getTimestamp());
+		nodeWriter.writeField(node.getChangesetId());
+		nodeWriter.writeField(buildTags(node));
+		nodeWriter.writeField(pointBuilder.createPoint(node.getLatitude(), node.getLongitude()));
+		nodeWriter.endRecord();
+		
+		if (enableBboxBuilder || enableLinestringBuilder) {
+			wayGeometryBuilder.addNodeLocation(node);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer wayContainer) {
+		Way way;
+		int sequenceId;
+		List<Long> nodeIds;
+		
+		way = wayContainer.getEntity();
+		
+		nodeIds = new ArrayList<Long>(way.getWayNodes().size());
+		for (WayNode wayNode : way.getWayNodes()) {
+			nodeIds.add(wayNode.getNodeId());
+		}
+		
+		// Keep invalid ways out of the database if desired by the user
+		if (way.getWayNodes().size() > 1 || keepInvalidWays) {
+			wayWriter.writeField(way.getId());
+			wayWriter.writeField(way.getVersion());
+			wayWriter.writeField(way.getUser().getId());
+			wayWriter.writeField(way.getTimestamp());
+			wayWriter.writeField(way.getChangesetId());
+			wayWriter.writeField(buildTags(way));
+			wayWriter.writeField(nodeIds);
+			if (enableBboxBuilder) {
+				wayWriter.writeField(wayGeometryBuilder.createWayBbox(way));
+			}
+			if (enableLinestringBuilder) {
+				wayWriter.writeField(wayGeometryBuilder.createWayLinestring(way));
+			}
+			wayWriter.endRecord();
+			
+			sequenceId = 0;
+			for (WayNode wayNode : way.getWayNodes()) {
+				wayNodeWriter.writeField(way.getId());
+				wayNodeWriter.writeField(wayNode.getNodeId());
+				wayNodeWriter.writeField(sequenceId++);
+				wayNodeWriter.endRecord();
+			}
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer relationContainer) {
+		Relation relation;
+		int memberSequenceId;
+		
+		relation = relationContainer.getEntity();
+		
+		relationWriter.writeField(relation.getId());
+		relationWriter.writeField(relation.getVersion());
+		relationWriter.writeField(relation.getUser().getId());
+		relationWriter.writeField(relation.getTimestamp());
+		relationWriter.writeField(relation.getChangesetId());
+		relationWriter.writeField(buildTags(relation));
+		relationWriter.endRecord();
+		
+		memberSequenceId = 0;
+		for (RelationMember member : relation.getMembers()) {
+			relationMemberWriter.writeField(relation.getId());
+			relationMemberWriter.writeField(member.getMemberId());
+			relationMemberWriter.writeField(memberTypeValueMapper.getMemberType(member.getMemberType()));
+			relationMemberWriter.writeField(member.getMemberRole());
+			relationMemberWriter.writeField(memberSequenceId++);
+			relationMemberWriter.endRecord();
+		}
+	}
+	
+	
+	/**
+	 * Writes any buffered data to the database and commits. 
+	 */
+	public void complete() {
+		writerContainer.complete();
+	}
+	
+	
+	/**
+	 * Releases all resources.
+	 */
+	public void release() {
+		writerContainer.release();
+		wayGeometryBuilder.release();
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java
new file mode 100644
index 0000000..e7347b6
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java
@@ -0,0 +1,127 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.PostgreSqlVersionConstants;
+
+
+/**
+ * Loads a COPY fileset into a database.
+ * 
+ * @author Brett Henderson
+ */
+public class CopyFilesetLoader implements Runnable {
+	
+	private static final Logger LOG = Logger.getLogger(CopyFilesetLoader.class.getName());
+	
+	
+	private static String[] appendColumn(String[] columns, String newColumn) {
+		String[] result;
+		
+		result = new String[columns.length + 1];
+		
+		System.arraycopy(columns, 0, result, 0, columns.length);
+		result[columns.length] = newColumn;
+		
+		return result;
+	}
+	
+	
+	private static final String[] COMMON_COLUMNS = {"id", "version", "user_id", "tstamp", "changeset_id", "tags"};
+	private static final String[] NODE_COLUMNS = appendColumn(COMMON_COLUMNS, "geom");
+	private static final String[] WAY_COLUMNS = appendColumn(COMMON_COLUMNS, "nodes");
+	private static final String[] RELATION_COLUMNS = COMMON_COLUMNS;
+	
+	
+	private DatabaseLoginCredentials loginCredentials;
+	private DatabasePreferences preferences;
+	private CopyFileset copyFileset;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 * @param copyFileset
+	 *            The set of COPY files to be loaded into the database.
+	 */
+	public CopyFilesetLoader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
+			CopyFileset copyFileset) {
+		this.loginCredentials = loginCredentials;
+		this.preferences = preferences;
+		this.copyFileset = copyFileset;
+	}
+    
+
+    /**
+     * Reads all data from the database and send it to the sink.
+     */
+    public void run() {
+    	DatabaseContext dbCtx = new DatabaseContext(loginCredentials);
+    	
+    	try {
+    		DatabaseCapabilityChecker capabilityChecker;
+			IndexManager indexManager;
+			String[] wayColumns;
+			
+			dbCtx.beginTransaction();
+			
+			capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+			new SchemaVersionValidator(dbCtx.getJdbcTemplate(), preferences)
+				.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
+			
+			wayColumns = WAY_COLUMNS;
+			if (capabilityChecker.isWayBboxSupported()) {
+				wayColumns = appendColumn(wayColumns, "bbox");
+			}
+			if (capabilityChecker.isWayLinestringSupported()) {
+				wayColumns = appendColumn(wayColumns, "linestring");
+			}
+    		
+    		indexManager = new IndexManager(dbCtx, false, false);
+    		
+			// Drop all constraints and indexes.
+			indexManager.prepareForLoad();
+    		
+    		LOG.finer("Loading users.");
+    		dbCtx.loadCopyFile(copyFileset.getUserFile(), "users");
+    		LOG.finer("Loading nodes.");
+    		dbCtx.loadCopyFile(copyFileset.getNodeFile(), "nodes", NODE_COLUMNS);
+    		LOG.finer("Loading ways.");
+    		dbCtx.loadCopyFile(copyFileset.getWayFile(), "ways", wayColumns);
+    		LOG.finer("Loading way nodes.");
+    		dbCtx.loadCopyFile(copyFileset.getWayNodeFile(), "way_nodes");
+    		LOG.finer("Loading relations.");
+    		dbCtx.loadCopyFile(copyFileset.getRelationFile(), "relations", RELATION_COLUMNS);
+    		LOG.finer("Loading relation members.");
+    		dbCtx.loadCopyFile(copyFileset.getRelationMemberFile(), "relation_members");
+    		LOG.finer("Committing changes.");
+    		
+    		LOG.fine("Data load complete.");
+    		
+    		// Add all constraints and indexes.
+    		indexManager.completeAfterLoad();
+    		
+    		dbCtx.commitTransaction();
+    		
+    		LOG.fine("Clustering database.");
+    		dbCtx.getJdbcTemplate().update("CLUSTER");
+    		
+    		LOG.fine("Vacuuming database.");
+    		dbCtx.getJdbcTemplate().update("VACUUM ANALYZE");
+    		
+    		LOG.fine("Complete.");
+    		
+    	} finally {
+    		dbCtx.release();
+    	}
+    }
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DatabaseCapabilityChecker.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DatabaseCapabilityChecker.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DatabaseCapabilityChecker.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DatabaseCapabilityChecker.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DbOrderedFeatureComparator.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DbOrderedFeatureComparator.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DbOrderedFeatureComparator.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DbOrderedFeatureComparator.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DirectoryCopyFileset.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DirectoryCopyFileset.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DirectoryCopyFileset.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/DirectoryCopyFileset.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java
new file mode 100644
index 0000000..54b7c95
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java
@@ -0,0 +1,258 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.database.FeaturePopulator;
+import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntitySubClassComparator;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
+import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
+import org.openstreetmap.osmosis.pgsnapshot.common.RowMapperRowCallbackListener;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+
+/**
+ * Provides functionality common to all top level entity daos.
+ * 
+ * @author Brett Henderson
+ * @param <T>
+ *            The entity type to be supported.
+ */
+public abstract class EntityDao<T extends Entity> {
+	
+	private JdbcTemplate jdbcTemplate;
+	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+	private ActionDao actionDao;
+	private EntityMapper<T> entityMapper;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param jdbcTemplate
+	 *            Provides access to the database.
+	 * @param entityMapper
+	 *            Provides entity type specific JDBC support.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	protected EntityDao(JdbcTemplate jdbcTemplate, EntityMapper<T> entityMapper, ActionDao actionDao) {
+		this.jdbcTemplate = jdbcTemplate;
+		this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+		this.entityMapper = entityMapper;
+		this.actionDao = actionDao;
+	}
+	
+	
+	/**
+	 * Gets the entity mapper implementation.
+	 * 
+	 * @return The entity mapper.
+	 */
+	protected EntityMapper<T> getEntityMapper() {
+		return entityMapper;
+	}
+	
+	
+	/**
+	 * Checks if the specified entity exists in the database.
+	 * 
+	 * @param entityId
+	 *            The unique identifier of the entity.
+	 * @return True if the entity exists in the database.
+	 */
+	public boolean exists(long entityId) {
+		return jdbcTemplate.queryForInt(entityMapper.getSqlSelectCount(true), entityId) > 0;
+	}
+	
+	
+	/**
+	 * Loads the specified entity from the database.
+	 * 
+	 * @param entityId
+	 *            The unique identifier of the entity.
+	 * @return The loaded entity.
+	 */
+	public T getEntity(long entityId) {
+		T entity;
+		
+		try {
+			entity = jdbcTemplate.queryForObject(entityMapper.getSqlSelect(true, false), entityMapper.getRowMapper(),
+					entityId);
+		} catch (EmptyResultDataAccessException e) {
+			throw new NoSuchRecordException(entityMapper.getEntityName()
+					+ " " + entityId + " doesn't exist.", e);
+		}
+		
+		return entity;
+	}
+	
+	
+	/**
+	 * Adds the specified entity to the database.
+	 * 
+	 * @param entity
+	 *            The entity to add.
+	 */
+	public void addEntity(T entity) {
+		Map<String, Object> args;
+		
+		args = new HashMap<String, Object>();
+		entityMapper.populateEntityParameters(args, entity);
+		
+		namedParameterJdbcTemplate.update(entityMapper.getSqlInsert(1), args);
+		
+		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.CREATE, entity.getId());
+	}
+	
+	
+	/**
+	 * Updates the specified entity details in the database.
+	 * 
+	 * @param entity
+	 *            The entity to update.
+	 */
+	public void modifyEntity(T entity) {
+		Map<String, Object> args;
+		
+		args = new HashMap<String, Object>();
+		entityMapper.populateEntityParameters(args, entity);
+		
+		namedParameterJdbcTemplate.update(entityMapper.getSqlUpdate(true), args);
+		
+		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.MODIFY, entity.getId());
+	}
+	
+	
+	/**
+	 * Removes the specified entity from the database.
+	 * 
+	 * @param entityId
+	 *            The id of the entity to remove.
+	 */
+	public void removeEntity(long entityId) {
+		Map<String, Object> args;
+		
+		args = new HashMap<String, Object>();
+		args.put("id", entityId);
+		
+		namedParameterJdbcTemplate.update(entityMapper.getSqlDelete(true), args);
+		
+		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.DELETE, entityId);
+	}
+	
+	
+	private ReleasableIterator<T> getFeaturelessEntity(String tablePrefix) {
+		FileBasedSort<T> sortingStore;
+		
+		sortingStore =
+			new FileBasedSort<T>(
+				new SingleClassObjectSerializationFactory(entityMapper.getEntityClass()),
+				new EntitySubClassComparator<T>(new EntityByTypeThenIdComparator()), true);
+		
+		try {
+			String sql;
+			SortingStoreRowMapperListener<T> storeListener;
+			RowMapperRowCallbackListener<T> rowCallbackListener;
+			ReleasableIterator<T> resultIterator;
+			
+			sql = entityMapper.getSqlSelect(tablePrefix, false, false);
+			
+			// Sends all received data into the object store.
+			storeListener = new SortingStoreRowMapperListener<T>(sortingStore);
+			// Converts result set rows into objects and passes them into the store.
+			rowCallbackListener = new RowMapperRowCallbackListener<T>(entityMapper.getRowMapper(), storeListener);
+			
+			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
+			jdbcTemplate.query(sql, rowCallbackListener);
+			
+			// Open a iterator on the store that will release the store upon completion.
+			resultIterator = new StoreReleasingIterator<T>(sortingStore.iterate(), sortingStore);
+			
+			// The store itself shouldn't be released now that it has been attached to the iterator.
+			sortingStore = null;
+			
+			return resultIterator;
+			
+		} finally {
+			if (sortingStore != null) {
+				sortingStore.release();
+			}
+		}
+	}
+	
+	
+	/**
+	 * Gets the feature populators for the entity type.
+	 * 
+	 * @param tablePrefix
+	 *            The prefix for the entity table name. This allows another table to be queried if
+	 *            necessary such as a temporary results table.
+	 * @return The feature populators.
+	 */
+	protected abstract List<FeaturePopulator<T>> getFeaturePopulators(String tablePrefix);
+	
+	
+	/**
+	 * Returns an iterator providing access to all entities in the database.
+	 * 
+	 * @param tablePrefix
+	 *            The prefix for the entity table name. This allows another table to be queried if
+	 *            necessary such as a temporary results table.
+	 * @return The entity iterator.
+	 */
+	public ReleasableIterator<T> iterate(String tablePrefix) {
+		ReleasableContainer releasableContainer;
+		
+		releasableContainer = new ReleasableContainer();
+		
+		try {
+			ReleasableIterator<T> entityIterator;
+			List<FeaturePopulator<T>> featurePopulators;
+			
+			// Create the featureless entity iterator but also store it temporarily in the
+			// releasable container so that it will get freed if we fail during retrieval of feature
+			// populators.
+			entityIterator = releasableContainer.add(getFeaturelessEntity(tablePrefix));
+			
+			// Retrieve the feature populators also adding them to the temporary releasable container.
+			featurePopulators = getFeaturePopulators(tablePrefix);
+			for (FeaturePopulator<T> featurePopulator : featurePopulators) {
+				releasableContainer.add(featurePopulator);
+			}
+			
+			// Build an entity reader capable of merging all sources together.
+			entityIterator = new EntityReader<T>(entityIterator, featurePopulators);
+			
+			// The sources are now all attached to the history reader so we don't want to release
+			// them in the finally block.
+			releasableContainer.clear();
+			
+			return entityIterator;
+			
+		} finally {
+			releasableContainer.release();
+		}
+	}
+	
+	
+	/**
+	 * Returns an iterator providing access to all entities in the database.
+	 * 
+	 * @return The entity iterator.
+	 */
+	public ReleasableIterator<T> iterate() {
+		return iterate("");
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java
new file mode 100644
index 0000000..d6d0e38
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java
@@ -0,0 +1,108 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.database.DbFeature;
+import org.openstreetmap.osmosis.core.store.Storeable;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+
+/**
+ * Provides functionality common to all entity feature daos.
+ * 
+ * @author Brett Henderson
+ * @param <Tef>
+ *            The entity feature type to be supported.
+ * @param <Tdb>
+ *            The entity feature database wrapper type to be used.
+ */
+public class EntityFeatureDao<Tef extends Storeable, Tdb extends DbFeature<Tef>> {
+	private EntityFeatureMapper<Tdb> entityFeatureMapper;
+	private JdbcTemplate jdbcTemplate;
+	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param jdbcTemplate
+	 *            Provides access to the database.
+	 * @param entityFeatureMapper
+	 *            Provides entity type specific JDBC support.
+	 */
+	protected EntityFeatureDao(JdbcTemplate jdbcTemplate, EntityFeatureMapper<Tdb> entityFeatureMapper) {
+		this.jdbcTemplate = jdbcTemplate;
+		this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+		
+		this.entityFeatureMapper = entityFeatureMapper;
+	}
+	
+	
+	/**
+	 * Loads all instances of this feature for the specified entity from the database.
+	 * 
+	 * @param entityId
+	 *            The unique identifier of the entity.
+	 * @return All instances of this feature type for the entity.
+	 */
+	public Collection<Tdb> getAll(long entityId) {
+		return jdbcTemplate.query(entityFeatureMapper.getSqlSelect("", true, true), entityFeatureMapper.getRowMapper());
+	}
+	
+	
+	/**
+	 * Loads all instances of this feature for the specified entity from the database.
+	 * 
+	 * @param entityId
+	 *            The unique identifier of the entity.
+	 * @return All instances of this feature type for the entity.
+	 */
+	public Collection<Tef> getAllRaw(long entityId) {
+		Collection<Tdb> dbFeatures;
+		Collection<Tef> rawFeatures;
+		
+		dbFeatures = getAll(entityId);
+		rawFeatures = new ArrayList<Tef>(dbFeatures.size());
+		for (Tdb dbFeature : dbFeatures) {
+			rawFeatures.add(dbFeature.getFeature());
+		}
+		
+		return rawFeatures;
+	}
+	
+	
+	/**
+	 * Adds the specified features to the database.
+	 * 
+	 * @param features
+	 *            The features to add.
+	 */
+	public void addAll(Collection<Tdb> features) {
+		Map<String, Object> args;
+		
+		args = new HashMap<String, Object>();
+		
+		for (Tdb feature : features) {
+			args.clear();
+			entityFeatureMapper.populateParameters(args, feature);
+			
+			namedParameterJdbcTemplate.update(entityFeatureMapper.getSqlInsert(1), args);
+		}
+	}
+	
+	
+	/**
+	 * Removes the specified feature list from the database.
+	 * 
+	 * @param entityId
+	 *            The id of the entity to remove.
+	 */
+	public void removeList(long entityId) {
+		jdbcTemplate.update(entityFeatureMapper.getSqlDelete(true), entityId);
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityReader.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityReader.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityReader.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityReader.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityRowMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/FeaturePopulatorImpl.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/FeaturePopulatorImpl.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/FeaturePopulatorImpl.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/FeaturePopulatorImpl.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java
new file mode 100644
index 0000000..b87657f
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java
@@ -0,0 +1,155 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Drops and creates indexes in support of bulk load activities.
+ * 
+ * @author Brett Henderson
+ */
+public class IndexManager {
+	
+	private static final Logger LOG = Logger.getLogger(IndexManager.class.getName());
+	
+	
+	private static final String[] PRE_LOAD_SQL = {
+		"ALTER TABLE users DROP CONSTRAINT pk_users",
+		"ALTER TABLE nodes DROP CONSTRAINT pk_nodes",
+		"ALTER TABLE ways DROP CONSTRAINT pk_ways",
+		"ALTER TABLE way_nodes DROP CONSTRAINT pk_way_nodes",
+		"ALTER TABLE relations DROP CONSTRAINT pk_relations",
+		"ALTER TABLE relation_members DROP CONSTRAINT pk_relation_members",
+		"DROP INDEX idx_nodes_geom",
+		"DROP INDEX idx_way_nodes_node_id",
+		"DROP INDEX idx_relation_members_member_id_and_type"
+	};
+	private static final String[] PRE_LOAD_SQL_WAY_BBOX = {
+		"DROP INDEX idx_ways_bbox"
+	};
+	private static final String[] PRE_LOAD_SQL_WAY_LINESTRING = {
+		"DROP INDEX idx_ways_linestring"
+	};
+	
+	private static final String[] POST_LOAD_SQL = {
+		"ALTER TABLE ONLY users ADD CONSTRAINT pk_users PRIMARY KEY (id)",
+		"ALTER TABLE ONLY nodes ADD CONSTRAINT pk_nodes PRIMARY KEY (id)",
+		"ALTER TABLE ONLY ways ADD CONSTRAINT pk_ways PRIMARY KEY (id)",
+		"ALTER TABLE ONLY way_nodes ADD CONSTRAINT pk_way_nodes PRIMARY KEY (way_id, sequence_id)",
+		"ALTER TABLE ONLY relations ADD CONSTRAINT pk_relations PRIMARY KEY (id)",
+		"ALTER TABLE ONLY relation_members ADD CONSTRAINT pk_relation_members PRIMARY KEY (relation_id, sequence_id)",
+		"CREATE INDEX idx_nodes_geom ON nodes USING gist (geom)",
+		"CREATE INDEX idx_way_nodes_node_id ON way_nodes USING btree (node_id)",
+		"CREATE INDEX idx_relation_members_member_id_and_type ON relation_members USING btree (member_id, member_type)"
+	};
+	private static final String[] POST_LOAD_SQL_WAY_BBOX = {
+		"CREATE INDEX idx_ways_bbox ON ways USING gist (bbox)"
+	};
+	private static final String[] POST_LOAD_SQL_WAY_LINESTRING = {
+		"CREATE INDEX idx_ways_linestring ON ways USING gist (linestring)"
+	};
+	private static final String POST_LOAD_SQL_POPULATE_WAY_BBOX =
+		"UPDATE ways SET bbox = ("
+		+ "SELECT ST_Envelope(ST_Collect(geom)) FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
+		+ " WHERE way_nodes.way_id = ways.id"
+		+ ")";
+	private static final String POST_LOAD_SQL_POPULATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ "SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ "SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ ") c"
+		+ ")";
+	
+	
+	private JdbcTemplate jdbcTemplate;
+	private DatabaseCapabilityChecker capabilityChecker;
+	private boolean populateBbox;
+	private boolean populateLinestring;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            Provides access to the database.
+	 * @param populateBbox
+	 *            If true, the bbox colum on the way table will be populated
+	 *            after load.
+	 * @param populateLinestring
+	 *            If true, the linestring column on the way table will be
+	 *            populated after load.
+	 */
+	public IndexManager(DatabaseContext dbCtx, boolean populateBbox, boolean populateLinestring) {
+		this.populateBbox = populateBbox;
+		this.populateLinestring = populateLinestring;
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+	}
+	
+	
+	/**
+	 * Drops indexes and constraints in the database.
+	 */
+	public void prepareForLoad() {
+		LOG.fine("Running pre-load SQL statements.");
+		for (int i = 0; i < PRE_LOAD_SQL.length; i++) {
+			LOG.finer("SQL: " + PRE_LOAD_SQL[i]);
+			jdbcTemplate.update(PRE_LOAD_SQL[i]);
+		}
+		if (capabilityChecker.isWayBboxSupported()) {
+			LOG.fine("Running pre-load bbox SQL statements.");
+			for (int i = 0; i < PRE_LOAD_SQL_WAY_BBOX.length; i++) {
+				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_BBOX[i]);
+				jdbcTemplate.update(PRE_LOAD_SQL_WAY_BBOX[i]);
+			}
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			LOG.fine("Running pre-load linestring SQL statements.");
+			for (int i = 0; i < PRE_LOAD_SQL_WAY_LINESTRING.length; i++) {
+				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_LINESTRING[i]);
+				jdbcTemplate.update(PRE_LOAD_SQL_WAY_LINESTRING[i]);
+			}
+		}
+		LOG.fine("Pre-load SQL statements complete.");
+	}
+	
+	
+	/**
+	 * Creates indexes in the database and populates derived columns.
+	 */
+	public void completeAfterLoad() {
+		LOG.fine("Running post-load SQL.");
+		for (int i = 0; i < POST_LOAD_SQL.length; i++) {
+			LOG.finer("SQL: " + POST_LOAD_SQL[i]);
+			jdbcTemplate.update(POST_LOAD_SQL[i]);
+		}
+		if (capabilityChecker.isWayBboxSupported()) {
+			LOG.fine("Running post-load bbox SQL statements.");
+			if (populateBbox) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_BBOX);
+				jdbcTemplate.update(POST_LOAD_SQL_POPULATE_WAY_BBOX);
+			}
+			for (int i = 0; i < POST_LOAD_SQL_WAY_BBOX.length; i++) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_BBOX[i]);
+				jdbcTemplate.update(POST_LOAD_SQL_WAY_BBOX[i]);
+			}
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			LOG.fine("Running post-load linestring SQL statements.");
+			if (populateLinestring) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
+				jdbcTemplate.update(POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
+			}
+			for (int i = 0; i < POST_LOAD_SQL_WAY_LINESTRING.length; i++) {
+				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_LINESTRING[i]);
+				jdbcTemplate.update(POST_LOAD_SQL_WAY_LINESTRING[i]);
+			}
+		}
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/MemberTypeValueMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/MemberTypeValueMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/MemberTypeValueMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/MemberTypeValueMapper.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java
new file mode 100644
index 0000000..0196907
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java
@@ -0,0 +1,84 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.database.FeaturePopulator;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Performs all node-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class NodeDao extends EntityDao<Node> {
+	private static final String SQL_UPDATE_WAY_BBOX =
+		"UPDATE ways w SET bbox = ("
+		+ " SELECT ST_Envelope(ST_Collect(n.geom))"
+		+ " FROM nodes n INNER JOIN way_nodes wn ON wn.node_id = n.id"
+		+ " WHERE wn.way_id = w.id"
+		+ " )"
+		+ " WHERE w.id IN ("
+		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
+		+ " )";
+	private static final String SQL_UPDATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ " ) c"
+		+ " )"
+		+ " WHERE w.id IN ("
+		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
+		+ " )";
+	
+	
+	private JdbcTemplate jdbcTemplate;
+	private DatabaseCapabilityChecker capabilityChecker;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public NodeDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		super(dbCtx.getJdbcTemplate(), new NodeMapper(), actionDao);
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void modifyEntity(Node entity) {
+		super.modifyEntity(entity);
+		
+		if (capabilityChecker.isWayBboxSupported()) {
+			jdbcTemplate.update(SQL_UPDATE_WAY_BBOX, entity.getId());
+		}
+		
+		if (capabilityChecker.isWayLinestringSupported()) {
+			jdbcTemplate.update(SQL_UPDATE_WAY_LINESTRING, entity.getId());
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected List<FeaturePopulator<Node>> getFeaturePopulators(String tablePrefix) {
+		return Collections.emptyList();
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeRowMapper.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java
new file mode 100644
index 0000000..b573e8c
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java
@@ -0,0 +1,441 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainerIterator;
+import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityManager;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainerIterator;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainerIterator;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainerIterator;
+import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
+import org.openstreetmap.osmosis.core.database.DatabasePreferences;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.MultipleSourceIterator;
+import org.openstreetmap.osmosis.core.store.ReleasableAdaptorForIterator;
+import org.openstreetmap.osmosis.core.store.UpcastIterator;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.PolygonBuilder;
+import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
+import org.openstreetmap.osmosis.pgsnapshot.v0_6.PostgreSqlVersionConstants;
+import org.postgis.PGgeometry;
+import org.postgis.Point;
+import org.postgis.Polygon;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Provides read-only access to a PostgreSQL dataset store. Each thread
+ * accessing the store must create its own reader. It is important that all
+ * iterators obtained from this reader are released before releasing the reader
+ * itself.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlDatasetContext implements DatasetContext {
+	
+	private static final Logger LOG = Logger.getLogger(PostgreSqlDatasetContext.class.getName());
+	
+	
+	private DatabaseLoginCredentials loginCredentials;
+	private DatabasePreferences preferences;
+	private DatabaseCapabilityChecker capabilityChecker;
+	private boolean initialized;
+	private DatabaseContext dbCtx;
+	private JdbcTemplate jdbcTemplate;
+	private UserDao userDao;
+	private NodeDao nodeDao;
+	private WayDao wayDao;
+	private RelationDao relationDao;
+	private PostgreSqlEntityManager<Node> nodeManager;
+	private PostgreSqlEntityManager<Way> wayManager;
+	private PostgreSqlEntityManager<Relation> relationManager;
+	private PolygonBuilder polygonBuilder;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param loginCredentials
+	 *            Contains all information required to connect to the database.
+	 * @param preferences
+	 *            Contains preferences configuring database behaviour.
+	 */
+	public PostgreSqlDatasetContext(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
+		this.loginCredentials = loginCredentials;
+		this.preferences = preferences;
+		
+		polygonBuilder = new PolygonBuilder();
+		
+		initialized = false;
+	}
+	
+	
+	/**
+	 * Initialises the database connection and associated data access objects.
+	 */
+	private void initialize() {
+		if (dbCtx == null) {
+			ActionDao actionDao;
+			
+			dbCtx = new DatabaseContext(loginCredentials);
+			jdbcTemplate = dbCtx.getJdbcTemplate();
+			
+			dbCtx.beginTransaction();
+			
+			new SchemaVersionValidator(jdbcTemplate, preferences).validateVersion(
+					PostgreSqlVersionConstants.SCHEMA_VERSION);
+			
+			capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+			
+			actionDao = new ActionDao(dbCtx);
+			userDao = new UserDao(dbCtx, actionDao);
+			nodeDao = new NodeDao(dbCtx, actionDao);
+			wayDao = new WayDao(dbCtx, actionDao);
+			relationDao = new RelationDao(dbCtx, actionDao);
+			
+			nodeManager = new PostgreSqlEntityManager<Node>(nodeDao, userDao);
+			wayManager = new PostgreSqlEntityManager<Way>(wayDao, userDao);
+			relationManager = new PostgreSqlEntityManager<Relation>(relationDao, userDao);
+		}
+		
+		initialized = true;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	@Deprecated
+	public Node getNode(long id) {
+		return getNodeManager().getEntity(id);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	@Deprecated
+	public Way getWay(long id) {
+		return getWayManager().getEntity(id);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	@Deprecated
+	public Relation getRelation(long id) {
+		return getRelationManager().getEntity(id);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityManager<Node> getNodeManager() {
+		if (!initialized) {
+			initialize();
+		}
+		
+		return nodeManager;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityManager<Way> getWayManager() {
+		if (!initialized) {
+			initialize();
+		}
+		
+		return wayManager;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public EntityManager<Relation> getRelationManager() {
+		if (!initialized) {
+			initialize();
+		}
+		
+		return relationManager;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public ReleasableIterator<EntityContainer> iterate() {
+		List<Bound> bounds;
+		List<ReleasableIterator<EntityContainer>> sources;
+		
+		if (!initialized) {
+			initialize();
+		}
+		
+		// Build the bounds list.
+		bounds = new ArrayList<Bound>();
+		bounds.add(new Bound("Osmosis " + OsmosisConstants.VERSION));
+		
+		sources = new ArrayList<ReleasableIterator<EntityContainer>>();
+		
+		sources.add(new UpcastIterator<EntityContainer, BoundContainer>(
+				new BoundContainerIterator(new ReleasableAdaptorForIterator<Bound>(bounds.iterator()))));
+		sources.add(new UpcastIterator<EntityContainer, NodeContainer>(
+				new NodeContainerIterator(nodeDao.iterate())));
+		sources.add(new UpcastIterator<EntityContainer, WayContainer>(
+				new WayContainerIterator(wayDao.iterate())));
+		sources.add(new UpcastIterator<EntityContainer, RelationContainer>(
+				new RelationContainerIterator(relationDao.iterate())));
+		
+		return new MultipleSourceIterator<EntityContainer>(sources);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public ReleasableIterator<EntityContainer> iterateBoundingBox(
+			double left, double right, double top, double bottom, boolean completeWays) {
+		List<Bound> bounds;
+		Point[] bboxPoints;
+		Polygon bboxPolygon;
+		int rowCount;
+		List<ReleasableIterator<EntityContainer>> resultSets;
+		
+		if (!initialized) {
+			initialize();
+		}
+		
+		// Build the bounds list.
+		bounds = new ArrayList<Bound>();
+		bounds.add(new Bound(right, left, top, bottom, "Osmosis " + OsmosisConstants.VERSION));
+		
+		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
+		// prevent this. Note that this is not recommended practice according to documentation
+		// but fixing this would require modifying the table statistics gathering
+		// configuration to produce better plans.
+		jdbcTemplate.update("SET enable_seqscan = false");
+		jdbcTemplate.update("SET enable_mergejoin = false");
+		jdbcTemplate.update("SET enable_hashjoin = false");
+		
+		// Build a polygon representing the bounding box.
+		// Sample box for query testing may be:
+		// GeomFromText('POLYGON((144.93912192855174 -37.82981987499741,
+		// 144.93912192855174 -37.79310006709244, 144.98188026000003
+		// -37.79310006709244, 144.98188026000003 -37.82981987499741,
+		// 144.93912192855174 -37.82981987499741))', -1)
+		bboxPoints = new Point[5];
+		bboxPoints[0] = new Point(left, bottom);
+		bboxPoints[1] = new Point(left, top);
+		bboxPoints[2] = new Point(right, top);
+		bboxPoints[3] = new Point(right, bottom);
+		bboxPoints[4] = new Point(left, bottom);
+		bboxPolygon = polygonBuilder.createPolygon(bboxPoints);
+		
+		// Select all nodes inside the box into the node temp table.
+		LOG.finer("Selecting all nodes inside bounding box.");
+		rowCount = jdbcTemplate.update(
+				"CREATE TEMPORARY TABLE bbox_nodes ON COMMIT DROP AS"
+				+ " SELECT * FROM nodes WHERE (geom && ?)",
+				new PGgeometry(bboxPolygon));
+		
+		LOG.finer("Adding a primary key to the temporary nodes table.");
+		jdbcTemplate.update("ALTER TABLE ONLY bbox_nodes ADD CONSTRAINT pk_bbox_nodes PRIMARY KEY (id)");
+		
+		LOG.finer("Updating query analyzer statistics on the temporary nodes table.");
+		jdbcTemplate.update("ANALYZE bbox_nodes");
+		
+		// Select all ways inside the bounding box into the way temp table.
+		if (capabilityChecker.isWayLinestringSupported()) {
+			LOG.finer("Selecting all ways inside bounding box using way linestring geometry.");
+			// We have full way geometry available so select ways
+			// overlapping the requested bounding box.
+			rowCount = jdbcTemplate.update(
+					"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
+					+ " SELECT * FROM ways WHERE (linestring && ?)",
+					new PGgeometry(bboxPolygon));
+			
+		} else if (capabilityChecker.isWayBboxSupported()) {
+			LOG.finer("Selecting all ways inside bounding box using dynamically built"
+					+ " way linestring with way bbox indexing.");
+			// The inner query selects the way id and node coordinates for
+			// all ways constrained by the way bounding box which is
+			// indexed.
+			// The middle query converts the way node coordinates into
+			// linestrings.
+			// The outer query constrains the query to the linestrings
+			// inside the bounding box. These aren't indexed but the inner
+			// query way bbox constraint will minimise the unnecessary data.
+			rowCount = jdbcTemplate.update(
+				"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
+					+ " SELECT w.* FROM ("
+					+ "SELECT c.id AS id, First(c.version) AS version, First(c.user_id) AS user_id,"
+					+ " First(c.tstamp) AS tstamp, First(c.changeset_id) AS changeset_id, First(c.tags) AS tags,"
+					+ " First(c.nodes) AS nodes, ST_MakeLine(c.geom) AS way_line FROM ("
+					+ "SELECT w.*, n.geom AS geom FROM nodes n"
+					+ " INNER JOIN way_nodes wn ON n.id = wn.node_id"
+					+ " INNER JOIN ways w ON wn.way_id = w.id"
+					+ " WHERE (w.bbox && ?) ORDER BY wn.way_id, wn.sequence_id"
+					+ ") c "
+					+ "GROUP BY c.id"
+					+ ") w "
+					+ "WHERE (w.way_line && ?)",
+					new PGgeometry(bboxPolygon),
+					new PGgeometry(bboxPolygon)
+			);
+			
+		} else {
+			LOG.finer("Selecting all way ids inside bounding box using already selected nodes.");
+			// No way bbox support is available so select ways containing
+			// the selected nodes.
+			rowCount = jdbcTemplate.update(
+				"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
+					+ " SELECT w.* FROM ways w"
+					+ " INNER JOIN ("
+					+ " SELECT wn.way_id FROM way_nodes wn"
+					+ " INNER JOIN bbox_nodes n ON wn.node_id = n.id GROUP BY wn.way_id"
+					+ ") wids ON w.id = wids.way_id"
+			);
+		}
+		LOG.finer(rowCount + " rows affected.");
+		
+		LOG.finer("Adding a primary key to the temporary ways table.");
+		jdbcTemplate.update("ALTER TABLE ONLY bbox_ways ADD CONSTRAINT pk_bbox_ways PRIMARY KEY (id)");
+		
+		LOG.finer("Updating query analyzer statistics on the temporary ways table.");
+		jdbcTemplate.update("ANALYZE bbox_ways");
+		
+		// Select all relations containing the nodes or ways into the relation table.
+		LOG.finer("Selecting all relation ids containing selected nodes or ways.");
+		rowCount = jdbcTemplate.update(
+			"CREATE TEMPORARY TABLE bbox_relations ON COMMIT DROP AS"
+				+ " SELECT r.* FROM relations r"
+				+ " INNER JOIN ("
+				+ "    SELECT relation_id FROM ("
+				+ "        SELECT rm.relation_id AS relation_id FROM relation_members rm"
+				+ "        INNER JOIN bbox_nodes n ON rm.member_id = n.id WHERE rm.member_type = 'N' "
+				+ "        UNION "
+				+ "        SELECT rm.relation_id AS relation_id FROM relation_members rm"
+				+ "        INNER JOIN bbox_ways w ON rm.member_id = w.id WHERE rm.member_type = 'W'"
+				+ "     ) rids GROUP BY relation_id"
+				+ ") rids ON r.id = rids.relation_id"
+		);
+		LOG.finer(rowCount + " rows affected.");
+		
+		LOG.finer("Adding a primary key to the temporary relations table.");
+		jdbcTemplate.update("ALTER TABLE ONLY bbox_relations ADD CONSTRAINT pk_bbox_relations PRIMARY KEY (id)");
+		
+		LOG.finer("Updating query analyzer statistics on the temporary relations table.");
+		jdbcTemplate.update("ANALYZE bbox_relations");
+		
+		// Include all relations containing the current relations into the
+		// relation table and repeat until no more inclusions occur.
+		do {
+			LOG.finer("Selecting parent relations of selected relations.");
+			rowCount = jdbcTemplate.update(
+				"INSERT INTO bbox_relations "
+					+ "SELECT r.* FROM relations r INNER JOIN ("
+					+ "    SELECT rm.relation_id FROM relation_members rm"
+					+ "    INNER JOIN bbox_relations br ON rm.member_id = br.id"
+					+ "    WHERE rm.member_type = 'R' AND NOT EXISTS ("
+					+ "        SELECT * FROM bbox_relations br2 WHERE rm.relation_id = br2.id"
+					+ "    ) GROUP BY rm.relation_id"
+					+ ") rids ON r.id = rids.relation_id"
+			);
+			LOG.finer(rowCount + " rows affected.");
+		} while (rowCount > 0);
+		
+		LOG.finer("Updating query analyzer statistics on the temporary relations table.");
+		jdbcTemplate.update("ANALYZE bbox_relations");
+		
+		// If complete ways is set, select all nodes contained by the ways into the node temp table.
+		if (completeWays) {
+			LOG.finer("Selecting all nodes for selected ways.");
+			jdbcTemplate.update("CREATE TEMPORARY TABLE bbox_way_nodes (id bigint) ON COMMIT DROP");
+			jdbcTemplate.queryForList("SELECT unnest_bbox_way_nodes()");
+			jdbcTemplate.update(
+					"CREATE TEMPORARY TABLE bbox_missing_way_nodes ON COMMIT DROP AS "
+					+ "SELECT buwn.id FROM (SELECT DISTINCT bwn.id FROM bbox_way_nodes bwn) buwn "
+					+ "WHERE NOT EXISTS ("
+					+ "    SELECT * FROM bbox_nodes WHERE id = buwn.id"
+					+ ");"
+			);
+			jdbcTemplate.update("ALTER TABLE ONLY bbox_missing_way_nodes"
+					+ " ADD CONSTRAINT pk_bbox_missing_way_nodes PRIMARY KEY (id)");
+			jdbcTemplate.update("ANALYZE bbox_missing_way_nodes");
+			rowCount = jdbcTemplate.update("INSERT INTO bbox_nodes "
+					+ "SELECT n.* FROM nodes n INNER JOIN bbox_missing_way_nodes bwn ON n.id = bwn.id;");
+			LOG.finer(rowCount + " rows affected.");
+		}
+		
+		LOG.finer("Updating query analyzer statistics on the temporary nodes table.");
+		jdbcTemplate.update("ANALYZE bbox_nodes");
+		
+		// Create iterators for the selected records for each of the entity types.
+		LOG.finer("Iterating over results.");
+		resultSets = new ArrayList<ReleasableIterator<EntityContainer>>();
+		resultSets.add(
+				new UpcastIterator<EntityContainer, BoundContainer>(
+						new BoundContainerIterator(new ReleasableAdaptorForIterator<Bound>(bounds.iterator()))));
+		resultSets.add(
+				new UpcastIterator<EntityContainer, NodeContainer>(
+						new NodeContainerIterator(nodeDao.iterate("bbox_"))));
+		resultSets.add(
+				new UpcastIterator<EntityContainer, WayContainer>(
+						new WayContainerIterator(wayDao.iterate("bbox_"))));
+		resultSets.add(
+				new UpcastIterator<EntityContainer, RelationContainer>(
+						new RelationContainerIterator(relationDao.iterate("bbox_"))));
+		
+		// Merge all readers into a single result iterator and return.			
+		return new MultipleSourceIterator<EntityContainer>(resultSets);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		dbCtx.commitTransaction();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		if (dbCtx != null) {
+			dbCtx.release();
+			
+			dbCtx = null;
+		}
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlEntityManager.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlEntityManager.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlEntityManager.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlEntityManager.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java
new file mode 100644
index 0000000..de1045a
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java
@@ -0,0 +1,195 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.database.DbFeature;
+import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
+import org.openstreetmap.osmosis.core.database.FeaturePopulator;
+import org.openstreetmap.osmosis.core.database.RelationMemberCollectionLoader;
+import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
+import org.openstreetmap.osmosis.core.store.UpcastIterator;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.RowMapperRowCallbackListener;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Performs all relation-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class RelationDao extends EntityDao<Relation> {
+	
+	private JdbcTemplate jdbcTemplate;
+	private EntityFeatureDao<RelationMember, DbOrderedFeature<RelationMember>> relationMemberDao;
+	private RelationMemberMapper relationMemberMapper;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public RelationDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		super(dbCtx.getJdbcTemplate(), new RelationMapper(), actionDao);
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		relationMemberMapper = new RelationMemberMapper();
+		relationMemberDao = new EntityFeatureDao<RelationMember, DbOrderedFeature<RelationMember>>(
+				dbCtx.getJdbcTemplate(), relationMemberMapper);
+	}
+	
+	
+	private void loadFeatures(long entityId, Relation entity) {
+		entity.getMembers().addAll(relationMemberDao.getAllRaw(entityId));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Relation getEntity(long entityId) {
+		Relation entity;
+		
+		entity = super.getEntity(entityId);
+		
+		loadFeatures(entityId, entity);
+		
+		return entity;
+	}
+	
+	
+	/**
+	 * Adds the specified relation member list to the database.
+	 * 
+	 * @param entityId
+	 *            The identifier of the entity to add these features to.
+	 * @param memberList
+	 *            The list of features to add.
+	 */
+	private void addMembers(long entityId, List<RelationMember> memberList) {
+		List<DbOrderedFeature<RelationMember>> dbList;
+		
+		dbList = new ArrayList<DbOrderedFeature<RelationMember>>(memberList.size());
+
+		for (int i = 0; i < memberList.size(); i++) {
+			dbList.add(new DbOrderedFeature<RelationMember>(entityId, memberList.get(i), i));
+		}
+		
+		relationMemberDao.addAll(dbList);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addEntity(Relation entity) {
+		super.addEntity(entity);
+		
+		addMembers(entity.getId(), entity.getMembers());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void modifyEntity(Relation entity) {
+		long relationId;
+		
+		super.modifyEntity(entity);
+		
+		relationId = entity.getId();
+		relationMemberDao.removeList(relationId);
+		addMembers(entity.getId(), entity.getMembers());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeEntity(long entityId) {
+		relationMemberDao.removeList(entityId);
+		
+		super.removeEntity(entityId);
+	}
+	
+	
+	private ReleasableIterator<DbOrderedFeature<RelationMember>> getRelationMembers(String tablePrefix) {
+		
+		FileBasedSort<DbOrderedFeature<RelationMember>> sortingStore =
+			new FileBasedSort<DbOrderedFeature<RelationMember>>(
+				new SingleClassObjectSerializationFactory(DbOrderedFeature.class),
+				new DbOrderedFeatureComparator<RelationMember>(), true);
+		
+		try {
+			String sql;
+			SortingStoreRowMapperListener<DbOrderedFeature<RelationMember>> storeListener;
+			RowMapperRowCallbackListener<DbOrderedFeature<RelationMember>> rowCallbackListener;
+			ReleasableIterator<DbOrderedFeature<RelationMember>> resultIterator;
+			
+			sql = relationMemberMapper.getSqlSelect(tablePrefix, false, false);
+			
+			// Sends all received data into the object store.
+			storeListener = new SortingStoreRowMapperListener<DbOrderedFeature<RelationMember>>(sortingStore);
+			// Converts result set rows into objects and passes them into the store.
+			rowCallbackListener = new RowMapperRowCallbackListener<DbOrderedFeature<RelationMember>>(
+					relationMemberMapper.getRowMapper(), storeListener);
+			
+			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
+			jdbcTemplate.query(sql, rowCallbackListener);
+			
+			// Open a iterator on the store that will release the store upon completion.
+			resultIterator =
+				new StoreReleasingIterator<DbOrderedFeature<RelationMember>>(sortingStore.iterate(), sortingStore);
+			
+			// The store itself shouldn't be released now that it has been attached to the iterator.
+			sortingStore = null;
+			
+			return resultIterator;
+			
+		} finally {
+			if (sortingStore != null) {
+				sortingStore.release();
+			}
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected List<FeaturePopulator<Relation>> getFeaturePopulators(String tablePrefix) {
+		ReleasableIterator<DbFeature<RelationMember>> relationMemberIterator;
+		List<FeaturePopulator<Relation>> featurePopulators;
+		
+		featurePopulators = new ArrayList<FeaturePopulator<Relation>>();
+		
+		// Get the way nodes for the selected entities.
+		relationMemberIterator = new UpcastIterator<DbFeature<RelationMember>, DbOrderedFeature<RelationMember>>(
+				getRelationMembers(tablePrefix));
+		
+		// Wrap the way node source into a feature populator that can attach them to their
+		// owning ways.
+		featurePopulators.add(
+				new FeaturePopulatorImpl<Relation, RelationMember, DbFeature<RelationMember>>(
+						relationMemberIterator, new RelationMemberCollectionLoader()));
+		
+		return featurePopulators;
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationMemberRowMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationRowMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/TempCopyFileset.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/TempCopyFileset.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/TempCopyFileset.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/TempCopyFileset.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java
new file mode 100644
index 0000000..744792a
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java
@@ -0,0 +1,87 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Performs all user-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class UserDao {
+	private static final String SELECT_USER = "SELECT id, name FROM users WHERE id = ?";
+	private static final String INSERT_USER = "INSERT INTO users(id, name) VALUES(?, ?)";
+	private static final String UPDATE_USER = "UPDATE users SET name = ? WHERE id = ?";
+	
+	private JdbcTemplate jdbcTemplate;
+	private ActionDao actionDao;
+	private UserRowMapper rowMapper;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public UserDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		this.actionDao = actionDao;
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		
+		rowMapper = new UserRowMapper();
+	}
+	
+	
+	/**
+	 * Loads the specified way from the database.
+	 * 
+	 * @param userId
+	 *            The unique identifier of the user.
+	 * @return The loaded user.
+	 */
+	public OsmUser getUser(long userId) {
+		OsmUser user;
+		
+		try {
+			user = jdbcTemplate.queryForObject(SELECT_USER, rowMapper, userId);
+		} catch (EmptyResultDataAccessException e) {
+			throw new NoSuchRecordException("User " + userId + " doesn't exist.", e);
+		}
+		
+		return user;
+	}
+	
+	
+	/**
+	 * Adds the specified user to the database.
+	 * 
+	 * @param user
+	 *            The user to add.
+	 */
+	public void addUser(OsmUser user) {
+		jdbcTemplate.update(INSERT_USER, user.getId(), user.getName());
+		
+		actionDao.addAction(ActionDataType.USER, ChangesetAction.CREATE, user.getId());
+	}
+	
+	
+	/**
+	 * Updates the specified user record in the database.
+	 * 
+	 * @param user
+	 *            The user to update.
+	 */
+	public void updateUser(OsmUser user) {
+		jdbcTemplate.update(UPDATE_USER, user.getName(), user.getId());
+		
+		actionDao.addAction(ActionDataType.USER, ChangesetAction.MODIFY, user.getId());
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserRowMapper.java
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java
new file mode 100644
index 0000000..8a7b213
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java
@@ -0,0 +1,168 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
+import org.openstreetmap.osmosis.core.database.FeaturePopulator;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+/**
+ * Performs all way-specific db operations.
+ * 
+ * @author Brett Henderson
+ */
+public class WayDao extends EntityDao<Way> {
+	
+	private static final String SQL_UPDATE_WAY_BBOX =
+		"UPDATE ways SET bbox = ("
+		+ " SELECT ST_Envelope(ST_Collect(geom))"
+		+ " FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
+		+ " WHERE way_nodes.way_id = ways.id"
+		+ " )"
+		+ " WHERE ways.id = ?";
+	private static final String SQL_UPDATE_WAY_LINESTRING =
+		"UPDATE ways w SET linestring = ("
+		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
+		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
+		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
+		+ " ) c"
+		+ " )"
+		+ " WHERE w.id  = ?";
+	
+	private JdbcTemplate jdbcTemplate;
+	private DatabaseCapabilityChecker capabilityChecker;
+	private EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>> wayNodeDao;
+	private WayNodeMapper wayNodeMapper;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dbCtx
+	 *            The database context to use for accessing the database.
+	 * @param actionDao
+	 *            The dao to use for adding action records to the database.
+	 */
+	public WayDao(DatabaseContext dbCtx, ActionDao actionDao) {
+		super(dbCtx.getJdbcTemplate(), new WayMapper(), actionDao);
+		
+		jdbcTemplate = dbCtx.getJdbcTemplate();
+		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
+		wayNodeMapper = new WayNodeMapper();
+		wayNodeDao = new EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>>(jdbcTemplate, wayNodeMapper);
+	}
+	
+	
+	private void loadFeatures(long entityId, Way entity) {
+		entity.getWayNodes().addAll(wayNodeDao.getAllRaw(entityId));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Way getEntity(long entityId) {
+		Way entity;
+		
+		entity = super.getEntity(entityId);
+		
+		loadFeatures(entityId, entity);
+		
+		return entity;
+	}
+
+
+	/**
+	 * Adds the specified way node list to the database.
+	 * 
+	 * @param entityId
+	 *            The identifier of the entity to add these features to.
+	 * @param wayNodeList
+	 *            The list of features to add.
+	 */
+	private void addWayNodeList(long entityId, List<WayNode> wayNodeList) {
+		List<DbOrderedFeature<WayNode>> dbList;
+		
+		dbList = new ArrayList<DbOrderedFeature<WayNode>>(wayNodeList.size());
+		
+		for (int i = 0; i < wayNodeList.size(); i++) {
+			dbList.add(new DbOrderedFeature<WayNode>(entityId, wayNodeList.get(i), i));
+		}
+		
+		wayNodeDao.addAll(dbList);
+	}
+	
+	
+	/**
+	 * Updates the bounding box column for the specified way.
+	 * 
+	 * @param wayId
+	 *            The way bounding box.
+	 */
+	private void updateWayGeometries(long wayId) {
+		if (capabilityChecker.isWayBboxSupported()) {
+			jdbcTemplate.update(SQL_UPDATE_WAY_BBOX, wayId);
+		}
+		if (capabilityChecker.isWayLinestringSupported()) {
+			jdbcTemplate.update(SQL_UPDATE_WAY_LINESTRING, wayId);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void addEntity(Way entity) {
+		super.addEntity(entity);
+		
+		addWayNodeList(entity.getId(), entity.getWayNodes());
+		
+		updateWayGeometries(entity.getId());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void modifyEntity(Way entity) {
+		long wayId;
+		
+		super.modifyEntity(entity);
+		
+		wayId = entity.getId();
+		wayNodeDao.removeList(wayId);
+		addWayNodeList(entity.getId(), entity.getWayNodes());
+		
+		updateWayGeometries(entity.getId());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void removeEntity(long entityId) {
+		wayNodeDao.removeList(entityId);
+		
+		super.removeEntity(entityId);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected List<FeaturePopulator<Way>> getFeaturePopulators(String tablePrefix) {
+		return Collections.emptyList();
+	}
+}
diff --git a/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java
new file mode 100644
index 0000000..86c82e1
--- /dev/null
+++ b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java
@@ -0,0 +1,223 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.lifecycle.Releasable;
+import org.openstreetmap.osmosis.pgsnapshot.common.CompactPersistentNodeLocationStore;
+import org.openstreetmap.osmosis.pgsnapshot.common.InMemoryNodeLocationStore;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocation;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStore;
+import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
+import org.openstreetmap.osmosis.pgsnapshot.common.PersistentNodeLocationStore;
+import org.postgis.LineString;
+import org.postgis.LinearRing;
+import org.postgis.Point;
+import org.postgis.Polygon;
+
+
+/**
+ * Caches a set of node latitudes and longitudes and uses these to calculate the
+ * geometries for ways.
+ * 
+ * @author Brett Henderson
+ */
+public class WayGeometryBuilder implements Releasable {
+	
+	/**
+	 * Stores the locations of nodes so that they can be used to build the way
+	 * geometries.
+	 */
+	protected NodeLocationStore locationStore;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param storeType
+	 *            The type of storage to use for holding node locations.
+	 */
+	public WayGeometryBuilder(NodeLocationStoreType storeType) {
+		if (NodeLocationStoreType.InMemory.equals(storeType)) {
+			locationStore = new InMemoryNodeLocationStore();
+		} else if (NodeLocationStoreType.TempFile.equals(storeType)) {
+			locationStore = new PersistentNodeLocationStore();
+		} else if (NodeLocationStoreType.CompactTempFile.equals(storeType)) {
+			locationStore = new CompactPersistentNodeLocationStore();
+		} else {
+			throw new OsmosisRuntimeException("The store type " + storeType + " is not recognized.");
+		}
+	}
+	
+	
+	/**
+	 * Adds the location of the node to the internal store.
+	 * 
+	 * @param node
+	 *            The node to add.
+	 */
+	public void addNodeLocation(Node node) {
+		locationStore.addLocation(node.getId(), new NodeLocation(node.getLongitude(), node.getLatitude()));
+	}
+
+    /**
+     * Get NodeLocation from internal store.
+     *
+     * @param nodeId
+     *              Id of the node we want the location for.
+     * @return Location of node
+     */
+    public NodeLocation getNodeLocation(long nodeId) {
+        return locationStore.getNodeLocation(nodeId);
+    }
+	
+	private Polygon createWayBbox(double left, double right, double bottom, double top) {
+		Point[] points;
+		LinearRing ring;
+		Polygon bbox;
+		
+		points = new Point[5];
+		points[0] = new Point(left, bottom);
+		points[1] = new Point(left, top);
+		points[2] = new Point(right, top);
+		points[3] = new Point(right, bottom);
+		points[4] = new Point(left, bottom);
+		
+		ring = new LinearRing(points);
+		
+		bbox = new Polygon(new LinearRing[] {ring});
+		bbox.srid = 4326;
+		
+		return bbox;
+	}
+	
+	
+	/**
+	 * Creates a linestring from a list of points.
+	 * 
+	 * @param points
+	 *            The points making up the line.
+	 * @return The linestring.
+	 */
+	public LineString createLinestring(List<Point> points) {
+		LineString lineString;
+		
+		lineString = new LineString(points.toArray(new Point[]{}));
+		lineString.srid = 4326;
+		
+		return lineString;
+	}
+
+
+    /**
+     * @param nodeId
+     *             Id of the node.
+     * @return Point object
+     */
+    public Point createPoint(long nodeId) {
+	    NodeLocation nodeLocation = locationStore.getNodeLocation(nodeId);
+        Point point = new Point(nodeLocation.getLongitude(), nodeLocation.getLatitude());
+        point.srid = 4326;
+
+        return point;
+    }
+
+	
+	/**
+	 * Builds a bounding box geometry object from the node references in the
+	 * specified way. Unknown nodes will be ignored.
+	 * 
+	 * @param way
+	 *            The way to create the bounding box for.
+	 * @return The bounding box surrounding the way.
+	 */
+	public Polygon createWayBbox(Way way) {
+		double left;
+		double right;
+		double top;
+		double bottom;
+		boolean nodesFound;
+		
+		nodesFound = false;
+		left = 0;
+		right = 0;
+		bottom = 0;
+		top = 0;
+		for (WayNode wayNode : way.getWayNodes()) {
+			NodeLocation nodeLocation;
+			double longitude;
+			double latitude;
+			
+			nodeLocation = locationStore.getNodeLocation(wayNode.getNodeId());
+			longitude = nodeLocation.getLongitude();
+			latitude = nodeLocation.getLatitude();
+			
+			if (nodeLocation.isValid()) {
+				if (nodesFound) {
+					if (longitude < left) {
+						left = longitude;
+					}
+					if (longitude > right) {
+						right = longitude;
+					}
+					if (latitude < bottom) {
+						bottom = latitude;
+					}
+					if (latitude > top) {
+						top = latitude;
+					}
+				} else {
+					left = longitude;
+					right = longitude;
+					bottom = latitude;
+					top = latitude;
+					
+					nodesFound = true;
+				}
+			}
+		}
+		
+		return createWayBbox(left, right, bottom, top);
+	}
+	
+	
+	/**
+	 * Builds a linestring geometry object from the node references in the
+	 * specified way. Unknown nodes will be ignored.
+	 * 
+	 * @param way
+	 *            The way to create the linestring for.
+	 * @return The linestring representing the way.
+	 */
+	public LineString createWayLinestring(Way way) {
+		List<Point> linePoints;
+		
+		linePoints = new ArrayList<Point>();
+		for (WayNode wayNode : way.getWayNodes()) {
+			NodeLocation nodeLocation;
+			
+			nodeLocation = locationStore.getNodeLocation(wayNode.getNodeId());
+	
+			if (nodeLocation.isValid()) {
+				linePoints.add(new Point(nodeLocation.getLongitude(), nodeLocation.getLatitude()));
+			} else {
+				return null;
+			}
+		}
+		return createLinestring(linePoints);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		locationStore.release();
+	}
+}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodeRowMapper.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodesArray.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodesArray.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodesArray.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayNodesArray.java
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayRowMapper.java b/osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayRowMapper.java
similarity index 100%
rename from pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayRowMapper.java
rename to osmosis-pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayRowMapper.java
diff --git a/pgsnapshot/src/main/resources/osmosis-plugins.conf b/osmosis-pgsnapshot/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from pgsnapshot/src/main/resources/osmosis-plugins.conf
rename to osmosis-pgsnapshot/src/main/resources/osmosis-plugins.conf
diff --git a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriver.java b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriver.java
similarity index 100%
rename from pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriver.java
rename to osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriver.java
diff --git a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverFactory.java b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverFactory.java
similarity index 100%
rename from pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverFactory.java
rename to osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverFactory.java
diff --git a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverPlugin.java b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverPlugin.java
similarity index 100%
rename from pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverPlugin.java
rename to osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/DatasetDriverPlugin.java
diff --git a/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java
new file mode 100644
index 0000000..4e7dd30
--- /dev/null
+++ b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java
@@ -0,0 +1,278 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.pgsnapshot.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests for PostgreSQL tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class PostgreSqlTest extends AbstractDataTest {
+	
+	private File getAuthFile() {
+		return dataUtils.createDataFile("db.pgsql.authfile", "v0_6/pgsql-authfile.txt");
+	}
+	
+	
+	/**
+	 * A basic test loading an osm file into a pgsql database, then dumping it
+	 * again and verifying that it is identical.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testLoadAndDump() throws IOException {
+		File authFile;
+		File inputFile;
+		File outputFile;
+		
+		// Generate input files.
+		authFile = getAuthFile();
+		inputFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
+		outputFile = dataUtils.newFile();
+		
+		// Remove all existing data from the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--truncate-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Load the database with a dataset.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6",
+				inputFile.getPath(),
+				"--write-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Dump the database to an osm file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-pgsql-0.6",
+				"authFile=" + authFile.getPath(),
+				"--dataset-dump-0.6",
+				"--tag-sort-0.6",
+				"--write-xml-0.6",
+				outputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the input file.
+		dataUtils.compareFiles(inputFile, outputFile);
+	}
+	
+	
+	/**
+	 * A test loading an osm file into a pgsql database, then applying a
+	 * changeset, then dumping it again and verifying the output is as expected.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testChangeset() throws IOException {
+		File authFile;
+		File snapshotFile;
+		File changesetFile;
+		File expectedResultFile;
+		File actualResultFile;
+		
+		// Generate input files.
+		authFile = getAuthFile();
+		snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
+		changesetFile = dataUtils.createDataFile("v0_6/db-changeset.osc");
+		expectedResultFile = dataUtils.createDataFile("v0_6/db-changeset-expected.osm");
+		actualResultFile = dataUtils.newFile();
+		
+		// Remove all existing data from the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--truncate-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Load the database with the snapshot file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6",
+				snapshotFile.getPath(),
+				"--write-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Apply the changeset file to the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-change-0.6",
+				changesetFile.getPath(),
+				"--write-pgsql-change-0.6",
+				"keepInvalidWays=false", 
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Dump the database to an osm file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-pgsql-0.6",
+				"authFile=" + authFile.getPath(),
+				"--dataset-dump-0.6",
+				"--tag-sort-0.6",
+				"--write-xml-0.6",
+				actualResultFile.getPath()
+			}
+		);
+		
+		// Validate that the dumped file matches the expected result.
+		dataUtils.compareFiles(expectedResultFile, actualResultFile);
+	}
+
+
+	/**
+	 * A test loading an osm file into a pgsql database, then making some modifications via the
+	 * dataset api, then dumping it again and verifying the output is as expected.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testDataset() throws IOException {
+		File authFile;
+		File snapshotFile;
+		File expectedResultFile;
+		File actualResultFile;
+		
+		// Generate input files.
+		authFile = getAuthFile();
+		snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
+		expectedResultFile = dataUtils.createDataFile("v0_6/db-dataset-expected.osm");
+		actualResultFile = dataUtils.newFile();
+		
+		// Remove all existing data from the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--truncate-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Load the database with the snapshot file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6",
+				snapshotFile.getPath(),
+				"--write-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Invoke the dataset driver task task to manipulate the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"-p",
+				DatasetDriverPlugin.class.getName(),
+				"--read-pgsql-0.6",
+				"authFile=" + authFile.getPath(),
+				"--drive-dataset"
+			}
+		);
+		
+		// Dump the database to an osm file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-pgsql-0.6",
+				"authFile=" + authFile.getPath(),
+				"--dataset-dump-0.6",
+				"--tag-sort-0.6",
+				"--write-xml-0.6",
+				actualResultFile.getPath()
+			}
+		);
+		
+		// Validate that the dumped file matches the expected result.
+		dataUtils.compareFiles(expectedResultFile, actualResultFile);
+	}
+
+
+	/**
+	 * A test loading an osm file into a pgsql database, then reading it via a
+	 * dataset bounding box covering the entire planet and verifying the output
+	 * is as expected.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testDatasetBoundingBox() throws IOException {
+		File authFile;
+		File inputFile;
+		File outputFile;
+		
+		// Generate input files.
+		authFile = getAuthFile();
+		inputFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
+		outputFile = dataUtils.newFile();
+		
+		// Remove all existing data from the database.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--truncate-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Load the database with a dataset.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6",
+				inputFile.getPath(),
+				"--write-pgsql-0.6",
+				"authFile=" + authFile.getPath()
+			}
+		);
+		
+		// Dump the database to an osm file.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-pgsql-0.6",
+				"authFile=" + authFile.getPath(),
+				"--dataset-bounding-box-0.6",
+				"completeWays=true",
+				"--tag-sort-0.6",
+				"--write-xml-0.6",
+				outputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the input file.
+		dataUtils.compareFiles(inputFile, outputFile);
+	}
+}
diff --git a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeLocationStoreTest.java b/osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeLocationStoreTest.java
similarity index 100%
rename from pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeLocationStoreTest.java
rename to osmosis-pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeLocationStoreTest.java
diff --git a/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm
new file mode 100644
index 0000000..4c36e27
--- /dev/null
+++ b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm
@@ -0,0 +1,48 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1-revised"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="4" version="13" timestamp="2012-12-11T15:10:12Z" changeset="101">
+    <nd ref="1"/>
+    <tag k="note" v="single node way"/>
+  </way>
+  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+    <member type="node" ref="7" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc
new file mode 100644
index 0000000..198e6e1
--- /dev/null
+++ b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc
@@ -0,0 +1,43 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <modify>
+    <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1-revised"/>
+    </node>
+  </modify>
+  <delete>
+    <way id="3" version="13" timestamp="2008-01-03T09:10:11Z" changeset="92"/>
+  </delete>
+  <create>
+    <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
+      <tag k="created_by" v="Me6"/>
+    </node>
+    <way id="5" version="1" timestamp="2012-12-11T15:12:24Z" changeset="10">
+      <tag k="note" v="zero node way"/>
+    </way>
+  </create>
+  <modify>
+    <!-- Add a new way node and change the user name. -->
+    <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+      <nd ref="1"/>
+      <nd ref="2"/>
+      <nd ref="3"/>
+      <nd ref="4"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <!-- This modify uses the same version number which will test db ability to cope with changeset replay. -->
+    <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+      <nd ref="2"/>
+      <nd ref="3"/>
+      <nd ref="4"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <!-- New version of the relation pointing to a different node. -->
+    <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
+      <member type="node" ref="7" role="noderole"/>
+      <member type="way" ref="1" role="wayrole1"/>
+      <member type="way" ref="2" role="wayrole2"/>
+      <tag k="type" v="myrelation"/>
+    </relation>
+  </modify>
+</osmChange>
diff --git a/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm
new file mode 100644
index 0000000..0e8fc49
--- /dev/null
+++ b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm
@@ -0,0 +1,52 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
+    <tag k="change" v="new tag"/>
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="7" version="16" timestamp="2008-01-02T18:19:20Z" changeset="93" lat="-11" lon="-12">
+    <tag k="change" v="new node"/>
+    <tag k="created_by" v="Me7"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="4" version="13" timestamp="2012-12-11T15:10:12Z" changeset="101">
+    <nd ref="1"/>
+    <tag k="note" v="single node way"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm
new file mode 100644
index 0000000..df72924
--- /dev/null
+++ b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm
@@ -0,0 +1,50 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
+    <tag k="created_by" v="Me5"/>
+  </node>
+  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
+    <tag k="created_by" v="Me6"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <nd ref="5"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="4" version="13" timestamp="2012-12-11T15:10:12Z" changeset="101">
+    <nd ref="1"/>
+    <tag k="note" v="single node way"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
+    <member type="node" ref="6" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/pgsnapshot/src/test/resources/data/template/v0_6/pgsql-authfile.txt b/osmosis-pgsnapshot/src/test/resources/data/template/v0_6/pgsql-authfile.txt
similarity index 100%
rename from pgsnapshot/src/test/resources/data/template/v0_6/pgsql-authfile.txt
rename to osmosis-pgsnapshot/src/test/resources/data/template/v0_6/pgsql-authfile.txt
diff --git a/osmosis-replication-http/.checkstyle b/osmosis-replication-http/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-replication-http/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-replication-http/.gitignore b/osmosis-replication-http/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-replication-http/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-replication-http/build.gradle b/osmosis-replication-http/build.gradle
new file mode 100644
index 0000000..fdf1c6f
--- /dev/null
+++ b/osmosis-replication-http/build.gradle
@@ -0,0 +1,7 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-replication')
+    compile project(':osmosis-xml')
+    compile group: 'org.jboss.netty', name: 'netty', version: dependencyVersionNetty
+    testCompile project(':osmosis-testutil')
+}
diff --git a/osmosis-replication-http/readme.txt b/osmosis-replication-http/readme.txt
new file mode 100644
index 0000000..6320f14
--- /dev/null
+++ b/osmosis-replication-http/readme.txt
@@ -0,0 +1,17 @@
+Notes for enhancements to replication.
+
+Split current --replicate-apidb task into two halves.
+* One half reads from the database and maintains a state file with transaction details.
+ * No sequence number maintained here.
+ * Move into the Osmosis-ApidbReplication project.
+* Second half writes to change files and writes state files with timestamp only.
+ * Sequence number allocated sequentially and written to a global state file.
+ * Move into the Osmosis-Replicate project.
+ * Starts web server (optional).
+ * Web server responds to URL http://<server>:<port>/sequenceNumber with current state number.
+ * Web server responds to URL http://<server>:<port>/sequenceNumber/upToDate with current state number and keeps sending updated state numbers as they become available.
+
+Create a new --serve-replication task.
+* Connects to the above web server to track current sequence number.
+* Starts web server.
+* Web server responds to URL http://<server>:<port>/replicate with current state number.
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/ReplicationHttpPluginLoader.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/ReplicationHttpPluginLoader.java
new file mode 100644
index 0000000..9904f48
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/ReplicationHttpPluginLoader.java
@@ -0,0 +1,43 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.ReplicationDataClientFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.ReplicationDataServerFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.ReplicationSequenceServerFactory;
+
+
+/**
+ * The plugin loader for the API Schema tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationHttpPluginLoader implements PluginLoader {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		Map<String, TaskManagerFactory> factoryMap;
+		
+		factoryMap = new HashMap<String, TaskManagerFactory>();
+
+		factoryMap.put("receive-replication", new ReplicationDataClientFactory());
+		factoryMap.put("rr", new ReplicationDataClientFactory());
+		factoryMap.put("send-replication-data", new ReplicationDataServerFactory());
+		factoryMap.put("srd", new ReplicationDataServerFactory());
+		factoryMap.put("send-replication-sequence", new ReplicationSequenceServerFactory());
+		factoryMap.put("srs", new ReplicationSequenceServerFactory());
+
+		factoryMap.put("read-replication-0.6", new ReplicationDataClientFactory());
+		factoryMap.put("send-replication-data-0.6", new ReplicationDataServerFactory());
+		factoryMap.put("send-replication-sequence-0.6", new ReplicationSequenceServerFactory());
+		
+		return factoryMap;
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClient.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClient.java
new file mode 100644
index 0000000..2ab41bb
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClient.java
@@ -0,0 +1,127 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.ReplicationDataClientChannelPipelineFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClient;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClientRestartManager;
+
+
+/**
+ * This task connects to a replication data server to obtain replication data,
+ * then sends the replication data to the sink. This requires the change sink to
+ * support the replication extensions allowing state persistence and multiple
+ * invocations.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataClient implements RunnableChangeSource {
+
+	private NoReleaseChangeSinkWrapper changeSinkWrapper;
+	private InetSocketAddress serverAddress;
+	private String pathPrefix;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param serverAddress
+	 *            The server to connect to.
+	 * @param pathPrefix
+	 *            The base path to add to the URL. This is necessary if a data
+	 *            server is sitting behind a proxy server that adds a prefix to
+	 *            the request path.
+	 */
+	public ReplicationDataClient(InetSocketAddress serverAddress, String pathPrefix) {
+		this.serverAddress = serverAddress;
+		this.pathPrefix = pathPrefix;
+
+		changeSinkWrapper = new NoReleaseChangeSinkWrapper();
+	}
+
+
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		changeSinkWrapper.setChangeSink(changeSink);
+	}
+
+
+	@Override
+	public void run() {
+		try {
+			// Create a sequence client restart manager so that our sequence
+			// client continues processing in the face of temporary connectivity
+			// issues.
+			SequenceClientRestartManager clientRestartManager = new SequenceClientRestartManager();
+			
+			// Create the client for receiving replication data.
+			ReplicationDataClientChannelPipelineFactory pipelineFactory =
+					new ReplicationDataClientChannelPipelineFactory(
+							clientRestartManager.getControl(), changeSinkWrapper, serverAddress.getHostName(),
+							pathPrefix);
+			SequenceClient client = new SequenceClient(serverAddress, pipelineFactory);
+
+			// Run the client and perform restarts if it fails. This call will
+			// block.
+			clientRestartManager.manageClient(client);
+
+		} finally {
+			changeSinkWrapper.realRelease();
+		}
+	}
+
+	/**
+	 * This acts as a proxy between the sequence client and the real change
+	 * sink. The primary purpose is to prevent the release method from being
+	 * called until all processing has completed.
+	 */
+	private static class NoReleaseChangeSinkWrapper implements ChangeSinkChangeSource {
+		private ChangeSink changeSink;
+
+
+		@Override
+		public void setChangeSink(ChangeSink changeSink) {
+			this.changeSink = changeSink;
+		}
+
+
+		@Override
+		public void initialize(Map<String, Object> metaData) {
+			changeSink.initialize(metaData);
+		}
+
+
+		@Override
+		public void process(ChangeContainer change) {
+			changeSink.process(change);
+		}
+
+
+		@Override
+		public void complete() {
+			changeSink.complete();
+		}
+
+
+		@Override
+		public void release() {
+			// Do nothing.
+		}
+
+
+		/**
+		 * Called by the main replication data client when all processing is
+		 * complete. Unlike the release method which does nothing, this calls
+		 * the change sink release method.
+		 */
+		public void realRelease() {
+			changeSink.release();
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClientFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClientFactory.java
new file mode 100644
index 0000000..cc4bba0
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataClientFactory.java
@@ -0,0 +1,57 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.net.InetSocketAddress;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.RunnableChangeSourceManager;
+
+
+/**
+ * The task manager factory for a HTTP replication data client.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataClientFactory extends TaskManagerFactory {
+	private static final String ARG_HOST = "host";
+	private static final String ARG_PORT = "port";
+	private static final String ARG_PATH_PREFIX = "pathPrefix";
+	private static final String DEFAULT_HOST = "localhost";
+	private static final int DEFAULT_PORT = 0;
+	private static final String DEFAULT_PATH_PREFIX = "";
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String host;
+		int port;
+		StringBuilder basePath;
+
+		// Get the task arguments.
+		host = getStringArgument(taskConfig, ARG_HOST, DEFAULT_HOST);
+		port = getIntegerArgument(taskConfig, ARG_PORT, DEFAULT_PORT);
+		basePath = new StringBuilder(getStringArgument(taskConfig, ARG_PATH_PREFIX, DEFAULT_PATH_PREFIX));
+		
+		// Ensure that the base path if it exists has a leading slash but no trailing slash.
+		while (basePath.length() > 0 && basePath.charAt(0) == '/') {
+			basePath.delete(0, 1);
+		}
+		while (basePath.length() > 0 && basePath.charAt(basePath.length() - 1) == '/') {
+			basePath.delete(basePath.length() - 1, basePath.length());
+		}
+		if (basePath.length() > 0) {
+			basePath.insert(0, '/');
+		}
+		
+		return new RunnableChangeSourceManager(
+			taskConfig.getId(),
+			new ReplicationDataClient(new InetSocketAddress(host, port), basePath.toString()),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServer.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServer.java
new file mode 100644
index 0000000..6c90501
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServer.java
@@ -0,0 +1,111 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.common.RunnableTask;
+import org.openstreetmap.osmosis.replication.common.ServerStateReader;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.ReplicationDataServerChannelPipelineFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClient;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClientRestartManager;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceNumberClientChannelPipelineFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceNumberClientListener;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceServer;
+
+
+/**
+ * This task creates a HTTP server that sends updated replication data to
+ * clients. It is notified of updated sequence numbers as they occur by
+ * connecting to a replication sequence server.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataServer implements RunnableTask {
+
+	private int notificationPort;
+	private File dataDirectory;
+	private int port;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param notificationPort
+	 *            The port to connect to for notification updates.
+	 * @param dataDirectory
+	 *            The location of the replication data and state files.
+	 * @param port
+	 *            The port to listen on.
+	 */
+	public ReplicationDataServer(int notificationPort, File dataDirectory, int port) {
+		this.notificationPort = notificationPort;
+		this.dataDirectory = dataDirectory;
+		this.port = port;
+	}
+
+
+	/**
+	 * Returns the port that is being used to listen for new connections.
+	 * 
+	 * @return The port number.
+	 */
+	public int getPort() {
+		return port;
+	}
+
+
+	private long getCurrentSequenceNumber() {
+		try {
+			return new ServerStateReader().getServerState(dataDirectory.toURI().toURL()).getSequenceNumber();
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException("Unable to get the current sequence number", e);
+		}
+	}
+
+
+	@Override
+	public void run() {
+		// Instantiate the replication data server.
+		final SequenceServer server = new SequenceServer(port, new ReplicationDataServerChannelPipelineFactory(
+				dataDirectory));
+
+		// Configure a listener to send sequence number events from the
+		// client to the server.
+		SequenceNumberClientListener numberListener = new SequenceNumberClientListener() {
+			@Override
+			public void notifySequenceNumber(long sequenceNumber) {
+				server.update(sequenceNumber);
+			}
+		};
+
+		// Create a sequence client restart manager so that our sequence
+		// client continues processing in the face of temporary connectivity
+		// issues.
+		SequenceClientRestartManager clientRestartManager = new SequenceClientRestartManager();
+
+		// Create the client for receiving updated sequence numbers..
+		SequenceNumberClientChannelPipelineFactory channelPipelineFactory =
+				new SequenceNumberClientChannelPipelineFactory(
+						clientRestartManager.getControl(), numberListener, "localhost");
+		SequenceClient client = new SequenceClient(new InetSocketAddress(notificationPort), channelPipelineFactory);
+
+		try {
+			// Start the server with the current replication number.
+			server.start(getCurrentSequenceNumber());
+
+			// Update the port. It may have been allocated dynamically if the
+			// port was specified as 0.
+			port = server.getPort();
+
+			// Run the client and perform restarts if it fails. This call will
+			// block.
+			clientRestartManager.manageClient(client);
+
+		} finally {
+			server.stop();
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServerFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServerFactory.java
new file mode 100644
index 0000000..ce06d1d
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationDataServerFactory.java
@@ -0,0 +1,51 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.RunnableTaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+
+
+/**
+ * The task manager factory for a replication sequence server.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataServerFactory extends TaskManagerFactory {
+	private static final String ARG_NOTIFICATION_PORT = "notificationPort";
+	private static final String ARG_DATA_DIRECTORY = "dataDirectory";
+	private static final String ARG_PORT = "port";
+	private static final int DEFAULT_NOTIFICATION_PORT = 0;
+	private static final String DEFAULT_DATA_DIRECTORY = "./";
+	private static final int DEFAULT_PORT = 0;
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		int port;
+		String dataDirectoryString;
+		File dataDirectory;
+		int notificationPort;
+
+		// Get the task arguments.
+		port = getIntegerArgument(taskConfig, ARG_PORT, DEFAULT_PORT);
+		dataDirectoryString = getStringArgument(taskConfig, ARG_DATA_DIRECTORY,
+				getDefaultStringArgument(taskConfig, DEFAULT_DATA_DIRECTORY));
+		notificationPort = getIntegerArgument(taskConfig, ARG_NOTIFICATION_PORT, DEFAULT_NOTIFICATION_PORT);
+
+		// Convert argument strings to strongly typed objects.
+		dataDirectory = new File(dataDirectoryString);
+
+		return new RunnableTaskManager(
+			taskConfig.getId(),
+			new ReplicationDataServer(notificationPort, dataDirectory, port),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServer.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServer.java
new file mode 100644
index 0000000..e251db3
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServer.java
@@ -0,0 +1,127 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceNumberServerChannelPipelineFactory;
+import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceServer;
+
+
+/**
+ * This task creates a HTTP server that sends updated replication sequence
+ * numbers to clients. It is notified of updated sequence numbers as they occur
+ * by being inserted into the middle of a replication pipeline.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationSequenceServer implements ChangeSinkChangeSource {
+
+	private static final Logger LOG = Logger.getLogger(ReplicationSequenceServer.class.getName());
+
+	private ChangeSink changeSink;
+	private ReplicationState state;
+	private long sequenceNumber;
+	private SequenceServer server;
+	private boolean serverStarted;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param port
+	 *            The port to listen on.
+	 */
+	public ReplicationSequenceServer(int port) {
+		server = new SequenceServer(port, new SequenceNumberServerChannelPipelineFactory());
+
+		serverStarted = false;
+	}
+
+
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+
+
+	/**
+	 * Returns the port that is being used to listen for new connections.
+	 * 
+	 * @return The port number.
+	 */
+	public int getPort() {
+		return server.getPort();
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		// Get the replication state from the upstream task.
+		if (!metaData.containsKey(ReplicationState.META_DATA_KEY)) {
+			throw new OsmosisRuntimeException("No replication state has been provided in metadata key "
+					+ ReplicationState.META_DATA_KEY + ".");
+		}
+		state = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+
+		// Call the downstream initialize which will among other things
+		// initialise the state.
+		changeSink.initialize(metaData);
+
+		// We must only read from the state object during initialize and
+		// complete because it may be updated by other threads at other times.
+		sequenceNumber = state.getSequenceNumber();
+
+		// If the sequence id is still 0 then replication hasn't been fully
+		// initialized and we can't start the server yet.
+		if (sequenceNumber > 0 && !serverStarted) {
+			// We can start the server now. We give it the previous sequence
+			// number because the current one is still in progress.
+			server.start(sequenceNumber - 1);
+			serverStarted = true;
+		}
+	}
+
+
+	@Override
+	public void process(ChangeContainer change) {
+		changeSink.process(change);
+	}
+
+
+	@Override
+	public void complete() {
+		changeSink.complete();
+
+		// The sink has completed persisting the replication so now we must
+		// notify the server which will notify the listening clients.
+		if (!serverStarted) {
+			server.start(sequenceNumber);
+			serverStarted = true;
+		} else {
+			server.update(sequenceNumber);
+		}
+	}
+
+
+	@Override
+	public void release() {
+		changeSink.release();
+
+		if (serverStarted) {
+			try {
+				server.stop();
+			} catch (RuntimeException e) {
+				LOG.log(Level.WARNING, "Replication sequence server stop failed.", e);
+			}
+
+			serverStarted = false;
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerFactory.java
new file mode 100644
index 0000000..607b1f5
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerFactory.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkChangeSourceManager;
+
+
+/**
+ * The task manager factory for a replication sequence server.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationSequenceServerFactory extends TaskManagerFactory {
+	private static final String ARG_PORT = "port";
+	private static final int DEFAULT_PORT = 80;
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		int port;
+		
+		// Get the task arguments.
+		port = getIntegerArgument(
+			taskConfig,
+			ARG_PORT,
+			getDefaultIntegerArgument(taskConfig, DEFAULT_PORT)
+		);
+		
+		return new ChangeSinkChangeSourceManager(
+			taskConfig.getId(),
+			new ReplicationSequenceServer(port),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ChunkedDataReceiver.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ChunkedDataReceiver.java
new file mode 100644
index 0000000..c7b6f01
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ChunkedDataReceiver.java
@@ -0,0 +1,220 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.util.CharsetUtil;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.lifecycle.Releasable;
+
+
+/**
+ * Provides the ability to read data that has been broken into data chunks. Each
+ * chunk is preceded by the number of bytes in the chunk followed by a carriage
+ * return line feed pair.
+ * 
+ * @author Brett Henderson
+ */
+public class ChunkedDataReceiver implements Releasable {
+
+	private static final Logger LOG = Logger.getLogger(ChunkedDataReceiver.class.getName());
+
+	private ChannelBuffer buffer;
+	private File tmpDataFile;
+	private FileChannel tmpDataChannel;
+	private List<File> readyFiles;
+	private boolean chunkInProgress;
+	private long bytesRemaining;
+
+
+	/**
+	 * Creates a new instance.
+	 */
+	public ChunkedDataReceiver() {
+		buffer = ChannelBuffers.dynamicBuffer();
+		
+		readyFiles = new ArrayList<File>();
+		chunkInProgress = false;
+	}
+
+
+	/**
+	 * Attempts to read a chunk length header from the data currently in the
+	 * buffer. If a carriage return line feed pair is found, then the data
+	 * preceeding those characters will be converted to a number and returned.
+	 * If the carriage return line feed pair cannot be found, then -1 will be
+	 * returned to indicate that the data is not yet complete.
+	 * 
+	 * @return The value of the chunk length header, or -1 if more data is
+	 *         required.
+	 */
+	private long getChunkLength() {
+		// Look for a carriage return line feed pair.
+		for (int i = buffer.readerIndex() + 1; i < buffer.writerIndex(); i++) {
+			if (buffer.getByte(i) == 0x0A) {
+				if (buffer.getByte(i - 1) == 0x0D) {
+					// All data between the reader index and one before i is the header.
+					String chunkSizeString = buffer.toString(buffer.readerIndex(), i - buffer.readerIndex() - 1,
+							CharsetUtil.UTF_8);
+					long chunkSize = Long.parseLong(chunkSizeString);
+
+					// Move the buffer past the current reader index.
+					buffer.readerIndex(i + 1);
+
+					return chunkSize;
+				}
+			}
+		}
+
+		return -1;
+	}
+
+
+	private void initializeChunk() {
+		try {
+			tmpDataFile = File.createTempFile("change", ".tmp");
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to create replication data temp file", e);
+		}
+		try {
+			tmpDataChannel = new FileOutputStream(tmpDataFile).getChannel();
+		} catch (FileNotFoundException e) {
+			throw new OsmosisRuntimeException("Unable to open chunk data temp file", e);
+		}
+		
+		chunkInProgress = true;
+	}
+
+
+	private void writeToChunk(ChannelBuffer writeBuffer) {
+		try {
+			// We can only write the minimum of the number of bytes available
+			// and the number of bytes remaining. The bytes available is an
+			// integer so if all values are possible the minimum value will also
+			// be an integer.
+			int bytesToWrite = (int) Math.min(writeBuffer.readableBytes(), bytesRemaining);
+			
+			// Write the data to the chunk data file.
+			tmpDataChannel.write(writeBuffer.toByteBuffer(writeBuffer.readerIndex(), bytesToWrite));
+			writeBuffer.skipBytes(bytesToWrite);
+			
+			bytesRemaining -= bytesToWrite;
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write chunk data to temp file", e);
+		}
+		
+		// Complete the chunk if it is complete.
+		if (bytesRemaining <= 0) {
+			try {
+				tmpDataChannel.close();
+			} catch (IOException e) {
+				throw new OsmosisRuntimeException("Unable to close chunk data temp file", e);
+			}
+
+			readyFiles.add(tmpDataFile);
+			tmpDataFile = null;
+			chunkInProgress = false;
+		}
+	}
+	
+	
+	private List<File> createResultFileList() {
+		List<File> resultFiles = new ArrayList<File>(readyFiles);
+		readyFiles.clear();
+		
+		return resultFiles;
+	}
+
+
+	/**
+	 * Processes the data in the input buffer. It requires each chunk of data to
+	 * be preceeded by a length header. Once all data within each chunk has been
+	 * read, a file will be produced which holds the contents of the chunk. When
+	 * no more data is available, all files resulting from the input data will
+	 * be returned. It is possible that 0 files will be returned. Any leftover
+	 * data not comprising a complete chunk will be retained internally until
+	 * the next call.
+	 * 
+	 * @param inputBuffer
+	 *            Add data in this buffer will be added.
+	 * @return All chunk data files completed during this call.
+	 */
+	public List<File> processData(ChannelBuffer inputBuffer) {
+		while (inputBuffer.readableBytes() > 0 || buffer.readableBytes() > 0) {
+			// If there is no chunk in progress so the next data will be the
+			// header.
+			if (!chunkInProgress) {
+				
+				// Move all input data to our internal buffer so that we have a
+				// single view of all available data.
+				buffer.writeBytes(inputBuffer);
+				bytesRemaining = getChunkLength();
+				
+				if (bytesRemaining >= 0) {
+					// We have read the chunk header, so now begin processing the
+					// chunk body.
+					initializeChunk();
+				} else {
+					return createResultFileList();
+				}
+			}
+			
+			// If we've reached this far we know a chunk is in progress so we
+			// must write to the data file. We write from the internal buffer if
+			// available, otherwise the input buffer.
+			if (buffer.readableBytes() > 0) {
+				writeToChunk(buffer);
+			} else {
+				writeToChunk(inputBuffer);
+			}
+		}
+		
+		return createResultFileList();
+	}
+
+
+	/**
+	 * Gets the buffer.
+	 * 
+	 * @return The buffer.
+	 */
+	public ChannelBuffer getBuffer() {
+		return buffer;
+	}
+
+
+	@Override
+	public void release() {
+		if (tmpDataChannel != null) {
+			try {
+				tmpDataChannel.close();
+			} catch (IOException ex) {
+				LOG.log(Level.WARNING, "Unable to close the current temporary chunk file", ex);
+			}
+		}
+		
+		if (tmpDataFile != null) {
+			if (!tmpDataFile.delete()) {
+				LOG.log(Level.WARNING, "Unable to delete the current temporary chunk file " + tmpDataFile);
+			}
+		}
+		
+		for (File readyFile : readyFiles) {
+			if (!readyFile.delete()) {
+				LOG.log(Level.WARNING, "Unable to delete the current temporary chunk file " + readyFile);
+			}
+		}
+		readyFiles.clear();
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientChannelPipelineFactory.java
new file mode 100644
index 0000000..eddcb6e
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientChannelPipelineFactory.java
@@ -0,0 +1,47 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * Builds Netty channel pipelines for new connections to servers.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataClientChannelPipelineFactory extends SequenceClientChannelPipelineFactory {
+
+	private ChangeSink changeSink;
+	private String serverHost;
+	private String pathPrefix;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param changeSink
+	 *            The destination for the replication data.
+	 * @param serverHost
+	 *            The name of the host system running the sequence server.
+	 * @param pathPrefix
+	 *            The base path to add to the URL. This is necessary if a data
+	 *            server is sitting behind a proxy server that adds a prefix to
+	 *            the request path.
+	 */
+	public ReplicationDataClientChannelPipelineFactory(SequenceClientControl control,
+			ChangeSink changeSink, String serverHost, String pathPrefix) {
+		super(control);
+
+		this.changeSink = changeSink;
+		this.serverHost = serverHost;
+		this.pathPrefix = pathPrefix;
+	}
+
+
+	@Override
+	protected SequenceClientHandler createHandler(SequenceClientControl control) {
+		return new ReplicationDataClientHandler(control, changeSink, serverHost, pathPrefix);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientHandler.java
new file mode 100644
index 0000000..9f688b6
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataClientHandler.java
@@ -0,0 +1,231 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+
+
+/**
+ * Netty handler for receiving replication data and notifying listeners.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataClientHandler extends SequenceClientHandler {
+
+	private static final Logger LOG = Logger.getLogger(ReplicationDataClientHandler.class.getName());
+
+	private ChangeSink changeSink;
+	private String pathPrefix;
+	private NoLifecycleChangeSinkWrapper noLifecycleChangeSink;
+	private boolean sinkInitInvoked;
+	private boolean replicationStateReceived;
+	private ReplicationState replicationState;
+	private ChunkedDataReceiver chunkReceiver;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param changeSink
+	 *            The destination for the replication data.
+	 * @param serverHost
+	 *            The name of the host system running the sequence server.
+	 * @param pathPrefix
+	 *            The base path to add to the URL. This is necessary if a data
+	 *            server is sitting behind a proxy server that adds a prefix to
+	 *            the request path.
+	 */
+	public ReplicationDataClientHandler(SequenceClientControl control, ChangeSink changeSink, String serverHost,
+			String pathPrefix) {
+		super(control, serverHost);
+
+		this.changeSink = changeSink;
+		this.pathPrefix = pathPrefix;
+
+		noLifecycleChangeSink = new NoLifecycleChangeSinkWrapper(changeSink);
+
+		sinkInitInvoked = false;
+		replicationStateReceived = false;
+		replicationState = null;
+		chunkReceiver = new ChunkedDataReceiver();
+	}
+
+
+	private void sendReplicationData(File chunkFile) {
+		// Release all class level resources and prepare for passing the
+		// replication data downstream.
+		replicationState = null;
+		replicationStateReceived = false;
+		sinkInitInvoked = false;
+		
+		// Send the replication data downstream but don't call any lifecycle
+		// methods on the change sink because we're managing those separately.
+		if (chunkFile != null) {
+			RunnableChangeSource changeReader = new XmlChangeReader(chunkFile, true, CompressionMethod.GZip);
+			changeReader.setChangeSink(noLifecycleChangeSink);
+			changeReader.run();
+		}
+		
+		changeSink.complete();
+	}
+
+
+	private void invokeSinkInit() {
+		replicationState = new ReplicationState();
+		Map<String, Object> metaData = new HashMap<String, Object>(1);
+		metaData.put(ReplicationState.META_DATA_KEY, replicationState);
+		changeSink.initialize(metaData);
+		sinkInitInvoked = true;
+	}
+
+
+	@Override
+	protected String getRequestUri() {
+		// We need to know the last replication number that we have received on
+		// a previous run. To do this we need to retrieve the replication state
+		// from our downstream replication task by initializing.
+		invokeSinkInit();
+
+		// The downstream task returns the next sequence number.
+		long requestSequenceNumber = replicationState.getSequenceNumber();
+
+		return pathPrefix + "/replicationData/" + requestSequenceNumber + "/tail";
+	}
+
+
+	private ReplicationState loadState(File stateFile) {
+		PropertiesPersister persister = new PropertiesPersister(stateFile);
+		ReplicationState state = new ReplicationState();
+		state.load(persister.loadMap());
+
+		return state;
+	}
+
+
+	@Override
+	protected void processMessageData(ChannelBuffer buffer) {
+		// Break the data down according to chunk alignment.
+		List<File> chunkFiles = chunkReceiver.processData(buffer);
+		
+		try {
+			for (File chunkFile : chunkFiles) {
+
+				if (!replicationStateReceived) {
+					// We usually have to invoke the sink init, but if this is
+					// during startup we may have already performed this step
+					// while preparing our initial request.
+					if (!sinkInitInvoked) {
+						invokeSinkInit();
+					}
+
+					// The first chunk contains the replication state stored in
+					// properties format.
+					ReplicationState serverReplicationState = loadState(chunkFile);
+					if (LOG.isLoggable(Level.FINER)) {
+						LOG.finer("Received replication state " + serverReplicationState.getSequenceNumber());
+					}
+
+					// Validate that the server has sent us the expected state.
+					if (serverReplicationState.getSequenceNumber() != replicationState.getSequenceNumber()) {
+						throw new OsmosisRuntimeException("Received sequence number "
+								+ serverReplicationState.getSequenceNumber() + " from server, expected "
+								+ replicationState.getSequenceNumber());
+					}
+
+					// Update the local state with server values.
+					replicationState.setTimestamp(serverReplicationState.getTimestamp());
+					replicationStateReceived = true;
+					
+					// If this is replication 0, then we need to finish
+					// processing now because the first sequence doesn't have
+					// any data.
+					if (replicationState.getSequenceNumber() == 0) {
+						sendReplicationData(null);
+					}
+				} else {
+					sendReplicationData(chunkFile);
+				}
+			}
+			
+		} finally {
+			// Delete all chunk files.
+			for (File chunkFile : chunkFiles) {
+				if (!chunkFile.delete()) {
+					LOG.log(Level.WARNING, "Unable to delete the current temporary chunk file " + chunkFile);
+				}
+			}
+		}
+	}
+
+
+	@Override
+	public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+		// Release any half populated chunk files.
+		chunkReceiver.release();
+
+		super.channelClosed(ctx, e);
+	}
+
+	/**
+	 * This acts as a proxy between the xml change reader and the real change
+	 * sink. The primary purpose is to only propagate calls to process because
+	 * the lifecycle methods initialize, complete and release are managed
+	 * separately.
+	 */
+	private static class NoLifecycleChangeSinkWrapper implements ChangeSink {
+		private ChangeSink changeSink;
+
+
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param changeSink
+		 *            The wrapped change sink.
+		 */
+		public NoLifecycleChangeSinkWrapper(ChangeSink changeSink) {
+			this.changeSink = changeSink;
+		}
+
+
+		@Override
+		public void initialize(Map<String, Object> metaData) {
+			// Do nothing.
+		}
+
+
+		@Override
+		public void process(ChangeContainer change) {
+			changeSink.process(change);
+		}
+
+
+		@Override
+		public void complete() {
+			// Do nothing.
+		}
+
+
+		@Override
+		public void release() {
+			// Do nothing.
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerChannelPipelineFactory.java
new file mode 100644
index 0000000..28eafc0
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerChannelPipelineFactory.java
@@ -0,0 +1,32 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.io.File;
+
+
+/**
+ * Builds Netty channel pipelines for new client connections.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataServerChannelPipelineFactory extends SequenceServerChannelPipelineFactory {
+	
+	private File dataDirectory;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param dataDirectory
+	 *            The location of the replication data files.
+	 */
+	public ReplicationDataServerChannelPipelineFactory(File dataDirectory) {
+		this.dataDirectory = dataDirectory;
+	}
+
+
+	@Override
+	protected SequenceServerHandler createHandler(SequenceServerControl control) {
+		return new ReplicationDataServerHandler(control, dataDirectory);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerHandler.java
new file mode 100644
index 0000000..5f1d2f5
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ReplicationDataServerHandler.java
@@ -0,0 +1,468 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.TimeZone;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.WriteCompletionEvent;
+import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.util.CharsetUtil;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.ReplicationSequenceFormatter;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+
+
+/**
+ * A sequence server handler implementation that sends the replication data
+ * associated with sequence numbers.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDataServerHandler extends SequenceServerHandler {
+
+	private static final Logger LOG = Logger.getLogger(ReplicationDataServerHandler.class.getName());
+	private static final String REQUEST_DATE_FORMAT = "yyyy-MM-dd-HH-mm-ss";
+	private static final int CHUNK_SIZE = 4096;
+
+	private File dataDirectory;
+	private ReplicationSequenceFormatter sequenceFormatter;
+	private FileChannel chunkedFileChannel;
+	private boolean fileSizeSent;
+	private boolean includeData;
+	private ChannelFuture sequenceFuture;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param dataDirectory
+	 *            The directory containing the replication data files.
+	 */
+	public ReplicationDataServerHandler(SequenceServerControl control, File dataDirectory) {
+		super(control);
+
+		this.dataDirectory = dataDirectory;
+
+		sequenceFormatter = new ReplicationSequenceFormatter(9, 3);
+	}
+
+
+	private DateFormat getRequestDateParser() {
+		SimpleDateFormat dateParser = new SimpleDateFormat(REQUEST_DATE_FORMAT);
+		Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+		dateParser.setCalendar(calendar);
+
+		return dateParser;
+	}
+
+
+	private File getStateFile(long sequenceNumber) {
+		return new File(dataDirectory, sequenceFormatter.getFormattedName(sequenceNumber, ".state.txt"));
+	}
+
+
+	private File getDataFile(long sequenceNumber) {
+		return new File(dataDirectory, sequenceFormatter.getFormattedName(sequenceNumber, ".osc.gz"));
+	}
+
+
+	private ReplicationState getReplicationState(long sequenceNumber) {
+		PropertiesPersister persister = new PropertiesPersister(getStateFile(sequenceNumber));
+		ReplicationState state = new ReplicationState();
+		state.load(persister.loadMap());
+
+		return state;
+	}
+
+
+	/**
+	 * Search through the replication state records and find the nearest
+	 * replication number with a timestamp earlier or equal to the requested
+	 * date. It is not sufficient to find the minimum known sequence record with
+	 * a timestamp greater than the requested date because there may be missing
+	 * replication records in between.
+	 * 
+	 * @param lastDate
+	 *            The last date known by the client.
+	 * @return The associated sequence number.
+	 */
+	private long getNextSequenceNumberByDate(Date lastDate) {
+		long startBound = 0;
+		long endBound = getControl().getLatestSequenceNumber();
+
+		// If the requested date is greater than or equal to the latest known
+		// timestamp we should return our latest sequence number so that the
+		// client will start receiving all new records as they arrive with
+		// possibly some duplicated change records.
+		if (lastDate.compareTo(getReplicationState(endBound).getTimestamp()) >= 0) {
+			return endBound;
+		}
+
+		// Continue splitting our range in half until either we find the
+		// requested record, or we only have one possibility remaining.
+		while ((endBound - startBound) > 1) {
+			// Calculate the current midpoint.
+			long midPoint = startBound + ((endBound - startBound) / 2);
+
+			// If the midpoint doesn't exist we need to reset the start bound to
+			// the midpoint and search again.
+			if (!getStateFile(midPoint).exists()) {
+				startBound = midPoint;
+				continue;
+			}
+
+			// If the midpoint timestamp is greater we search in the lower half,
+			// otherwise the higher half.
+			int comparison = lastDate.compareTo(getReplicationState(midPoint).getTimestamp());
+			if (comparison == 0) {
+				// We have an exact match so stop processing now.
+				return midPoint;
+			} else if (comparison < 0) {
+				// We will now search in the lower half of the search range.
+				// Even though we know the midpoint is not the right value, we
+				// include it in the next range because our search assumes that
+				// the right sequence number is less than the end point.
+				endBound = midPoint;
+			} else {
+				// We will now search in the upper half of the search range.
+				// Even though the mid point has a timestamp less than the
+				// requested value, it still may be the selected value if the
+				// next timestamp is greater.
+				startBound = midPoint;
+			}
+		}
+
+		// We only have one possibility remaining which is the start bound. This
+		// is the requested record if it exists and has a timestamp less than or
+		// equal to that requested.
+		if (getStateFile(startBound).exists()
+				&& lastDate.compareTo(getReplicationState(startBound).getTimestamp()) >= 0) {
+			return startBound;
+		} else {
+			// We cannot find any replication records with an early enough date.
+			// This typically means that replication records for that time
+			// period either no longer exist or never existed.
+			throw new ResourceGoneException();
+		}
+	}
+
+
+	private FileChannel openFileChannel(File file) {
+		try {
+			return new FileInputStream(file).getChannel();
+		} catch (FileNotFoundException e) {
+			throw new OsmosisRuntimeException("Unable to open file " + file, e);
+		}
+	}
+
+
+	private ChannelBuffer readFromFile(FileChannel fileChannel, int bytesToRead) {
+		try {
+			// Allocate a buffer for the data to be read.
+			byte[] rawBuffer = new byte[bytesToRead];
+
+			// Copy data into the buffer using NIO.
+			ByteBuffer nioBuffer = ByteBuffer.wrap(rawBuffer);
+			for (int bytesRead = 0; bytesRead < bytesToRead;) {
+				int lastBytesRead = fileChannel.read(nioBuffer);
+
+				// We always expect to read data.
+				if (lastBytesRead < 0) {
+					throw new OsmosisRuntimeException("Unexpectedly reached the end of the replication data file");
+				}
+				if (lastBytesRead == 0) {
+					throw new OsmosisRuntimeException("Last read of the replication data file returned 0 bytes");
+				}
+
+				bytesRead += lastBytesRead;
+			}
+
+			// Create and return a Netty buffer.
+			return ChannelBuffers.wrappedBuffer(rawBuffer);
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read from the replication data file", e);
+		}
+	}
+
+
+	private ChannelBuffer loadFile(File file) {
+		FileChannel fileChannel = openFileChannel(file);
+
+		try {
+			if (fileChannel.size() > Integer.MAX_VALUE) {
+				throw new OsmosisRuntimeException("Maximum file size supported is " + Integer.MAX_VALUE + " bytes");
+			}
+
+			// Determine the size of the file.
+			int fileSize = (int) fileChannel.size();
+
+			// Read the entire file.
+			ChannelBuffer buffer = readFromFile(fileChannel, fileSize);
+
+			// We no longer need access to the file.
+			fileChannel.close();
+
+			return buffer;
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read from file " + file, e);
+		} finally {
+			try {
+				fileChannel.close();
+			} catch (IOException e) {
+				LOG.log(Level.WARNING, "Unable to close channel for file " + file, e);
+			}
+		}
+	}
+
+
+	private ChannelBuffer getFileChunk() {
+		try {
+			// Determine how many bytes are left in the file.
+			long remaining = chunkedFileChannel.size() - chunkedFileChannel.position();
+
+			// We will only send up to our maximum chunk size.
+			if (remaining > CHUNK_SIZE) {
+				remaining = CHUNK_SIZE;
+			}
+
+			// Read the next data for the next chunk.
+			ChannelBuffer buffer = readFromFile(chunkedFileChannel, (int) remaining);
+
+			// Close the file if we've reached the end.
+			if (chunkedFileChannel.position() >= chunkedFileChannel.size()) {
+				chunkedFileChannel.close();
+				chunkedFileChannel = null;
+			}
+
+			return buffer;
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read from the replication data file", e);
+		}
+	}
+	
+	
+	private ChannelBuffer buildChunkHeader(long chunkSize) {
+		return ChannelBuffers.copiedBuffer(Long.toString(chunkSize) + "\r\n", CharsetUtil.UTF_8);
+	}
+
+
+	@Override
+	protected void handleRequest(ChannelHandlerContext ctx, HttpRequest request) {
+		final String replicationStateUri = "replicationState";
+		final String replicationDataUri = "replicationData";
+		final String textContentType = "text/plain";
+		final String dataContentType = "application/octet-stream";
+
+		// Split the request Uri into its path elements.
+		String uri = request.getUri();
+		if (!uri.startsWith("/")) {
+			throw new OsmosisRuntimeException("Uri doesn't start with a / character: " + uri);
+		}
+		Queue<String> uriElements = new LinkedList<String>(Arrays.asList(uri.split("/")));
+		uriElements.remove(); // First element is empty due to leading '/'.
+
+		// First element must be either the replication state or replication
+		// data uri which determines whether replication data will be included
+		// or just the replication state.
+		String contentType;
+		if (uriElements.isEmpty()) {
+			throw new ResourceNotFoundException();
+		}
+		String requestTypeString = uriElements.remove();
+		if (replicationStateUri.equals(requestTypeString)) {
+			contentType = textContentType;
+			includeData = false;
+		} else if (replicationDataUri.equals(requestTypeString)) {
+			contentType = dataContentType;
+			includeData = true;
+		} else {
+			throw new ResourceNotFoundException();
+		}
+
+		/*
+		 * The next element determines which replication number to start from.
+		 * The request is one of "current" or N where is the last sequence
+		 * number received by the client.
+		 */
+		long nextSequenceNumber;
+		if (uriElements.isEmpty()) {
+			throw new ResourceNotFoundException();
+		}
+		String sequenceStartString = uriElements.remove();
+		if ("current".equals(sequenceStartString)) {
+			nextSequenceNumber = getControl().getLatestSequenceNumber();
+		} else {
+			// Try to parse the sequence start string as a number. If that fails
+			// try to parse as a date.
+			try {
+				nextSequenceNumber = Long.parseLong(sequenceStartString);
+			} catch (NumberFormatException e) {
+				try {
+					Date lastDate = getRequestDateParser().parse(sequenceStartString);
+					nextSequenceNumber = getNextSequenceNumberByDate(lastDate);
+
+				} catch (ParseException e1) {
+					throw new BadRequestException("Requested sequence number of " + sequenceStartString
+							+ " is not a number, or a date in format yyyy-MM-dd-HH-mm-ss.");
+				}
+
+			}
+		}
+
+		// If the next element exists and is "tail" it means that the client
+		// wants to stay connected and receive updated sequences as they become
+		// available.
+		boolean follow;
+		if (!uriElements.isEmpty()) {
+			String tailElement = uriElements.remove();
+			if ("tail".equals(tailElement)) {
+				follow = true;
+			} else {
+				throw new ResourceNotFoundException();
+			}
+		} else {
+			follow = false;
+		}
+
+		// Validate that that no more URI elements are available.
+		if (!uriElements.isEmpty()) {
+			throw new ResourceNotFoundException();
+		}
+
+		// Begin sending replication sequence information to the client.
+		if (LOG.isLoggable(Level.FINER)) {
+			LOG.finer("New request details, includeData=" + includeData + ", sequenceNumber=" + nextSequenceNumber
+					+ ", tail=" + follow);
+		}
+		initiateSequenceWriting(ctx, contentType, nextSequenceNumber, follow);
+	}
+
+
+	@Override
+	protected void writeSequence(ChannelHandlerContext ctx, ChannelFuture future, long sequenceNumber) {
+		// We do not support sending new replication data until the previous
+		// send has completed.
+		if (chunkedFileChannel != null) {
+			throw new OsmosisRuntimeException(
+					"We cannot send new replication data until the previous write has completed");
+		}
+		
+		if (LOG.isLoggable(Level.FINEST)) {
+			LOG.finest("Sequence being written, includeData=" + includeData + ", sequenceNumber="
+					+ sequenceNumber);
+		}
+		
+		// We must save the future to attach to the final write.
+		sequenceFuture = future;
+
+		// Get the name of the replication data file.
+		File stateFile = getStateFile(sequenceNumber);
+		File dataFile = getDataFile(sequenceNumber);
+
+		// Load the contents of the state file.
+		ChannelBuffer stateFileBuffer = loadFile(stateFile);
+		
+		// Add a chunk length header.
+		stateFileBuffer = ChannelBuffers.wrappedBuffer(buildChunkHeader(stateFileBuffer.readableBytes()),
+				stateFileBuffer);
+
+		// Only include replication data if initially requested by the client
+		// and if this is not sequence 0.
+		if (includeData && sequenceNumber > 0) {
+			// Open the data file read for sending.
+			chunkedFileChannel = openFileChannel(dataFile);
+			fileSizeSent = false;
+		}
+
+		/*
+		 * Send the state file to the client. If replication data is to be sent
+		 * we will continue when we receive completion information via the
+		 * writeComplete method. We must create a new future now if we have more
+		 * data coming because we don't want the future of the current event to
+		 * fire until we're completely finished processing.
+		 */
+		ChannelFuture writeFuture;
+		if (chunkedFileChannel != null) {
+			writeFuture = Channels.future(ctx.getChannel());
+		} else {
+			writeFuture = sequenceFuture;
+		}
+		Channels.write(ctx, writeFuture, new DefaultHttpChunk(stateFileBuffer));
+	}
+
+
+	@Override
+	public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception {
+		if (chunkedFileChannel != null) {
+			// We have an open file channel so we are still sending replication
+			// data.
+			ChannelBuffer buffer;
+			ChannelFuture future;
+			if (!fileSizeSent) {
+				// Send a chunk header containing the size of the file.
+				ChannelBuffer fileSizeBuffer = buildChunkHeader(chunkedFileChannel.size());
+				fileSizeSent = true;
+				future = Channels.future(ctx.getChannel());
+				buffer = fileSizeBuffer;
+			} else {
+				// Send the next chunk to the client.
+				buffer = getFileChunk();
+				if (chunkedFileChannel != null) {
+					future = Channels.future(ctx.getChannel());
+				} else {
+					// This is the last write for this sequence so attach the original future.
+					future = sequenceFuture;
+				}
+			}
+			
+			// Write the data to the channel.
+			Channels.write(ctx, future, new DefaultHttpChunk(buffer));
+		} else {
+			super.writeComplete(ctx, e);
+		}
+	}
+
+
+	@Override
+	public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+		// Close the in-progress chunk file channel if it exists.
+		if (chunkedFileChannel != null) {
+			try {
+				chunkedFileChannel.close();
+			} catch (IOException ex) {
+				LOG.log(Level.WARNING, "Unable to close the replication data file.", ex);
+			}
+			chunkedFileChannel = null;
+		}
+		super.channelClosed(ctx, e);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClient.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClient.java
new file mode 100644
index 0000000..46a284d
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClient.java
@@ -0,0 +1,102 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+
+import org.jboss.netty.bootstrap.ClientBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * This class creates a HTTP client that connects to a sequence server, listens
+ * for updated sequences as they are received, and notifies any configured
+ * listeners.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceClient {
+
+	private InetSocketAddress serverAddress;
+	private SequenceClientChannelPipelineFactory channelPipelineFactory;
+	/**
+	 * A flag used only by the external control thread to remember if the server
+	 * has been started or not.
+	 */
+	private boolean masterRunning;
+	/**
+	 * The factory for all processing threads.
+	 */
+	private ChannelFactory factory;
+	/**
+	 * The channel used to receive sequence updates from the server.
+	 */
+	private Channel channel;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param serverAddress
+	 *            The address of the sequence server providing notification of
+	 *            updated sequence numbers.
+	 * @param channelPipelineFactory
+	 *            The factory for creating channel pipelines for new client
+	 *            connections.
+	 */
+	public SequenceClient(InetSocketAddress serverAddress,
+			SequenceClientChannelPipelineFactory channelPipelineFactory) {
+		this.serverAddress = serverAddress;
+		this.channelPipelineFactory = channelPipelineFactory;
+	}
+
+
+	/**
+	 * Starts the client.
+	 */
+	public void start() {
+		if (masterRunning) {
+			throw new OsmosisRuntimeException("The server has already been started");
+		}
+
+		// Mark the client as running.
+		masterRunning = true;
+
+		// Create the processing thread pools.
+		factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
+
+		ClientBootstrap bootstrap = new ClientBootstrap(factory);
+		bootstrap.setPipelineFactory(channelPipelineFactory);
+		bootstrap.setOption("tcpNoDelay", true);
+		bootstrap.setOption("keepAlive", true);
+		ChannelFuture future = bootstrap.connect(serverAddress);
+
+		// Get a reference to the channel.
+		channel = future.getChannel();
+
+		// Wait for the connection attempt to complete.
+		future.awaitUninterruptibly();
+
+		// Abort if the startup failed.
+		if (!future.isSuccess()) {
+			throw new OsmosisRuntimeException("Unable to launch sequence client.");
+		}
+	}
+
+
+	/**
+	 * Stops the client. This must be called in all cases even if start failed.
+	 */
+	public void stop() {
+		if (masterRunning) {
+			channel.close().awaitUninterruptibly();
+
+			factory.releaseExternalResources();
+			masterRunning = false;
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientChannelPipelineFactory.java
new file mode 100644
index 0000000..395e679
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientChannelPipelineFactory.java
@@ -0,0 +1,47 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.handler.codec.http.HttpClientCodec;
+
+
+/**
+ * Builds Netty channel pipelines for new connections to servers.
+ * 
+ * 
+ * @author Brett Henderson
+ */
+public abstract class SequenceClientChannelPipelineFactory implements
+		ChannelPipelineFactory {
+
+	private SequenceClientControl centralControl;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 */
+	public SequenceClientChannelPipelineFactory(SequenceClientControl control) {
+		this.centralControl = control;
+	}
+
+
+	/**
+	 * Creates a handler to be used for processing channel messages.
+	 * 
+	 * @param control
+	 *            The control object used to send event notifications.
+	 * @return The channel handler.
+	 */
+	protected abstract SequenceClientHandler createHandler(SequenceClientControl control);
+
+
+	@Override
+	public ChannelPipeline getPipeline() throws Exception {
+		return Channels.pipeline(new HttpClientCodec(), createHandler(centralControl));
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientControl.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientControl.java
new file mode 100644
index 0000000..fe03353
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientControl.java
@@ -0,0 +1,17 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+/**
+ * This interface provides Netty handlers executing in worker threads with
+ * access to sequence client control methods.
+ * 
+ * @author Brett Henderson
+ */
+public interface SequenceClientControl {
+
+	/**
+	 * Allows a Netty handler to tell the controller that the channel has been
+	 * closed.
+	 */
+	void channelClosed();
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientHandler.java
new file mode 100644
index 0000000..ab27a76
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientHandler.java
@@ -0,0 +1,137 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.nio.channels.ClosedChannelException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
+import org.jboss.netty.handler.codec.http.HttpChunk;
+import org.jboss.netty.handler.codec.http.HttpMethod;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.handler.codec.http.HttpResponse;
+import org.jboss.netty.handler.codec.http.HttpResponseStatus;
+import org.jboss.netty.handler.codec.http.HttpVersion;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Netty handler for receiving replication sequence information and notifying
+ * listeners.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class SequenceClientHandler extends SimpleChannelHandler {
+
+	private static final Logger LOG = Logger.getLogger(SequenceClientHandler.class.getName());
+
+	private SequenceClientControl control;
+	private String serverHost;
+	private boolean midStream;
+	private boolean active;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param serverHost
+	 *            The name of the host system running the sequence server.
+	 */
+	public SequenceClientHandler(SequenceClientControl control, String serverHost) {
+		this.control = control;
+		this.serverHost = serverHost;
+		
+		active = true;
+	}
+
+
+	/**
+	 * Gets the URI to request from the server to initialise message processing.
+	 * 
+	 * @return The request URI.
+	 */
+	protected abstract String getRequestUri();
+
+
+	@Override
+	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+		// Send a request to the server asking for sequence number
+		// notifications.
+		HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, getRequestUri());
+		request.addHeader("Host", serverHost);
+		Channels.write(ctx, e.getFuture(), request);
+
+		midStream = false;
+	}
+
+
+	/**
+	 * Processes the contents of a single HTTP chunk.
+	 * 
+	 * @param buffer
+	 *            The data contained in the chunk.
+	 */
+	protected abstract void processMessageData(ChannelBuffer buffer);
+
+
+	@Override
+	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+		if (active) {
+			ChannelBuffer buffer;
+	
+			if (!midStream) {
+				HttpResponse response = (HttpResponse) e.getMessage();
+				HttpResponseStatus status = response.getStatus();
+				if (!HttpResponseStatus.OK.equals(status)) {
+					throw new OsmosisRuntimeException("Received a " + status + " response from the server.");
+				}
+				buffer = response.getContent();
+				midStream = true;
+			} else {
+				HttpChunk chunk = (HttpChunk) e.getMessage();
+				buffer = chunk.getContent();
+			}
+	
+			// Perform implementation specific processing of the buffer contents.
+			if (buffer.readableBytes() > 0) {
+				processMessageData(buffer);
+			}
+		}
+	}
+
+
+	@Override
+	public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+		control.channelClosed();
+	}
+
+
+	@Override
+	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+		// When we close the channel we may still continue to receive some
+		// data. Flag that this should be ignored.
+		active = false;
+		
+		// Get the cause of the exception.
+		Throwable t = e.getCause();
+
+		// A ClosedChannelException occurs if the client disconnects and is not
+		// an error scenario.
+		if (!(t instanceof ClosedChannelException)) {
+			LOG.log(Level.SEVERE, "Error during processing for channel " + ctx.getChannel() + ".", t);
+		}
+
+		// We must stop sending to this client if any errors occur during
+		// processing.
+		e.getChannel().close();
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientRestartManager.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientRestartManager.java
new file mode 100644
index 0000000..5cccac9
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceClientRestartManager.java
@@ -0,0 +1,152 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * This class encapsulates the functionality required to manage restarts of a
+ * sequence client on failure.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceClientRestartManager {
+
+	private static final Logger LOG = Logger.getLogger(SequenceClientRestartManager.class.getName());
+	private static final int RESTART_DELAY = 60000;
+
+	private Lock controlLock;
+	private Condition controlCondition;
+	private ClientControl control;
+	private boolean clientRunning;
+
+
+	/**
+	 * Creates a new instance.
+	 */
+	public SequenceClientRestartManager() {
+		controlLock = new ReentrantLock();
+		controlCondition = controlLock.newCondition();
+
+		control = new ClientControl();
+	}
+
+
+	/**
+	 * Either thread can call this method when they wish to wait until an update
+	 * has been performed by the other thread.
+	 */
+	private void waitForUpdate() {
+		try {
+			controlCondition.await();
+
+		} catch (InterruptedException e) {
+			throw new OsmosisRuntimeException("Thread was interrupted.", e);
+		}
+	}
+
+
+	/**
+	 * Either thread can call this method when they wish to signal the other
+	 * thread that an update has occurred.
+	 */
+	private void signalUpdate() {
+		controlCondition.signal();
+	}
+
+
+	/**
+	 * Returns a sequence client control object to be used when creating a new
+	 * sequence client.
+	 * 
+	 * @return The sequence client controller.
+	 */
+	public SequenceClientControl getControl() {
+		return control;
+	}
+
+
+	/**
+	 * Runs the sequence client and restarts it if it fails. This sequence
+	 * client must have been created using a control listener returned from the
+	 * getControl method of this object.
+	 * 
+	 * @param sequenceClient
+	 *            The sequence client to manage.
+	 */
+	public void manageClient(SequenceClient sequenceClient) {
+		try {
+			// Run the client within a loop to allow client restarts if
+			// problems occur.
+			while (true) {
+				// Initialise the running flag. We must do this before we
+				// start the client because the client thread will set it to
+				// false when it finishes which may occur very soon after
+				// starting.
+				clientRunning = true;
+				try {
+					sequenceClient.start();
+				} catch (OsmosisRuntimeException e) {
+					// The client startup failed, so log the exception and try
+					// again in the next loop.
+					LOG.warning("Unable to start the sequence client, will retry in " + RESTART_DELAY
+							+ " milliseconds.");
+				}
+
+				// Wait for the client to stop.
+				try {
+					controlLock.lock();
+					while (clientRunning) {
+						waitForUpdate();
+					}
+				} finally {
+					controlLock.unlock();
+				}
+
+				// Stop the client explicitly which will close any remaining
+				// resources.
+				sequenceClient.stop();
+
+				// Wait for 1 minute between connection failures.
+				try {
+					Thread.sleep(RESTART_DELAY);
+				} catch (InterruptedException e) {
+					throw new OsmosisRuntimeException("Thread sleep failed between sequence number client invocations",
+							e);
+				}
+			}
+
+		} finally {
+			sequenceClient.stop();
+		}
+	}
+
+	/**
+	 * Internal class used to process event updates from the sequence number
+	 * client.
+	 * 
+	 * @author Brett Henderson
+	 */
+	private class ClientControl implements SequenceClientControl {
+
+		@Override
+		public void channelClosed() {
+			controlLock.lock();
+
+			try {
+				// The client has failed. We need to tell the master thread so
+				// that it can act accordingly (eg. restart the client).
+				clientRunning = false;
+				signalUpdate();
+
+			} finally {
+				controlLock.unlock();
+			}
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientChannelPipelineFactory.java
new file mode 100644
index 0000000..4d3f458
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientChannelPipelineFactory.java
@@ -0,0 +1,38 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+/**
+ * Builds Netty channel pipelines for new connections to servers.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceNumberClientChannelPipelineFactory extends SequenceClientChannelPipelineFactory {
+
+	private SequenceNumberClientListener sequenceNumberListener;
+	private String serverHost;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param sequenceNumberListener
+	 *            This will be notified when new sequence numbers are received.
+	 * @param serverHost
+	 *            The name of the host system running the sequence server.
+	 */
+	public SequenceNumberClientChannelPipelineFactory(SequenceClientControl control,
+			SequenceNumberClientListener sequenceNumberListener, String serverHost) {
+		super(control);
+
+		this.serverHost = serverHost;
+		this.sequenceNumberListener = sequenceNumberListener;
+	}
+
+
+	@Override
+	protected SequenceClientHandler createHandler(SequenceClientControl control) {
+		return new SequenceNumberClientHandler(control, sequenceNumberListener, serverHost);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientHandler.java
new file mode 100644
index 0000000..1a0a9d0
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientHandler.java
@@ -0,0 +1,60 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.util.CharsetUtil;
+
+
+/**
+ * Netty handler for receiving replication sequence numbers and notifying
+ * listeners.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceNumberClientHandler extends SequenceClientHandler {
+
+	private static final Logger LOG = Logger.getLogger(SequenceNumberClientHandler.class.getName());
+
+	private SequenceNumberClientListener sequenceNumberListener;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 * @param sequenceNumberListener
+	 *            This will be notified when new sequence numbers are received.
+	 * @param serverHost
+	 *            The name of the host system running the sequence server.
+	 */
+	public SequenceNumberClientHandler(SequenceClientControl control,
+			SequenceNumberClientListener sequenceNumberListener, String serverHost) {
+		super(control, serverHost);
+
+		this.sequenceNumberListener = sequenceNumberListener;
+	}
+
+
+	@Override
+	protected String getRequestUri() {
+		return "/sequenceNumber/current/tail";
+	}
+
+
+	@Override
+	protected void processMessageData(ChannelBuffer buffer) {
+		// The readable data is the sequence number in string form.
+		String sequenceNumberString = buffer.toString(CharsetUtil.UTF_8);
+		long sequenceNumber = Long.parseLong(sequenceNumberString);
+		if (LOG.isLoggable(Level.FINEST)) {
+			LOG.finest("Received sequence number " + sequenceNumber);
+		}
+
+		// Send the new sequence number notification.
+		sequenceNumberListener.notifySequenceNumber(sequenceNumber);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientListener.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientListener.java
new file mode 100644
index 0000000..1bb081e
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberClientListener.java
@@ -0,0 +1,20 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+/**
+ * Used by SequenceNumberClientHandler to notify a listener about received
+ * sequence numbers.
+ * 
+ * @author Brett Henderson
+ */
+public interface SequenceNumberClientListener {
+
+	/**
+	 * Allows a Netty handler to notify when a new sequence number has been
+	 * received.
+	 * 
+	 * @param sequenceNumber
+	 *            The received sequence number.
+	 */
+	void notifySequenceNumber(long sequenceNumber);
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerChannelPipelineFactory.java
new file mode 100644
index 0000000..8d86390
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerChannelPipelineFactory.java
@@ -0,0 +1,15 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+/**
+ * Builds Netty channel pipelines for new client connections.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceNumberServerChannelPipelineFactory extends SequenceServerChannelPipelineFactory {
+
+	@Override
+	protected SequenceServerHandler createHandler(SequenceServerControl control) {
+		return new SequenceNumberServerHandler(control);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerHandler.java
new file mode 100644
index 0000000..cc73475
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceNumberServerHandler.java
@@ -0,0 +1,118 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.util.CharsetUtil;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * A sequence server handler implementation that sends the sequence number
+ * itself.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceNumberServerHandler extends SequenceServerHandler {
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 */
+	public SequenceNumberServerHandler(SequenceServerControl control) {
+		super(control);
+	}
+
+
+	@Override
+	protected void handleRequest(ChannelHandlerContext ctx, HttpRequest request) {
+		final String sequenceNumberUri = "sequenceNumber";
+		final String contentType = "text/plain";
+
+		// Split the request Uri into its path elements.
+		String uri = request.getUri();
+		if (!uri.startsWith("/")) {
+			throw new OsmosisRuntimeException("Uri doesn't start with a / character: " + uri);
+		}
+		Queue<String> uriElements = new LinkedList<String>(Arrays.asList(uri.split("/")));
+		
+		// First element is empty due to leading '/', unless there is only a '/'
+		// in which case there will be no elements.
+		if (uriElements.size() > 0) {
+			uriElements.remove();
+		}
+
+		// First element must be the sequence number base uri.
+		if (uriElements.isEmpty() || !sequenceNumberUri.equals(uriElements.remove())) {
+			throw new ResourceNotFoundException();
+		}
+
+		/*
+		 * The next element determines which replication number to start from.
+		 * The request is one of "current" or N where is the last sequence
+		 * number received by the client.
+		 */
+		long nextSequenceNumber;
+		if (uriElements.isEmpty()) {
+			throw new ResourceNotFoundException();
+		}
+		String sequenceStartString = uriElements.remove();
+		if ("current".equals(sequenceStartString)) {
+			nextSequenceNumber = getControl().getLatestSequenceNumber();
+		} else {
+			try {
+				nextSequenceNumber = Long.parseLong(sequenceStartString);
+			} catch (NumberFormatException e) {
+				throw new BadRequestException("Requested sequence number of " + sequenceStartString
+						+ " is not a number.");
+			}
+		}
+
+		// If the next element exists and is "tail" it means that the client
+		// wants to stay connected and receive updated sequences as they become
+		// available.
+		boolean follow;
+		if (!uriElements.isEmpty()) {
+			String tailElement = uriElements.remove();
+			if ("tail".equals(tailElement)) {
+				follow = true;
+			} else {
+				throw new ResourceNotFoundException();
+			}
+		} else {
+			follow = false;
+		}
+		
+		// Validate that that no more URI elements are available.
+		if (!uriElements.isEmpty()) {
+			throw new ResourceNotFoundException();
+		}
+
+		// Begin sending replication sequence information to the client.
+		initiateSequenceWriting(ctx, contentType, nextSequenceNumber, follow);
+	}
+
+
+	@Override
+	protected void writeSequence(ChannelHandlerContext ctx, ChannelFuture future, long sequenceNumber) {
+		// Convert the sequence to a string and then a buffer.
+		ChannelBuffer buffer = ChannelBuffers.copiedBuffer(Long.toString(sequenceNumber), CharsetUtil.UTF_8);
+
+		// Wrap the buffer in a HTTP chunk.
+		DefaultHttpChunk chunk = new DefaultHttpChunk(buffer);
+
+		// Pass the chunk downstream.
+		Channels.write(ctx, future, chunk);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServer.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServer.java
new file mode 100644
index 0000000..646908f
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServer.java
@@ -0,0 +1,393 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * This class creates a HTTP server that sends updated replication sequences to
+ * clients. Once started it is notified of updated sequence numbers as they
+ * occur and will pass the sequence data to listening clients. The sequence data
+ * is implementation dependent.
+ * 
+ * @author Brett Henderson
+ */
+public class SequenceServer implements SequenceServerControl {
+
+	private static final Logger LOG = Logger.getLogger(SequenceServer.class.getName());
+
+	private int port;
+	private SequenceServerChannelPipelineFactory channelPipelineFactory;
+	/**
+	 * Limits shared data access to one thread at a time.
+	 */
+	private Lock sharedLock;
+	/**
+	 * A flag used to remember if the server has been started or not.
+	 */
+	private boolean serverStarted;
+	private long sequenceNumber;
+	private ChannelFactory factory;
+	private ChannelGroup allChannels;
+	private List<Channel> waitingChannels;
+	private ExecutorService sendService;
+	private int totalRequests;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param port
+	 *            The port number to listen on.
+	 * @param channelPipelineFactory
+	 *            The factory for creating channel pipelines for new client
+	 *            connections.
+	 */
+	public SequenceServer(int port, SequenceServerChannelPipelineFactory channelPipelineFactory) {
+		this.port = port;
+		this.channelPipelineFactory = channelPipelineFactory;
+
+		// Provide handlers with access to control functions.
+		channelPipelineFactory.setControl(this);
+
+		// Create the thread synchronisation primitives.
+		sharedLock = new ReentrantLock();
+
+		// Create the list of channels waiting to be notified about a new
+		// sequence.
+		waitingChannels = new ArrayList<Channel>();
+	}
+
+
+	/**
+	 * Returns the port that the server is listening on.
+	 * 
+	 * @return The listening port.
+	 */
+	public int getPort() {
+		return port;
+	}
+
+
+	/**
+	 * Starts the server.
+	 * 
+	 * @param initialSequenceNumber
+	 *            The initial sequence number.
+	 */
+	public void start(long initialSequenceNumber) {
+		sharedLock.lock();
+
+		try {
+			if (serverStarted) {
+				throw new OsmosisRuntimeException("The server has already been started");
+			}
+
+			sequenceNumber = initialSequenceNumber;
+			totalRequests = 0;
+
+			// Create a channel group to hold all channels for use during
+			// shutdown.
+			allChannels = new DefaultChannelGroup("sequence-server");
+
+			// Create the processing thread pools.
+			factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
+					Executors.newCachedThreadPool());
+
+			// Launch the server.
+			ServerBootstrap bootstrap = new ServerBootstrap(factory);
+			bootstrap.setPipelineFactory(channelPipelineFactory);
+			bootstrap.setOption("child.tcpNoDelay", true);
+			bootstrap.setOption("child.keepAlive", true);
+			Channel serverChannel = bootstrap.bind(new InetSocketAddress(port));
+			allChannels.add(serverChannel);
+
+			// Get the port that the server is listening on. This may be
+			// dynamically allocated if 0 was originally specified.
+			InetSocketAddress address = (InetSocketAddress) serverChannel.getLocalAddress();
+			port = address.getPort();
+			if (LOG.isLoggable(Level.INFO)) {
+				LOG.info("Server listening on port " + port);
+			}
+
+			/*
+			 * Create our own background sending thread. Initiating the send of
+			 * a sequence should be relatively light on CPU so one thread should
+			 * keep up with a large number of clients. However we may trigger a
+			 * large number of messages at once which might cause a
+			 * multi-threaded pool to spawn a large number of threads for very
+			 * short lived processing.
+			 */
+			sendService = Executors.newSingleThreadExecutor();
+
+			// Server startup has succeeded.
+			serverStarted = true;
+
+		} finally {
+			sharedLock.unlock();
+		}
+	}
+
+
+	/**
+	 * Notifies that server of a new sequence number.
+	 * 
+	 * @param newSequenceNumber
+	 *            The new sequence number.
+	 */
+	public void update(long newSequenceNumber) {
+		sharedLock.lock();
+		try {
+			if (!serverStarted) {
+				throw new OsmosisRuntimeException("The server has not been started");
+			}
+
+			if (LOG.isLoggable(Level.FINER)) {
+				LOG.finer("Updating with new sequence " + newSequenceNumber);
+			}
+
+			// Verify that the new sequence number is not less than the existing
+			// sequence number.
+			if (newSequenceNumber < sequenceNumber) {
+				throw new OsmosisRuntimeException("Received sequence number " + newSequenceNumber
+						+ " from server, expected " + sequenceNumber + " or greater");
+			}
+			long oldSequenceNumber = sequenceNumber;
+			sequenceNumber = newSequenceNumber;
+
+			// If the new sequence number is greater than our existing number
+			// then we can send updates to our clients.
+			if (oldSequenceNumber < sequenceNumber) {
+				final long nextSequenceNumber = oldSequenceNumber + 1;
+				/*
+				 * Create a new waiting channels list and process from the
+				 * original. This is necessary because some channels may get
+				 * added back in during processing causing a concurrent
+				 * modification exception. Due to the Netty implementation, if a
+				 * write operation completes before we get a chance to register
+				 * the completion listener, the listener will run within this
+				 * thread and that will mean the channel will need to be added
+				 * to the waiting list before we complete sending messages to
+				 * all the other channels.
+				 */
+				List<Channel> existingWaitingChannels = waitingChannels;
+				waitingChannels = new ArrayList<Channel>();
+				for (final Channel channel : existingWaitingChannels) {
+					if (LOG.isLoggable(Level.FINEST)) {
+						LOG.finest("Waking up channel " + channel + " with sequence " + sequenceNumber);
+					}
+					// Submit the request via the worker thread.
+					sendService.submit(new Runnable() {
+						@Override
+						public void run() {
+							sendSequence(channel, nextSequenceNumber, true);
+						}
+					});
+				}
+			}
+
+		} finally {
+			sharedLock.unlock();
+		}
+	}
+
+
+	/**
+	 * Stops the server.
+	 */
+	public void stop() {
+		sharedLock.lock();
+
+		try {
+			if (serverStarted) {
+				// Shutdown our background worker thread.
+				sendService.shutdownNow();
+
+				// Shutdown the Netty framework.
+				allChannels.close().awaitUninterruptibly();
+				factory.releaseExternalResources();
+
+				// Clear our control flag.
+				serverStarted = false;
+			}
+		} finally {
+			sharedLock.unlock();
+		}
+	}
+
+
+	/**
+	 * Sends the specified sequence to the channel. If follow is specified, the
+	 * channel will be held open and follow up calls will be made to
+	 * determineNextChannelAction with this channel and sequence number when the
+	 * operation completes. If follow is not specified, the channel will be
+	 * closed when the operation completes.
+	 * 
+	 * @param channel
+	 *            The channel.
+	 * @param currentSequenceNumber
+	 *            The sequence to be sent.
+	 * @param follow
+	 *            If true, the channel will be held open and updated sequences
+	 *            sent as they are arrive.
+	 */
+	private void sendSequence(final Channel channel, final long currentSequenceNumber, final boolean follow) {
+		// Write the sequence number to the channel.
+		ChannelFuture future = channel.write(currentSequenceNumber);
+
+		if (follow) {
+			// Upon completion of this write, check to see whether a new
+			// sequence must be sent or whether we should wait for further
+			// updates.
+			future.addListener(new ChannelFutureListener() {
+				@Override
+				public void operationComplete(ChannelFuture future) throws Exception {
+					// Only send more data if the write was successful.
+					if (future.isSuccess()) {
+						determineNextChannelAction(channel, currentSequenceNumber + 1, follow);
+					}
+				}
+			});
+		} else {
+			// Upon completion of this write, close the channel.
+			future.addListener(new ChannelFutureListener() {
+				@Override
+				public void operationComplete(ChannelFuture future) throws Exception {
+					channel.close();
+				}
+			});
+		}
+	}
+
+
+	private void determineNextChannelActionImpl(Channel channel, long nextSequenceNumber, boolean follow) {
+		long currentSequenceNumber;
+		boolean sequenceAvailable;
+
+		// We can only access the master sequence number and waiting channels
+		// while we have the lock.
+		sharedLock.lock();
+		try {
+			currentSequenceNumber = sequenceNumber;
+
+			// Check if the next sequence number is available yet.
+			sequenceAvailable = nextSequenceNumber <= currentSequenceNumber;
+
+			// If the sequence is not available, make sure that the client
+			// hasn't requested a sequence number more than one past current.
+			if (!sequenceAvailable) {
+				if ((nextSequenceNumber - currentSequenceNumber) > 1) {
+					channel.close();
+					throw new OsmosisRuntimeException("Requested sequence number " + nextSequenceNumber
+							+ " is more than 1 past current number " + currentSequenceNumber);
+				}
+			}
+
+			// If the sequence isn't available we add the channel to the list
+			// waiting for a new sequence notification.
+			if (!sequenceAvailable) {
+				if (LOG.isLoggable(Level.FINEST)) {
+					LOG.finest("Next sequence " + nextSequenceNumber + " is not available yet so adding channel "
+							+ channel + " to waiting list.");
+				}
+				waitingChannels.add(channel);
+			}
+		} finally {
+			sharedLock.unlock();
+		}
+
+		// Send the sequence if it is available.
+		if (sequenceAvailable) {
+			if (LOG.isLoggable(Level.FINEST)) {
+				LOG.finest("Next sequence " + nextSequenceNumber + " is available.");
+			}
+			sendSequence(channel, nextSequenceNumber, follow);
+		}
+	}
+
+
+	/**
+	 * Allows a Netty handler to notify the controller that the channel is ready
+	 * for more data. If the controller has new sequence information available
+	 * it will send it, otherwise it will add the channel to the waiting list.
+	 * This method will perform execution in a background worker thread and will
+	 * return immediately.
+	 * 
+	 * @param channel
+	 *            The client channel.
+	 * @param nextSequenceNumber
+	 *            The sequence number that the client needs to be sent next.
+	 * @param follow
+	 *            If true, the channel will be held open and updated sequences
+	 *            sent as they arrive.
+	 */
+	public void determineNextChannelAction(final Channel channel, final long nextSequenceNumber, final boolean follow) {
+		/*
+		 * We submit new requests from our own worker thread instead of using
+		 * the Netty IO thread. This is not to free up IO threads because
+		 * initiating the send of a sequence is a relatively lightweight
+		 * operation. It is to avoid the situation where a Netty IO thread
+		 * encounters a stack overflow when it completes writing a sequence,
+		 * then finds another available and sends it, then finds another
+		 * available and so on in a recursive fashion.
+		 */
+		sendService.submit(new Runnable() {
+			@Override
+			public void run() {
+				determineNextChannelActionImpl(channel, nextSequenceNumber, follow);
+			}
+		});
+	}
+
+
+	@Override
+	public long getLatestSequenceNumber() {
+		// Get the current sequence number within the lock.
+		sharedLock.lock();
+		try {
+			return sequenceNumber;
+		} finally {
+			sharedLock.unlock();
+		}
+	}
+
+
+	@Override
+	public void registerChannel(Channel channel) {
+		// Update the total requests counter within the lock.
+		sharedLock.lock();
+		try {
+			totalRequests++;
+		} finally {
+			sharedLock.unlock();
+		}
+		
+		allChannels.add(channel);
+	}
+
+
+	@Override
+	public ServerStatistics getStatistics() {
+		// The all channels collection contains the server channel which must be
+		// removed from the count to get the number of client connections.
+		return new ServerStatistics(totalRequests, allChannels.size() - 1);
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerChannelPipelineFactory.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerChannelPipelineFactory.java
new file mode 100644
index 0000000..b31cf02
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerChannelPipelineFactory.java
@@ -0,0 +1,45 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.handler.codec.http.HttpServerCodec;
+
+
+/**
+ * Builds Netty channel pipelines for new client connections.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class SequenceServerChannelPipelineFactory implements ChannelPipelineFactory {
+
+	private SequenceServerControl centralControl;
+
+
+	/**
+	 * Provides handlers with access to server control functions.
+	 * 
+	 * @param control
+	 *            The new control object.
+	 */
+	public void setControl(SequenceServerControl control) {
+		this.centralControl = control;
+	}
+
+
+	/**
+	 * Creates a handler to be used for processing channel messages.
+	 * 
+	 * @param control
+	 *            The control object used to send event notifications.
+	 * @return The channel handler.
+	 */
+	protected abstract SequenceServerHandler createHandler(SequenceServerControl control);
+
+
+	@Override
+	public ChannelPipeline getPipeline() throws Exception {
+		return Channels.pipeline(new HttpServerCodec(), createHandler(centralControl));
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerControl.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerControl.java
new file mode 100644
index 0000000..c6ea1b5
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerControl.java
@@ -0,0 +1,57 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import org.jboss.netty.channel.Channel;
+
+
+/**
+ * This interface provides Netty handlers executing in worker threads with
+ * access to sequence server control methods.
+ * 
+ * @author Brett Henderson
+ */
+public interface SequenceServerControl {
+
+	/**
+	 * Allows a Netty handler to request the latest sequence number from the
+	 * controller.
+	 * 
+	 * @return The latest sequence number.
+	 */
+	long getLatestSequenceNumber();
+
+
+	/**
+	 * Allows a Netty handler to notify the controller that the channel is ready
+	 * for more data. If the controller has new sequence information available
+	 * it will send it, otherwise it will add the channel to the waiting list.
+	 * 
+	 * @param channel
+	 *            The client channel.
+	 * @param nextSequenceNumber
+	 *            The sequence number that the client needs to be sent next.
+	 * @param follow
+	 *            If true, the channel will be held open and updated sequences
+	 *            sent as they arrive.
+	 */
+	void determineNextChannelAction(Channel channel, long nextSequenceNumber, boolean follow);
+
+
+	/**
+	 * Allows a Netty handler to register a channel with the main controller.
+	 * This allows the controller to close the channel when the server shuts
+	 * down.
+	 * 
+	 * @param channel
+	 *            The channel to be registered.
+	 */
+	void registerChannel(Channel channel);
+	
+	
+	/**
+	 * Gets the runtime statistics of the server.
+	 * 
+	 * @return The server statistics.
+	 */
+	ServerStatistics getStatistics();
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerHandler.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerHandler.java
new file mode 100644
index 0000000..1f7fe97
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/SequenceServerHandler.java
@@ -0,0 +1,369 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+import java.net.InetSocketAddress;
+import java.nio.channels.ClosedChannelException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.handler.codec.http.HttpResponseStatus;
+import org.jboss.netty.handler.codec.http.HttpVersion;
+import org.jboss.netty.util.CharsetUtil;
+
+
+/**
+ * Netty handler for sending replication sequence numbers to clients.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class SequenceServerHandler extends SimpleChannelHandler {
+
+	private static final Logger LOG = Logger.getLogger(SequenceServerHandler.class.getName());
+
+	private SequenceServerControl control;
+	private long currentSequenceNumber;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param control
+	 *            Provides the Netty handlers with access to the controller.
+	 */
+	public SequenceServerHandler(SequenceServerControl control) {
+		this.control = control;
+	}
+
+
+	/**
+	 * Gets the central control object.
+	 * 
+	 * @return The controller.
+	 */
+	protected SequenceServerControl getControl() {
+		return control;
+	}
+
+
+	@Override
+	public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
+		control.registerChannel(e.getChannel());
+	}
+
+
+	/**
+	 * Writes a HTTP 404 response to the client.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 * @param requestedUri
+	 *            The URI requested by the client.
+	 */
+	private void writeResourceNotFound(final ChannelHandlerContext ctx, String requestedUri) {
+		// Write the HTTP header to the client.
+		DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.NOT_FOUND);
+		response.addHeader("Content-Type", "text/plain");
+
+		// Send the 404 message to the client.
+		ChannelBuffer buffer = ChannelBuffers.copiedBuffer("The requested resource does not exist: " + requestedUri,
+				CharsetUtil.UTF_8);
+		response.setContent(buffer);
+
+		// Write the header. Use a new future because the future we've been
+		// passed is for upstream.
+		ChannelFuture headerFuture = Channels.future(ctx.getChannel());
+		Channels.write(ctx, headerFuture, response);
+
+		// Wait for the previous operation to finish and then close the channel.
+		headerFuture.addListener(new ChannelFutureListener() {
+			@Override
+			public void operationComplete(ChannelFuture future) {
+				ctx.getChannel().close();
+			}
+		});
+	}
+
+
+	/**
+	 * Writes a HTTP 410 response to the client.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 * @param requestedUri
+	 *            The URI requested by the client.
+	 */
+	private void writeResourceGone(final ChannelHandlerContext ctx, String requestedUri) {
+		// Write the HTTP header to the client.
+		DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.GONE);
+		response.addHeader("Content-Type", "text/plain");
+
+		// Send the 410 message to the client.
+		ChannelBuffer buffer = ChannelBuffers.copiedBuffer("The requested resource is no longer available: "
+				+ requestedUri, CharsetUtil.UTF_8);
+		response.setContent(buffer);
+
+		// Write the header. Use a new future because the future we've been
+		// passed is for upstream.
+		ChannelFuture headerFuture = Channels.future(ctx.getChannel());
+		Channels.write(ctx, headerFuture, response);
+
+		// Wait for the previous operation to finish and then close the channel.
+		headerFuture.addListener(new ChannelFutureListener() {
+			@Override
+			public void operationComplete(ChannelFuture future) {
+				ctx.getChannel().close();
+			}
+		});
+	}
+
+
+	/**
+	 * Writes a HTTP 400 response to the client.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 * @param requestedUri
+	 *            The URI requested by the client.
+	 * @param errorMessage
+	 *            Further information about why the request is bad.
+	 */
+	private void writeBadRequest(final ChannelHandlerContext ctx, String requestedUri,
+			String errorMessage) {
+		final String newLine = "\r\n";
+
+		// Write the HTTP header to the client.
+		DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.NOT_FOUND);
+		response.addHeader("Content-Type", "text/plain");
+
+		// Send the 400 message to the client.
+		StringBuilder messageBuilder = new StringBuilder();
+		messageBuilder.append("Bad Request").append(newLine);
+		messageBuilder.append("Message: ").append(errorMessage).append(newLine);
+		messageBuilder.append("Requested URI: ").append(requestedUri).append(newLine);
+		ChannelBuffer buffer = ChannelBuffers.copiedBuffer(messageBuilder.toString(), CharsetUtil.UTF_8);
+		response.setContent(buffer);
+
+		// Write the header. Use a new future because the future we've been
+		// passed is for upstream.
+		ChannelFuture headerFuture = Channels.future(ctx.getChannel());
+		Channels.write(ctx, headerFuture, response);
+
+		// Wait for the previous operation to finish and then close the channel.
+		headerFuture.addListener(new ChannelFutureListener() {
+			@Override
+			public void operationComplete(ChannelFuture future) {
+				ctx.getChannel().close();
+			}
+		});
+	}
+
+
+	/**
+	 * Writes server statistics to the client.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 */
+	private void writeStatistics(final ChannelHandlerContext ctx) {
+		final String newLine = "\r\n";
+		
+		ServerStatistics statistics = control.getStatistics();
+
+		// Write the HTTP header to the client.
+		DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK);
+		response.addHeader("Content-Type", "text/plain");
+
+		// Send the statistics message to the client.
+		StringBuilder messageBuilder = new StringBuilder();
+		messageBuilder.append("Server Statistics").append(newLine);
+		messageBuilder.append("Total Requests: ").append(statistics.getTotalRequests()).append(newLine);
+		messageBuilder.append("Active Connections: ").append(statistics.getActiveConnections()).append(newLine);
+		ChannelBuffer buffer = ChannelBuffers.copiedBuffer(messageBuilder.toString(), CharsetUtil.UTF_8);
+		response.setContent(buffer);
+
+		// Write the header. Use a new future because the future we've been
+		// passed is for upstream.
+		ChannelFuture headerFuture = Channels.future(ctx.getChannel());
+		Channels.write(ctx, headerFuture, response);
+
+		// Wait for the previous operation to finish and then close the channel.
+		headerFuture.addListener(new ChannelFutureListener() {
+			@Override
+			public void operationComplete(ChannelFuture future) {
+				ctx.getChannel().close();
+			}
+		});
+	}
+
+
+	/**
+	 * Writes sequence data to the client. If follow is set, it allows
+	 * continuous updates to be streamed to the client.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 * @param contentType
+	 *            The content type to set on the HTTP response.
+	 * @param requestedSequenceNumber
+	 *            The requested sequence number. Sending will start from this
+	 *            number.
+	 * @param follow
+	 *            If true, continuous updates will be sent to the client.
+	 */
+	protected void initiateSequenceWriting(final ChannelHandlerContext ctx,
+			String contentType, final long requestedSequenceNumber, final boolean follow) {
+		// Create the HTTP header to send to the client.
+		DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
+		response.addHeader("Content-Type", contentType);
+		response.setChunked(true);
+		response.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
+
+		// Write the header. We must use a new future because the future we've
+		// been passed is for upstream.
+		ChannelFuture headerFuture = Channels.future(ctx.getChannel());
+		Channels.write(ctx, headerFuture, response);
+
+		// Wait for the previous operation to finish and then start sending
+		// sequence numbers to this channel.
+		headerFuture.addListener(new ChannelFutureListener() {
+			@Override
+			public void operationComplete(ChannelFuture future) throws Exception {
+				if (future.isSuccess()) {
+					control.determineNextChannelAction(ctx.getChannel(), requestedSequenceNumber, follow);
+				}
+			}
+		});
+	}
+
+
+	/**
+	 * Parses the request and initialises the response processing, typically by
+	 * calling the writeSequence method.
+	 * 
+	 * @param ctx
+	 *            The Netty context.
+	 * @param request
+	 *            The client request.
+	 */
+	protected abstract void handleRequest(ChannelHandlerContext ctx, HttpRequest request);
+
+
+	@Override
+	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+		// We have received a message from the client which is a HTTP request.
+		HttpRequest request = (HttpRequest) e.getMessage();
+		
+		InetSocketAddress remoteAddress = (InetSocketAddress) ctx.getChannel().getRemoteAddress();
+		if (LOG.isLoggable(Level.FINE)) {
+			LOG.fine("Received new request from " + remoteAddress.getAddress().getHostAddress() + ":"
+					+ remoteAddress.getPort());
+		}
+
+		// Process the HTTP request.
+		try {
+			// Check if this is a request to a generic URL. If it isn't
+			// something we support then delegate to the specific handler.
+			if (request.getUri().equals("/statistics")) {
+				writeStatistics(ctx);
+			} else {
+				handleRequest(ctx, request);
+			}
+			
+		} catch (ResourceNotFoundException ex) {
+			writeResourceNotFound(ctx, request.getUri());
+		} catch (ResourceGoneException ex) {
+			writeResourceGone(ctx, request.getUri());
+		} catch (BadRequestException ex) {
+			writeBadRequest(ctx, request.getUri(), ex.getMessage());
+		}
+	}
+
+
+	/**
+	 * Convert the sequence number to sequence data and write to the channel.
+	 * 
+	 * @param ctx
+	 *            The channel handler context.
+	 * @param future
+	 *            The future for current processing.
+	 * @param sequenceNumber
+	 *            The sequence number to be written.
+	 */
+	protected abstract void writeSequence(ChannelHandlerContext ctx, ChannelFuture future, long sequenceNumber);
+
+
+	@Override
+	public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+		// The message event is a Long containing the sequence number.
+		currentSequenceNumber = (Long) e.getMessage();
+
+		// Call the concrete implementation to convert the sequence to writable
+		// data.
+		writeSequence(ctx, e.getFuture(), currentSequenceNumber);
+	}
+
+
+	@Override
+	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+		// Get the cause of the exception.
+		Throwable t = e.getCause();
+
+		// A ClosedChannelException occurs if the client disconnects and is not
+		// an error scenario.
+		if (!(t instanceof ClosedChannelException)) {
+			LOG.log(Level.SEVERE, "Error during processing for channel " + ctx.getChannel() + ".", t);
+		}
+
+		// We must stop sending to this client if any errors occur during
+		// processing.
+		e.getChannel().close();
+	}
+
+	/**
+	 * Used during request parsing to notify that the requested URI could not be
+	 * found.
+	 */
+	protected static class ResourceNotFoundException extends RuntimeException {
+		private static final long serialVersionUID = -1L;
+	}
+
+	/**
+	 * Used during request parsing to notify that the request is invalid in some
+	 * way.
+	 */
+	protected static class BadRequestException extends RuntimeException {
+		private static final long serialVersionUID = -1L;
+
+
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param message
+		 *            The error message.
+		 */
+		public BadRequestException(String message) {
+			super(message);
+		}
+	}
+
+	/**
+	 * Used during request parsing to notify that the requested URI is no longer
+	 * available.
+	 */
+	protected static class ResourceGoneException extends RuntimeException {
+		private static final long serialVersionUID = -1L;
+	}
+}
diff --git a/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ServerStatistics.java b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ServerStatistics.java
new file mode 100644
index 0000000..a07dbb0
--- /dev/null
+++ b/osmosis-replication-http/src/main/java/org/openstreetmap/osmosis/replicationhttp/v0_6/impl/ServerStatistics.java
@@ -0,0 +1,46 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
+
+/**
+ * Captures statistics for a sequence server.
+ * 
+ * @author Brett Henderson
+ */
+public class ServerStatistics {
+	private int totalRequests;
+	private int activeConnections;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param totalRequests
+	 *            The total requests handled by the server.
+	 * @param activeConnections
+	 *            The current number of active connections.
+	 */
+	public ServerStatistics(int totalRequests, int activeConnections) {
+		this.totalRequests = totalRequests;
+		this.activeConnections = activeConnections;
+	}
+
+
+	/**
+	 * Gets the total number of requests handled by the server.
+	 * 
+	 * @return The total number of requests.
+	 */
+	public int getTotalRequests() {
+		return totalRequests;
+	}
+
+
+	/**
+	 * Gets the current number of active connections.
+	 * 
+	 * @return The number of active connections.
+	 */
+	public int getActiveConnections() {
+		return activeConnections;
+	}
+}
diff --git a/osmosis-replication-http/src/main/resources/osmosis-plugins.conf b/osmosis-replication-http/src/main/resources/osmosis-plugins.conf
new file mode 100644
index 0000000..9d97b4e
--- /dev/null
+++ b/osmosis-replication-http/src/main/resources/osmosis-plugins.conf
@@ -0,0 +1 @@
+org.openstreetmap.osmosis.replicationhttp.ReplicationHttpPluginLoader
\ No newline at end of file
diff --git a/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationDestination.java b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationDestination.java
new file mode 100644
index 0000000..633d3c1
--- /dev/null
+++ b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationDestination.java
@@ -0,0 +1,105 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+
+
+/**
+ * A mocked replication destination allowing the existing replication state to be loaded and the
+ * current state maintained. All data processing calls such as process will be ignored.
+ */
+public class MockReplicationDestination implements ChangeSink {
+	
+	private boolean stateExists;
+	private ReplicationState currentState;
+	private Map<String, String> storedState;
+	
+	
+	/**
+	 * Creates a new instance with no state.
+	 */
+	public MockReplicationDestination() {
+		stateExists = false;
+		storedState = new HashMap<String, String>();
+	}
+	
+	
+	/**
+	 * Creates a new instance with an initial state.
+	 * 
+	 * @param initialState
+	 *            The initial replication state.
+	 */
+	public MockReplicationDestination(ReplicationState initialState) {
+		this();
+		
+		initialState.store(storedState);
+		stateExists = true;
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Get the replication state from the upstream task.
+		if (!metaData.containsKey(ReplicationState.META_DATA_KEY)) {
+			throw new OsmosisRuntimeException(
+					"No replication state has been provided in metadata key " + ReplicationState.META_DATA_KEY + ".");
+		}
+		currentState = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+		
+		// Initialise the state from the stored state if it exists and increment
+		// the sequence number.
+		if (stateExists) {
+			currentState.load(storedState);
+			currentState.setSequenceNumber(currentState.getSequenceNumber() + 1);
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		currentState.store(storedState);
+		stateExists = true;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * Returns the current state object tracked internally. This will be a state
+	 * object provided by a caller in the initialize method. It will remain
+	 * available after complete and release have been called.
+	 * 
+	 * @return The current state.
+	 */
+	public ReplicationState getCurrentState() {
+		return currentState;
+	}
+}
diff --git a/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationSource.java b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationSource.java
new file mode 100644
index 0000000..2726a08
--- /dev/null
+++ b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/MockReplicationSource.java
@@ -0,0 +1,80 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSource;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+
+
+/**
+ * This test task is used for driving test replication data through a
+ * replication pipeline.
+ * 
+ * @author Brett Henderson
+ */
+public class MockReplicationSource implements ChangeSource {
+
+	private ChangeSink changeSink;
+
+
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+
+
+	/**
+	 * Sends a replication sequence containing dummy data to the destination.
+	 */
+	public void sendSequence() {
+		// Initialise the replication stream.
+		ReplicationState state = new ReplicationState();
+		Map<String, Object> metaData = new HashMap<String, Object>(1);
+		metaData.put(ReplicationState.META_DATA_KEY, state);
+		changeSink.initialize(metaData);
+
+		// Send the change data unless this is sequence 0 where no data is
+		// allowed. We'll only send a single record for simplicity.
+		if (state.getSequenceNumber() > 0) {
+			// We'll do a create action on the first replication pass, and modify subsequently.
+			ChangeAction action;
+			if (state.getSequenceNumber() == 1) {
+				action = ChangeAction.Create;
+			} else {
+				action = ChangeAction.Modify;
+			}
+			
+			// Create a change record which data derived from the
+			// replication sequence number itself.
+			ChangeContainer change = new ChangeContainer(new NodeContainer(new Node(new CommonEntityData(10,
+					(int) state.getSequenceNumber(), new Date(state.getSequenceNumber() * 1000), new OsmUser(11,
+							"test"), state.getSequenceNumber() * 2), state.getSequenceNumber() * 3,
+					state.getSequenceNumber() * 4)), action);
+			
+			// Send the record downstream.
+			changeSink.process(change);
+		}
+		
+		state.setTimestamp(new Date(state.getSequenceNumber() * 1000));
+		
+		changeSink.complete();
+	}
+
+
+	/**
+	 * Releases all downstream resources.
+	 */
+	public void release() {
+		changeSink.release();
+	}
+}
diff --git a/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerTest.java b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerTest.java
new file mode 100644
index 0000000..d885ec1
--- /dev/null
+++ b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationSequenceServerTest.java
@@ -0,0 +1,46 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the replication sequence server task.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationSequenceServerTest extends AbstractDataTest {
+
+	/**
+	 * Very basic test that launches the server, runs several replication
+	 * iterations and then shuts down without connecting any clients.
+	 * 
+	 * @throws InterruptedException
+	 *             if processing is interrupted.
+	 */
+	@Test
+	public void testStartupShutdown() throws InterruptedException {
+		ReplicationSequenceServer server;
+
+		server = new ReplicationSequenceServer(0);
+		server.setChangeSink(new MockReplicationDestination());
+
+		try {
+			for (int i = 0; i < 10; i++) {
+				ReplicationState state = new ReplicationState();
+				Map<String, Object> metaData = new HashMap<String, Object>();
+				metaData.put(ReplicationState.META_DATA_KEY, state);
+				server.initialize(metaData);
+				Thread.sleep(10);
+				server.complete();
+			}
+		} finally {
+			server.release();
+		}
+	}
+}
diff --git a/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationTest.java b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationTest.java
new file mode 100644
index 0000000..6affaf0
--- /dev/null
+++ b/osmosis-replication-http/src/test/java/org/openstreetmap/osmosis/replicationhttp/v0_6/ReplicationTest.java
@@ -0,0 +1,160 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replicationhttp.v0_6;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskRunner;
+import org.openstreetmap.osmosis.replication.common.ReplicationSequenceFormatter;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationWriter;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Performs an end to end test of the HTTP replication classes.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationTest extends AbstractDataTest {
+
+	/**
+	 * Configures logging to write all output to the console.
+	 */
+	private static void configureLoggingConsole() {
+		Logger rootLogger;
+		Handler consoleHandler;
+
+		rootLogger = Logger.getLogger("");
+
+		// Remove any existing handlers.
+		for (Handler handler : rootLogger.getHandlers()) {
+			rootLogger.removeHandler(handler);
+		}
+
+		// Add a new console handler.
+		consoleHandler = new ConsoleHandler();
+		consoleHandler.setLevel(Level.ALL);
+		rootLogger.addHandler(consoleHandler);
+	}
+
+
+	/**
+	 * Configures the logging level.
+	 * 
+	 * @param level
+	 *            The new logging level to apply.
+	 */
+	private static void configureLoggingLevel(Level level) {
+		Logger rootLogger;
+
+		rootLogger = Logger.getLogger("");
+
+		// Set the required logging level.
+		rootLogger.setLevel(level);
+
+		// Set the JPF logger to one level lower.
+		Logger.getLogger("org.java.plugin").setLevel(Level.WARNING);
+	}
+
+
+	/**
+	 * Single end to end test.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs during processing.
+	 */
+	@Test
+	public void test() throws Exception {
+		final int sequenceCount = 100;
+		long timerStart;
+
+		// Due to the multi-threaded nature of this test, it may be necessary to
+		// enable logging to diagnose problems.
+		final boolean enableLogging = false;
+		if (enableLogging) {
+			configureLoggingConsole();
+			configureLoggingLevel(Level.FINEST);
+			Logger.getLogger("org.openstreetmap.osmosis.replication.v0_6.ReplicationStateWriter").setLevel(Level.INFO);
+		}
+
+		// Create the primary replication data source.
+		MockReplicationSource source = new MockReplicationSource();
+
+		// Create the sequence server for notifying when new sequence numbers
+		// are available and connect it to the primary source.
+		ReplicationSequenceServer sequenceServer = new ReplicationSequenceServer(0);
+		source.setChangeSink(sequenceServer);
+
+		// Create a replication data writer and receive data from the primary
+		// data source (via the sequence server).
+		File workingDir1 = dataUtils.newFolder();
+		sequenceServer.setChangeSink(new ReplicationWriter(workingDir1));
+
+		// Send sequence through the primary pipeline to ensure the
+		// sequence server is running.
+		source.sendSequence();
+
+		// Create a HTTP replication data server using the data from the
+		// replication writer, and receive sequence number updates from the
+		// sequence server.
+		ReplicationDataServer dataServer = new ReplicationDataServer(sequenceServer.getPort(), workingDir1, 0);
+
+		// Start the HTTP data server.
+		TaskRunner serverRunner = new TaskRunner(dataServer, "data-server");
+		serverRunner.start();
+
+		/*
+		 * The server starts in another thread so we need to wait until it has
+		 * started. We will wait until the dynamically allocated port is
+		 * exported via the getPort method which occurs after server startup.
+		 */
+		timerStart = System.currentTimeMillis();
+		while (dataServer.getPort() == 0 && (System.currentTimeMillis() - timerStart < 10000)) {
+			Thread.sleep(10);
+		}
+		Assert.assertFalse("Server port was not dynamically allocated.", sequenceServer.getPort() == 0);
+
+		// Create a HTTP replication data client receiving data from the data
+		// server.
+		ReplicationDataClient dataClient = new ReplicationDataClient(new InetSocketAddress(dataServer.getPort()), "");
+
+		// Create a replication data writer to receiving data from the HTTP data
+		// source.
+		File workingDir2 = dataUtils.newFolder();
+		dataClient.setChangeSink(new ReplicationWriter(workingDir2));
+
+		// Start the HTTP data server and HTTP data client.
+		TaskRunner clientRunner = new TaskRunner(dataClient, "data-client");
+		clientRunner.start();
+
+		// Send the test replication intervals.
+		for (int i = 0; i < sequenceCount; i++) {
+			source.sendSequence();
+		}
+
+		// Wait for all the data to reach the destination.
+		File finalStateFile = new File(workingDir2, new ReplicationSequenceFormatter(9, 3).getFormattedName(
+				sequenceCount, ".state.txt"));
+		timerStart = System.currentTimeMillis();
+		while (!finalStateFile.exists() && (System.currentTimeMillis() - timerStart < 10000)) {
+			Thread.sleep(100);
+		}
+
+		// Verify that all of the replication sequences made it to the
+		// destination.
+		Assert.assertTrue("The state file for sequence " + sequenceCount + " doesn't exist.", finalStateFile.exists());
+
+		// Shut down the pipelines.
+		clientRunner.interrupt();
+		serverRunner.interrupt();
+		clientRunner.join();
+		serverRunner.join();
+		source.release();
+	}
+}
diff --git a/osmosis-replication/.checkstyle b/osmosis-replication/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-replication/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-replication/.gitignore b/osmosis-replication/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-replication/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-replication/build.gradle b/osmosis-replication/build.gradle
new file mode 100644
index 0000000..71c5006
--- /dev/null
+++ b/osmosis-replication/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-set')
+    compile project(':osmosis-xml')
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java
new file mode 100644
index 0000000..8103208
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java
@@ -0,0 +1,67 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.replication.v0_6.IntervalDownloaderFactory;
+import org.openstreetmap.osmosis.replication.v0_6.IntervalDownloaderInitializerFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationDownloaderFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationDownloaderInitializerFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationFileMergerFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationFileMergerInitializerFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationLagReaderFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationToChangeWriterFactory;
+import org.openstreetmap.osmosis.replication.v0_6.ReplicationWriterFactory;
+
+
+/**
+ * The plugin loader for the replication tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationPluginLoader implements PluginLoader {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		Map<String, TaskManagerFactory> factoryMap;
+		
+		factoryMap = new HashMap<String, TaskManagerFactory>();
+		
+		factoryMap.put("read-change-interval", new IntervalDownloaderFactory());
+		factoryMap.put("rci", new IntervalDownloaderFactory());
+		factoryMap.put("read-change-interval-init", new IntervalDownloaderInitializerFactory());
+		factoryMap.put("rcii", new IntervalDownloaderInitializerFactory());
+		factoryMap.put("read-replication-interval", new ReplicationDownloaderFactory());
+		factoryMap.put("rri", new ReplicationDownloaderFactory());
+		factoryMap.put("read-replication-interval-init", new ReplicationDownloaderInitializerFactory());
+		factoryMap.put("rrii", new ReplicationDownloaderInitializerFactory());
+		factoryMap.put("merge-replication-files", new ReplicationFileMergerFactory());
+		factoryMap.put("mrf", new ReplicationFileMergerFactory());
+		factoryMap.put("merge-replication-files-init", new ReplicationFileMergerInitializerFactory());
+		factoryMap.put("mrfi", new ReplicationFileMergerInitializerFactory());
+		factoryMap.put("read-replication-lag", new ReplicationLagReaderFactory());
+		factoryMap.put("rrl", new ReplicationLagReaderFactory());
+		factoryMap.put("write-replication", new ReplicationWriterFactory());
+		factoryMap.put("wr", new ReplicationWriterFactory());
+		factoryMap.put("replication-to-change", new ReplicationToChangeWriterFactory());
+		factoryMap.put("rtc", new ReplicationToChangeWriterFactory());
+		
+		factoryMap.put("read-change-interval-0.6", new IntervalDownloaderFactory());
+		factoryMap.put("read-change-interval-init-0.6", new IntervalDownloaderInitializerFactory());
+		factoryMap.put("read-replication-interval-0.6", new ReplicationDownloaderFactory());
+		factoryMap.put("read-replication-interval-init-0.6", new ReplicationDownloaderInitializerFactory());
+		factoryMap.put("merge-replication-files-0.6", new ReplicationFileMergerFactory());
+		factoryMap.put("merge-replication-files-init-0.6", new ReplicationFileMergerInitializerFactory());
+		factoryMap.put("read-replication-lag-0.6", new ReplicationLagReaderFactory());
+		factoryMap.put("write-replication-0.6", new ReplicationWriterFactory());
+		factoryMap.put("replication-to-change-0.6", new ReplicationToChangeWriterFactory());
+		
+		return factoryMap;
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStore.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStore.java
new file mode 100644
index 0000000..9b974cd
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStore.java
@@ -0,0 +1,80 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.common;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
+
+
+/**
+ * A {@link ReplicationStore} implementation storing all data to the filesystem.
+ * 
+ * @author Brett Henderson
+ */
+public class FileReplicationStore implements ReplicationStore {
+	private static final String STATE_FILE = "state.txt";
+
+	private PropertiesPersister currentStatePersister;
+	private ReplicationFileSequenceFormatter sequenceFormatter;
+	private boolean saveCurrentState;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param storeDirectory
+	 *            The directory used to hold the contents of the store.
+	 * @param saveCurrentState
+	 *            If true, the current state will be updated by the
+	 *            {@link #saveState(ReplicationState)} operation as well as the
+	 *            sequenced state.
+	 */
+	public FileReplicationStore(File storeDirectory, boolean saveCurrentState) {
+		currentStatePersister = new PropertiesPersister(new File(storeDirectory, STATE_FILE));
+		sequenceFormatter = new ReplicationFileSequenceFormatter(storeDirectory);
+		this.saveCurrentState = saveCurrentState;
+	}
+
+
+	@Override
+	public ReplicationState getCurrentState() {
+		ReplicationState state = new ReplicationState();
+		state.load(currentStatePersister.loadMap());
+		return state;
+	}
+
+
+	@Override
+	public ReplicationState getState(long sequence) {
+		File stateFile = sequenceFormatter.getFormattedName(sequence, ".state.txt");
+		return new ReplicationState(new PropertiesPersister(stateFile).loadMap());
+	}
+
+
+	@Override
+	public XmlChangeReader getData(long sequence) {
+		File changeFile = sequenceFormatter.getFormattedName(sequence, ".osc.gz");
+		return new XmlChangeReader(changeFile, false, CompressionMethod.GZip);
+	}
+
+
+	@Override
+	public void saveState(ReplicationState state) {
+		File stateFile = sequenceFormatter.getFormattedName(state.getSequenceNumber(), ".state.txt");
+		new PropertiesPersister(stateFile).store(state.store());
+
+		if (saveCurrentState) {
+			currentStatePersister.store(state.store());
+		}
+	}
+
+
+	@Override
+	public XmlChangeWriter saveData(long sequence) {
+		File changeFile = sequenceFormatter.getFormattedName(sequence, ".osc.gz");
+		return new XmlChangeWriter(changeFile, CompressionMethod.GZip);
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationFileSequenceFormatter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationFileSequenceFormatter.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationFileSequenceFormatter.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationFileSequenceFormatter.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationSequenceFormatter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationSequenceFormatter.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationSequenceFormatter.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationSequenceFormatter.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java
new file mode 100644
index 0000000..ab1a704
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java
@@ -0,0 +1,189 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.common;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.time.DateFormatter;
+import org.openstreetmap.osmosis.core.time.DateParser;
+
+
+/**
+ * Contains the state to be remembered between replication invocations. This
+ * state ensures that no data is missed during replication, and ensures that
+ * none is repeated except after certain failure situations.
+ */
+public class ReplicationState {
+	/**
+	 * The key used when passing an instance through the pipeline as metadata.
+	 */
+	public static final String META_DATA_KEY = "replication.state";
+	
+	
+	private Date timestamp;
+	private long sequenceNumber;
+
+
+	/**
+	 * Creates a new instance with all values set to defaults.
+	 */
+	public ReplicationState() {
+		this.timestamp = new Date(0);
+		this.sequenceNumber = 0;
+	}
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param timestamp
+	 *            The maximum timestamp of data currently read from the database.
+	 * @param sequenceNumber
+	 *            The replication sequence number.
+	 */
+	public ReplicationState(Date timestamp, long sequenceNumber) {
+		this.timestamp = timestamp;
+		this.sequenceNumber = sequenceNumber;
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param properties
+	 *            The properties to load state from.
+	 */
+	public ReplicationState(Map<String, String> properties) {
+		load(properties);
+	}
+	
+	
+	private String loadProperty(Map<String, String> properties, String key) {
+		if (!properties.containsKey(key)) {
+			throw new OsmosisRuntimeException("The replication state doesn't contain a " + key + " property.");
+		}
+		return properties.get(key);
+	}
+	
+	
+	/**
+	 * Loads all state from the provided properties object.
+	 * 
+	 * @param properties
+	 *            The properties to be read.
+	 */
+	public void load(Map<String, String> properties) {
+		timestamp = new DateParser().parse(loadProperty(properties, "timestamp"));
+		sequenceNumber = Long.parseLong(loadProperty(properties, "sequenceNumber"));
+	}
+
+
+	/**
+	 * Writes all state into the provided properties object.
+	 * 
+	 * @param properties
+	 *            The properties to be updated.
+	 */
+	public void store(Map<String, String> properties) {
+		properties.put("timestamp", new DateFormatter().format(timestamp));
+		properties.put("sequenceNumber", Long.toString(sequenceNumber));
+	}
+
+
+	/**
+	 * Writes all state into a new properties object.
+	 * 
+	 * @return The properties.
+	 */
+	public Map<String, String> store() {
+		Map<String, String> properties = new HashMap<String, String>();
+		store(properties);
+		return properties;
+	}
+
+
+	/**
+	 * Gets the maximum timestamp of data currently read from the database.
+	 * 
+	 * @return The timestamp.
+	 */
+	public Date getTimestamp() {
+		return timestamp;
+	}
+
+
+	/**
+	 * Sets the maximum timestamp of data currently read from the database.
+	 * 
+	 * @param timestamp
+	 *            The timestamp.
+	 */
+	public void setTimestamp(Date timestamp) {
+		this.timestamp = timestamp;
+	}
+	
+	
+	/**
+	 * Gets the replication sequence number.
+	 * 
+	 * @return The sequence number.
+	 */
+	public long getSequenceNumber() {
+		return sequenceNumber;
+	}
+
+
+	/**
+	 * Sets the replication sequence number.
+	 * 
+	 * @param sequenceNumber
+	 *            The sequence number.
+	 */
+	public void setSequenceNumber(long sequenceNumber) {
+		this.sequenceNumber = sequenceNumber;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		boolean result;
+		
+		if (obj instanceof ReplicationState) {
+			ReplicationState compareState = (ReplicationState) obj;
+			
+			if (timestamp.equals(compareState.timestamp)
+					&& sequenceNumber == compareState.sequenceNumber) {
+				result = true;
+			} else {
+				result = false;
+			}
+		} else {
+			result = false;
+		}
+		
+		return result;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		return (int) sequenceNumber + (int) timestamp.getTime();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "ReplicationState(timestamp=" + timestamp + ", sequenceNumber=" + sequenceNumber + ")";
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStore.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStore.java
new file mode 100644
index 0000000..9da7da7
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStore.java
@@ -0,0 +1,63 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.common;
+
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
+
+
+/**
+ * Defines a store for replication data.
+ * 
+ * @author Brett Henderson
+ */
+public interface ReplicationStore {
+	/**
+	 * Gets the current replication state. This corresponds to the state of the
+	 * latest sequence in the store.
+	 * 
+	 * @return The replication state.
+	 */
+	ReplicationState getCurrentState();
+
+
+	/**
+	 * Gets the state for the specified sequence.
+	 * 
+	 * @param sequence
+	 *            The sequence to be loaded.
+	 * @return The replication state.
+	 */
+	ReplicationState getState(long sequence);
+
+
+	/**
+	 * Gets the data for the specified sequence.
+	 * 
+	 * @param sequence
+	 *            The sequence to be loaded.
+	 * @return The change reader.
+	 */
+	XmlChangeReader getData(long sequence);
+
+
+	/**
+	 * Sets the state for the specified sequence. The current state may be
+	 * updated to match depending on the store configuration. This should only
+	 * be called after data has been successfully written.
+	 * 
+	 * @param state
+	 *            The replication state.
+	 */
+	void saveState(ReplicationState state);
+
+
+	/**
+	 * Obtains an change writer used to save the replication data for the
+	 * specified sequence.
+	 * 
+	 * @param sequence
+	 *            The sequence to be saved.
+	 * @return The change writer.
+	 */
+	XmlChangeWriter saveData(long sequence);
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java
new file mode 100644
index 0000000..917a73c
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java
@@ -0,0 +1,126 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.common;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Retrieves replication state files from the server hosting replication data.
+ */
+public class ServerStateReader {
+	private static final Logger LOG = Logger.getLogger(ServerStateReader.class.getName());
+	private static final String SERVER_STATE_FILE = "state.txt";
+	private static final String SEQUENCE_STATE_FILE_SUFFIX = ".state.txt";
+	
+	
+	private ReplicationSequenceFormatter sequenceFormatter;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public ServerStateReader() {
+		sequenceFormatter = new ReplicationSequenceFormatter(9, 3);
+	}
+	
+	
+	/**
+	 * Retrieves the latest state from the server.
+	 * 
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @return The state.
+	 */
+	public ReplicationState getServerState(URL baseUrl) {
+		return getServerState(baseUrl, SERVER_STATE_FILE);
+	}
+	
+	
+	/**
+	 * Retrieves the specified state from the server.
+	 * 
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @param sequenceNumber
+	 *            The sequence number of the state to be retrieved from the server.
+	 * @return The state.
+	 */
+	public ReplicationState getServerState(URL baseUrl, long sequenceNumber) {
+		return getServerState(baseUrl, sequenceFormatter.getFormattedName(sequenceNumber, SEQUENCE_STATE_FILE_SUFFIX));
+	}
+
+
+	/**
+	 * Retrieves the specified state from the server.
+	 * 
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @param stateFile
+	 *            The state file to be retrieved.
+	 * @return The state.
+	 */
+	private ReplicationState getServerState(URL baseUrl, String stateFile) {
+		URL stateUrl;
+		InputStream stateStream = null;
+		
+		try {
+			stateUrl = new URL(baseUrl, stateFile);
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException("The server timestamp URL could not be created.", e);
+		}
+		
+		try {
+			BufferedReader reader;
+			Properties stateProperties;
+			Map<String, String> stateMap;
+			ReplicationState state;
+			
+			URLConnection connection = stateUrl.openConnection();
+			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
+			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
+			stateStream = connection.getInputStream();
+			
+			reader = new BufferedReader(new InputStreamReader(stateStream));
+			stateProperties = new Properties();
+			stateProperties.load(reader);
+			
+			stateMap = new HashMap<String, String>();
+			for (Entry<Object, Object> property : stateProperties.entrySet()) {
+				stateMap.put((String) property.getKey(), (String) property.getValue());
+			}
+			
+			state = new ReplicationState(stateMap);
+			
+			stateStream.close();
+			stateStream = null;
+			
+			return state;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read the state from the server.", e);
+		} finally {
+			try {
+				if (stateStream != null) {
+					stateStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to close state stream.", e);
+			}
+		}
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/TimestampTracker.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/TimestampTracker.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/common/TimestampTracker.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/common/TimestampTracker.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java
new file mode 100644
index 0000000..00779e3
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java
@@ -0,0 +1,389 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.common.RunnableTask;
+import org.openstreetmap.osmosis.core.util.FileBasedLock;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.ReplicationSequenceFormatter;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.replication.common.ServerStateReader;
+import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+
+
+/**
+ * This class downloads a set of replication files from a HTTP server and tracks the progress of
+ * which files have already been processed. The actual processing of changeset files is performed by
+ * sub-classes. This class forms the basis of a replication mechanism.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class BaseReplicationDownloader implements RunnableTask {
+	
+	private static final Logger LOG = Logger.getLogger(BaseReplicationDownloader.class.getName());
+	private static final String LOCK_FILE = "download.lock";
+	private static final String CONFIG_FILE = "configuration.txt";
+	private static final String LOCAL_STATE_FILE = "state.txt";
+	
+	
+	private File workingDirectory;
+	private ReplicationSequenceFormatter sequenceFormatter;
+	private ServerStateReader serverStateReader;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public BaseReplicationDownloader(File workingDirectory) {
+		this.workingDirectory = workingDirectory;
+		
+		sequenceFormatter = new ReplicationSequenceFormatter(9, 3);
+		serverStateReader = new ServerStateReader();
+	}
+	
+	
+	/**
+	 * Provides sub-classes with access to the working directory.
+	 * 
+	 * @return The working directory for the task.
+	 */
+	protected File getWorkingDirectory() {
+		return workingDirectory;
+	}
+	
+	
+	/**
+	 * Downloads the file from the server with the specified name and writes it
+	 * to a local temporary file.
+	 * 
+	 * @param fileName
+	 *            The name of the file to download.
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @return The temporary file containing the downloaded data.
+	 */
+	private File downloadReplicationFile(String fileName, URL baseUrl) {
+		URL changesetUrl;
+		InputStream inputStream = null;
+		OutputStream outputStream = null;
+		
+		try {
+			changesetUrl = new URL(baseUrl, fileName);
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException("The server file URL could not be created.", e);
+		}
+		
+		try {
+			BufferedInputStream source;
+			BufferedOutputStream sink;
+			File outputFile;
+			byte[] buffer;
+			
+			// Open an input stream for the changeset file on the server.
+			URLConnection connection = changesetUrl.openConnection();
+			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
+			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
+			inputStream = connection.getInputStream();
+			source = new BufferedInputStream(inputStream, 65536);
+			
+			// Create a temporary file to write the data to.
+			outputFile = File.createTempFile("change", null);
+			
+			// Open a output stream for the destination file.
+			outputStream = new FileOutputStream(outputFile);
+			sink = new BufferedOutputStream(outputStream, 65536);
+			
+			// Download the file.
+			buffer = new byte[65536];
+			for (int bytesRead = source.read(buffer); bytesRead > 0; bytesRead = source.read(buffer)) {
+				sink.write(buffer, 0, bytesRead);
+			}
+			sink.flush();
+			
+			// Clean up all file handles.
+			inputStream.close();
+			inputStream = null;
+			outputStream.close();
+			outputStream = null;
+			
+			return outputFile;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read the changeset file " + fileName + " from the server.", e);
+		} finally {
+			try {
+				if (inputStream != null) {
+					inputStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to changeset download stream.", e);
+			}
+			try {
+				if (outputStream != null) {
+					outputStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to changeset output stream.", e);
+			}
+		}
+	}
+	
+	
+	private void processReplicationFile(File replicationFile, ReplicationState replicationState) {
+		try {
+			XmlChangeReader xmlReader;
+			
+			// Send the contents of the replication file to the sink but suppress the complete
+			// and release methods.
+			xmlReader = new XmlChangeReader(replicationFile, true, CompressionMethod.GZip);
+			
+			// Delegate to the sub-class to process the xml.
+			processChangeset(xmlReader, replicationState);
+			
+		} finally {
+			if (!replicationFile.delete()) {
+				LOG.warning("Unable to delete file " + replicationFile.getName());
+			}
+		}
+	}
+
+
+	/**
+	 * Determines the maximum timestamp of data to be downloaded during this invocation. This may be
+	 * overriden by sub-classes, but the sub-classes must call this implemention first and then
+	 * limit the maximum timestamp further if needed. A sub-class may never increase the maximum
+	 * timestamp beyond that calculated by this method.
+	 * 
+	 * @param configuration
+	 *            The configuration.
+	 * @param serverTimestamp
+	 *            The timestamp of the latest data on the server.
+	 * @param localTimestamp
+	 *            The timestamp of the most recently downloaded data.
+	 * @return The maximum timestamp for this invocation.
+	 */
+	protected Date calculateMaximumTimestamp(ReplicationDownloaderConfiguration configuration, Date serverTimestamp,
+			Date localTimestamp) {
+		Date maximumTimestamp;
+		
+		maximumTimestamp = serverTimestamp;
+		
+		// Limit the duration according to the maximum defined in the configuration.
+		if (configuration.getMaxInterval() > 0) {
+			if ((serverTimestamp.getTime() - localTimestamp.getTime())
+				> configuration.getMaxInterval()) {
+				maximumTimestamp = new Date(localTimestamp.getTime() + configuration.getMaxInterval());
+			}
+		}
+		
+		LOG.finer("Maximum timestamp is " + maximumTimestamp);
+		
+		return maximumTimestamp;
+	}
+	
+	
+	private ReplicationState download(ReplicationDownloaderConfiguration configuration, ReplicationState serverState,
+			ReplicationState initialLocalState) {
+		URL baseUrl;
+		ReplicationState localState;
+		Date maximumDownloadTimestamp;
+		
+		localState = initialLocalState;
+		
+		// Determine the location of download files.
+		baseUrl = configuration.getBaseUrl();
+		
+		// Determine the maximum timestamp that can be downloaded.
+		maximumDownloadTimestamp =
+			calculateMaximumTimestamp(configuration, serverState.getTimestamp(), localState.getTimestamp());
+		LOG.fine("The maximum timestamp to be downloaded is " + maximumDownloadTimestamp + ".");
+		
+		// Download all files and send their contents to the sink.
+		while (localState.getSequenceNumber() < serverState.getSequenceNumber()) {
+			File replicationFile;
+			long sequenceNumber;
+			ReplicationState fileReplicationState;
+			
+			// Check to see if our local state has already reached the maximum
+			// allowable timestamp. This will typically occur if a job is run
+			// again before new data becomes available, or if an implementation
+			// of this class (eg. ReplicationFileMerger) is waiting for a full
+			// time period of data to become available before processing.
+			if (localState.getTimestamp().compareTo(maximumDownloadTimestamp) >= 0) {
+				break;
+			}
+			
+			// Calculate the next sequence number.
+			sequenceNumber = localState.getSequenceNumber() + 1;
+			LOG.finer("Processing replication sequence " + sequenceNumber + ".");
+			
+			// Get the state associated with the next file.
+			fileReplicationState = serverStateReader.getServerState(baseUrl, sequenceNumber);
+			
+			// Ensure that the next state is within the allowable timestamp
+			// range. We must stop if the next data takes us beyond the maximum
+			// timestamp. This will either occur if a maximum download time
+			// duration limit has been imposed, or if a time-aligned boundary
+			// has been reached.
+			if (fileReplicationState.getTimestamp().compareTo(maximumDownloadTimestamp) > 0) {
+				// We will always allow at least one replication interval
+				// through to deal with the case where a single interval exceeds
+				// the maximum duration. This can happen if the source data has
+				// a long time gap between two intervals due to system downtime.
+				if (localState.getSequenceNumber() != initialLocalState.getSequenceNumber()) {
+					break;
+				}
+			}
+			
+			// Download the next replication file to a temporary file.
+			replicationFile =
+				downloadReplicationFile(sequenceFormatter.getFormattedName(sequenceNumber, ".osc.gz"), baseUrl);
+			
+			// Process the file and send its contents to the sink.
+			processReplicationFile(replicationFile, fileReplicationState);
+			
+			// Update the local state to reflect the file state just processed.
+			localState = fileReplicationState;
+		}
+		
+		return localState;
+	}
+	
+	
+	private void runImpl() {
+		try {
+			ReplicationDownloaderConfiguration configuration;
+			ReplicationState serverState;
+			ReplicationState localState;
+			PropertiesPersister localStatePersistor;
+			
+			// Instantiate utility objects.
+			configuration = new ReplicationDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
+			
+			// Obtain the server state.
+			LOG.fine("Reading current server state.");
+			serverState = serverStateReader.getServerState(configuration.getBaseUrl());
+			
+			// Build the local state persister which is used for both loading and storing local state.
+			localStatePersistor = new PropertiesPersister(new File(workingDirectory, LOCAL_STATE_FILE));
+			
+			// Begin processing.
+			processInitialize(Collections.<String, Object>emptyMap());
+			
+			// If local state isn't available we need to copy server state to be the initial local state
+			// then exit.
+			if (localStatePersistor.exists()) {
+				localState = new ReplicationState(localStatePersistor.loadMap());
+				
+				// Download and process the replication files.
+				localState = download(configuration, serverState, localState);
+				
+			} else {
+				localState = serverState;
+				
+				processInitializeState(localState);
+			}
+			
+			// Commit downstream changes.
+			processComplete();
+			
+			// Persist the local state.
+			localStatePersistor.store(localState.store());
+			
+		} finally {
+			processRelease();
+		}
+	}
+	
+	
+	/**
+	 * This is called prior to any processing being performed. It allows any
+	 * setup activities to be performed.
+	 * 
+	 * @param metaData
+	 *            The meta data associated with this processing request (empty
+	 *            in the current implementation).
+	 */
+	protected abstract void processInitialize(Map<String, Object> metaData);
+	
+	
+	/**
+	 * Invoked once during the first execution run to allow initialisation based on the initial
+	 * replication state downloaded from the server.
+	 * 
+	 * @param initialState
+	 *            The first server state.
+	 */
+	protected abstract void processInitializeState(ReplicationState initialState);
+	
+	
+	/**
+	 * Processes the changeset.
+	 * 
+	 * @param xmlReader
+	 *            The changeset reader initialised to point to the changeset file.
+	 * @param replicationState
+	 *            The replication state associated with the changeset file.
+	 */
+	protected abstract void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState);
+
+
+	/**
+	 * This is implemented by sub-classes and is called when all changesets have been processed.
+	 * This should perform any completion tasks such as committing changes to a database.
+	 */
+	protected abstract void processComplete();
+
+
+	/**
+	 * This is implemented by sub-classes and is called and the completion of all processing
+	 * regardless of whether it was successful or not. This should perform any cleanup tasks such as
+	 * closing files or releasing database connections.
+	 */
+	protected abstract void processRelease();
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		FileBasedLock fileLock;
+		
+		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
+		
+		try {
+			fileLock.lock();
+			
+			runImpl();
+			
+			fileLock.unlock();
+			
+		} finally {
+			fileLock.release();
+		}
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java
new file mode 100644
index 0000000..3178134
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java
@@ -0,0 +1,413 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskRunner;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.openstreetmap.osmosis.core.time.DateParser;
+import org.openstreetmap.osmosis.core.util.FileBasedLock;
+import org.openstreetmap.osmosis.replication.common.TimestampTracker;
+import org.openstreetmap.osmosis.replication.v0_6.impl.ChangesetFileNameFormatter;
+import org.openstreetmap.osmosis.replication.v0_6.impl.IntervalDownloaderConfiguration;
+import org.openstreetmap.osmosis.set.v0_6.ChangeMerger;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+
+
+/**
+ * Downloads a set of change files from a HTTP server, and merges them into a
+ * single output stream. It tracks the intervals covered by the current files
+ * and stores the current timestamp between invocations forming the basis of a
+ * replication mechanism.
+ * 
+ * @author Brett Henderson
+ */
+public class IntervalDownloader implements RunnableChangeSource {
+	
+	private static final Logger LOG = Logger.getLogger(IntervalDownloader.class.getName());
+	
+	
+	private static final String LOCK_FILE = "download.lock";
+	private static final String CONFIG_FILE = "configuration.txt";
+	private static final String TSTAMP_FILE = "timestamp.txt";
+	private static final String TSTAMP_NEW_FILE = "timestamp-new.txt";
+	private static final String SERVER_TSTAMP_FILE = "timestamp.txt";
+	
+	
+	private ChangeSink changeSink;
+	private String taskId;
+	private File workingDirectory;
+	private DateParser dateParser;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param taskId
+	 *            The identifier for the task, this is required because the
+	 *            names of threads created by this task will use this name as a
+	 *            prefix.
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public IntervalDownloader(String taskId, File workingDirectory) {
+		this.taskId = taskId;
+		this.workingDirectory = workingDirectory;
+		
+		dateParser = new DateParser();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * Retrieves the latest timestamp from the server.
+	 * 
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @return The timestamp.
+	 */
+	private Date getServerTimestamp(URL baseUrl) {
+		URL timestampUrl;
+		InputStream timestampStream = null;
+		
+		try {
+			timestampUrl = new URL(baseUrl, SERVER_TSTAMP_FILE);
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException("The server timestamp URL could not be created.", e);
+		}
+		
+		try {
+			BufferedReader reader;
+			Date result;
+			
+			URLConnection connection = timestampUrl.openConnection();
+			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
+			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
+			timestampStream = connection.getInputStream();
+			
+			reader = new BufferedReader(new InputStreamReader(timestampStream));
+			
+			result = dateParser.parse(reader.readLine());
+			
+			timestampStream.close();
+			timestampStream = null;
+			
+			return result;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read the timestamp from the server.", e);
+		} finally {
+			try {
+				if (timestampStream != null) {
+					timestampStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to close timestamp stream.", e);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Downloads the file from the server with the specified name and writes it
+	 * to a local temporary file.
+	 * 
+	 * @param fileName
+	 *            The name of the file to download.
+	 * @param baseUrl
+	 *            The url of the directory containing change files.
+	 * @return The temporary file containing the downloaded data.
+	 */
+	private File downloadChangesetFile(String fileName, URL baseUrl) {
+		URL changesetUrl;
+		InputStream inputStream = null;
+		OutputStream outputStream = null;
+		
+		try {
+			changesetUrl = new URL(baseUrl, fileName);
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException("The server file URL could not be created.", e);
+		}
+		
+		try {
+			BufferedInputStream source;
+			BufferedOutputStream sink;
+			File outputFile;
+			byte[] buffer;
+			
+			// Open an input stream for the changeset file on the server.
+			URLConnection connection = changesetUrl.openConnection();
+			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
+			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
+			inputStream = connection.getInputStream();
+			source = new BufferedInputStream(inputStream, 65536);
+			
+			// Create a temporary file to write the data to.
+			outputFile = File.createTempFile("change", null);
+			
+			// Open a output stream for the destination file.
+			outputStream = new FileOutputStream(outputFile);
+			sink = new BufferedOutputStream(outputStream, 65536);
+			
+			// Download the file.
+			buffer = new byte[65536];
+			for (int bytesRead = source.read(buffer); bytesRead > 0; bytesRead = source.read(buffer)) {
+				sink.write(buffer, 0, bytesRead);
+			}
+			sink.flush();
+			
+			// Clean up all file handles.
+			inputStream.close();
+			inputStream = null;
+			outputStream.close();
+			outputStream = null;
+			
+			return outputFile;
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read the changeset file " + fileName + " from the server.", e);
+		} finally {
+			try {
+				if (inputStream != null) {
+					inputStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to changeset download stream.", e);
+			}
+			try {
+				if (outputStream != null) {
+					outputStream.close();
+				}
+			} catch (IOException e) {
+				// We are already in an error condition so log and continue.
+				LOG.log(Level.WARNING, "Unable to changeset output stream.", e);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Downloads the changeset files from the server and writes their contents
+	 * to the output task.
+	 */
+	private void download() {
+		IntervalDownloaderConfiguration configuration;
+		TimestampTracker timestampTracker;
+		ChangesetFileNameFormatter fileNameFormatter;
+		Date currentTime;
+		Date maximumTime;
+		URL baseUrl;
+		int maxDownloadCount;
+		int downloadCount;
+		ArrayList<File> tmpFileList;
+		ArrayList<RunnableChangeSource> tasks;
+		ArrayList<TaskRunner> taskRunners;
+		boolean tasksSuccessful;
+		
+		// Instantiate utility objects.
+		configuration = new IntervalDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
+		timestampTracker = new TimestampTracker(
+			new File(workingDirectory, TSTAMP_FILE),
+			new File(workingDirectory, TSTAMP_NEW_FILE)
+		);
+		fileNameFormatter = new ChangesetFileNameFormatter(
+			configuration.getChangeFileBeginFormat(),
+			configuration.getChangeFileEndFormat()
+		);
+		
+		// Create the base url.
+		try {
+			baseUrl = new URL(configuration.getBaseUrl());
+		} catch (MalformedURLException e) {
+			throw new OsmosisRuntimeException(
+					"Unable to convert URL string (" + configuration.getBaseUrl() + ") into a URL.", e);
+		}
+		
+		tmpFileList = new ArrayList<File>();
+		
+		// Load the current time from the timestamp tracking file.
+		currentTime = timestampTracker.getTime();
+		
+		// Load the latest timestamp from the server.
+		maximumTime = getServerTimestamp(baseUrl);
+		
+		// Process until all files have been retrieved from the server.
+		maxDownloadCount = configuration.getMaxDownloadCount();
+		downloadCount = 0;
+		while ((maxDownloadCount == 0 || downloadCount < maxDownloadCount) && currentTime.before(maximumTime)) {
+			Date nextTime;
+			String downloadFileName;
+			
+			// Calculate the end of the next time interval.
+			nextTime = new Date(currentTime.getTime() + configuration.getIntervalLength());
+			
+			// Generate the filename to be retrieved from the server.
+			downloadFileName = fileNameFormatter.generateFileName(currentTime, nextTime);
+			
+			// Download the changeset from the server.
+			tmpFileList.add(downloadChangesetFile(downloadFileName, baseUrl));
+			
+			// Move the current time to the next interval.
+			currentTime = nextTime;
+			
+			// Increment the current download count.
+			downloadCount++;
+		}
+		
+		// Generate a set of tasks for loading the change files and merge them
+		// into a single change stream.
+		tasks = new ArrayList<RunnableChangeSource>();
+		for (File tmpFile : tmpFileList) {
+			XmlChangeReader changeReader;
+			
+			// Generate a change reader task for the current task.
+			changeReader = new XmlChangeReader(
+				tmpFile,
+				true,
+				CompressionMethod.GZip
+			);
+			
+			// If tasks already exist, a change merge task must be used to merge
+			// existing output with this task output, otherwise this task can be
+			// added to the list directly.
+			if (tasks.size() > 0) {
+				ChangeMerger changeMerger;
+				
+				// Create a new change merger merging the last task output with the current task.
+				changeMerger = new ChangeMerger(ConflictResolutionMethod.LatestSource, 10);
+				
+				// Connect the inputs of this merger to the most recent change
+				// output and the new change output.
+				tasks.get(tasks.size() - 1).setChangeSink(changeMerger.getChangeSink(0));
+				changeReader.setChangeSink(changeMerger.getChangeSink(1));
+				
+				tasks.add(changeReader);
+				tasks.add(changeMerger);
+				
+			} else {
+				tasks.add(changeReader);
+			}
+		}
+		
+		// We only need to execute sub-threads if tasks exist, otherwise we must
+		// notify the sink that we have completed.
+		if (tasks.size() > 0) {
+			// Connect the last task to the change sink.
+			tasks.get(tasks.size() - 1).setChangeSink(changeSink);
+			
+			// Create task runners for each of the tasks to provide thread
+			// management.
+			taskRunners = new ArrayList<TaskRunner>(tasks.size());
+			for (int i = 0; i < tasks.size(); i++) {
+				taskRunners.add(new TaskRunner(tasks.get(i), "Thread-" + taskId + "-worker" + i));
+			}
+			
+			// Launch all of the tasks.
+			for (int i = 0; i < taskRunners.size(); i++) {
+				TaskRunner taskRunner;
+				
+				taskRunner = taskRunners.get(i);
+				
+				LOG.fine("Launching changeset worker + " + i + " in a new thread.");
+				
+				taskRunner.start();
+			}
+			
+			// Wait for all the tasks to complete.
+			tasksSuccessful = true;
+			for (int i = 0; i < taskRunners.size(); i++) {
+				TaskRunner taskRunner;
+				
+				taskRunner = taskRunners.get(i);
+				
+				LOG.fine("Waiting for changeset worker " + i + " to complete.");
+				
+				try {
+					taskRunner.join();
+				} catch (InterruptedException e) {
+					// We are already in an error condition so log and continue.
+					LOG.log(Level.WARNING, "The wait for task completion was interrupted.", e);
+				}
+				
+				if (!taskRunner.isSuccessful()) {
+					LOG.log(Level.SEVERE, "Changeset worker " + i + " failed", taskRunner.getException());
+					
+					tasksSuccessful = false;
+				}
+			}
+			
+		} else {
+			changeSink.complete();
+			tasksSuccessful = true;
+		}
+		
+		// Remove the temporary files.
+		for (File tmpFile : tmpFileList) {
+			if (!tmpFile.delete()) {
+				LOG.warning("Unable to delete file " + tmpFile.getName());
+			}
+		}
+		
+		if (!tasksSuccessful) {
+			throw new OsmosisRuntimeException("One or more changeset workers failed.");
+		}
+		
+		// Update the timestamp tracker.
+		timestampTracker.setTime(currentTime);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		FileBasedLock fileLock;
+		
+		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
+		
+		try {
+			changeSink.initialize(Collections.<String, Object>emptyMap());
+			
+			fileLock.lock();
+			
+			download();
+			
+			fileLock.unlock();
+			
+		} finally {
+			changeSink.release();
+			fileLock.release();
+		}
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderFactory.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializer.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializer.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializer.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializer.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializerFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializerFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializerFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloaderInitializerFactory.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java
new file mode 100644
index 0000000..881ee01
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java
@@ -0,0 +1,127 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorter;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+
+
+/**
+ * Downloads a set of replication files from a HTTP server, and merges them into a
+ * single output stream. It tracks the intervals covered by the current files
+ * and stores the current timestamp between invocations forming the basis of a
+ * replication mechanism.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationDownloader extends BaseReplicationDownloader implements RunnableChangeSource {
+	
+	private ChangeSorter changeSorter;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public ReplicationDownloader(File workingDirectory) {
+		super(workingDirectory);
+		
+		// We will sort all contents prior to sending to the sink. This adds overhead that may not
+		// always be required, but provides consistent behaviour.
+		changeSorter = new ChangeSorter(new ChangeForStreamableApplierComparator());
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		changeSorter.setChangeSink(changeSink);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processInitialize(Map<String, Object> metaData) {
+		changeSorter.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processInitializeState(ReplicationState initialState) {
+		// We don't allow automatic state file initialization for replication
+		// downloading. This is the most common task used by end users to
+		// retrieve replication information. It is very rare that a user would
+		// want to begin from the latest server state file. In most cases
+		// they'll want to begin processing from a known point in time.
+		throw new OsmosisRuntimeException("The local state.txt file doesn't exist."
+				+ " See http://wiki.openstreetmap.org/wiki/Osmosis/Detailed_Usage"
+				+ "#--read-replication-interval-init_.28--rrii.29"
+				+ " for more details on how to create the state.txt file.");
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState) {
+		final ChangeSink localChangeSink = changeSorter;
+		
+		xmlReader.setChangeSink(new ChangeSink() {
+			private ChangeSink suppressedChangeSink = localChangeSink;
+
+			@Override
+			public void initialize(Map<String, Object> metaData) {
+				// Suppress the call.
+			}
+			@Override
+			public void process(ChangeContainer change) {
+				suppressedChangeSink.process(change);
+			}
+			@Override
+			public void complete() {
+				// Suppress the call.
+			}
+			@Override
+			public void release() {
+				// Suppress the call.
+			} });
+		
+		xmlReader.run();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processComplete() {
+		changeSorter.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processRelease() {
+		changeSorter.release();
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderFactory.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializer.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializer.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializer.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializer.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializerFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializerFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializerFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloaderInitializerFactory.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java
new file mode 100644
index 0000000..6dc51fe
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java
@@ -0,0 +1,328 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorter;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.FileReplicationStore;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.replication.common.ReplicationStore;
+import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
+import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationFileMergerConfiguration;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
+
+
+/**
+ * Consumes the files in a replication directory and combines them into larger
+ * replication files grouped by a time interval. This allows replication files
+ * created at regular intervals to be combined into larger files for more
+ * efficient consumption where latency is less of an issue.
+ */
+public class ReplicationFileMerger extends BaseReplicationDownloader {
+	private static final Logger LOG = Logger.getLogger(ReplicationFileMerger.class.getName());
+
+	private static final String DATA_DIRECTORY = "data";
+	private static final String CONFIG_FILE = "configuration.txt";
+
+	private boolean sinkActive;
+	private ChangeSink changeSink;
+	private ReplicationState currentDataState;
+	private PropertiesPersister dataStatePersister;
+	private ReplicationStore replicationStore;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public ReplicationFileMerger(File workingDirectory) {
+		super(workingDirectory);
+
+		replicationStore = new FileReplicationStore(new File(getWorkingDirectory(), DATA_DIRECTORY), true);
+
+		sinkActive = false;
+	}
+
+
+	private Date alignDateToIntervalBoundary(Date requestedDate, long intervalLength) {
+		long remainder;
+
+		remainder = requestedDate.getTime() % intervalLength;
+
+		if (remainder > 0) {
+			return new Date(requestedDate.getTime() - remainder);
+		} else {
+			return requestedDate;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected Date calculateMaximumTimestamp(ReplicationDownloaderConfiguration configuration, Date serverTimestamp,
+			Date localTimestamp) {
+		Date maximumTimestamp;
+		long intervalLength;
+
+		// Read the current persisted state.
+		currentDataState = new ReplicationState(dataStatePersister.loadMap());
+
+		// Get the default maximum timestamp according to base calculations.
+		maximumTimestamp = super.calculateMaximumTimestamp(configuration, serverTimestamp, localTimestamp);
+
+		// Align the maximum timestamp to an interval boundary.
+		intervalLength = getConfiguration().getIntervalLength();
+		if (intervalLength > 0) {
+			maximumTimestamp = alignDateToIntervalBoundary(maximumTimestamp, intervalLength);
+
+			// For the first sequence file, we make sure we make sure that the
+			// maximum timestamp is
+			// ahead of the data timestamp. If it isn't, we move the maximum
+			// timestamp backwards by
+			// one interval to address the case where the local timestamp is
+			// behind the data
+			// timestamp causing some data to be downloaded and processed.
+			if (currentDataState.getSequenceNumber() == 0) {
+				if (maximumTimestamp.compareTo(currentDataState.getTimestamp()) <= 0) {
+					maximumTimestamp = new Date(maximumTimestamp.getTime() - intervalLength);
+				}
+			}
+		}
+
+		// If the maximum timestamp exceeds the current local timestamp, but
+		// does not exceed the current data timestamp then we shouldn't perform
+		// any processing. If we download data we'll be forced to open a new
+		// data file for the next interval which will not be populated fully
+		// if the maximum timestamp is not high enough. To stop processing, we
+		// simply set the maximum timestamp to equal the current local
+		// timestamp.
+		if ((maximumTimestamp.compareTo(localTimestamp) > 0)
+				&& (maximumTimestamp.compareTo(currentDataState.getTimestamp()) <= 0)) {
+			maximumTimestamp = localTimestamp;
+		}
+		
+		LOG.finer("Maximum timestamp is " + maximumTimestamp);
+
+		return maximumTimestamp;
+	}
+
+
+	private ChangeSink buildResultWriter(long sequenceNumber) {
+		XmlChangeWriter xmlChangeWriter;
+		ChangeSorter changeSorter;
+
+		xmlChangeWriter = replicationStore.saveData(sequenceNumber);
+
+		changeSorter = new ChangeSorter(new ChangeForStreamableApplierComparator());
+		changeSorter.setChangeSink(xmlChangeWriter);
+
+		return changeSorter;
+	}
+
+
+	private void writeChangeset(XmlChangeReader xmlReader) {
+		final ChangeSink localChangeSink = changeSink;
+
+		xmlReader.setChangeSink(new ChangeSink() {
+			private ChangeSink suppressedWriter = localChangeSink;
+
+
+			@Override
+			public void initialize(Map<String, Object> metaData) {
+				// Suppress the call.
+			}
+
+
+			@Override
+			public void process(ChangeContainer change) {
+				suppressedWriter.process(change);
+			}
+
+
+			@Override
+			public void complete() {
+				// Suppress the call.
+			}
+
+
+			@Override
+			public void release() {
+				// Suppress the call.
+			}
+		});
+
+		xmlReader.run();
+	}
+
+
+	private ReplicationFileMergerConfiguration getConfiguration() {
+		return new ReplicationFileMergerConfiguration(new File(getWorkingDirectory(), CONFIG_FILE));
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processInitialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processInitializeState(ReplicationState initialState) {
+		Date initialDate;
+		Date alignedDate;
+		long intervalLength;
+
+		intervalLength = getConfiguration().getIntervalLength();
+
+		initialDate = initialState.getTimestamp();
+
+		// Align the date to an interval boundary.
+		alignedDate = alignDateToIntervalBoundary(initialDate, intervalLength);
+
+		// If the date has been moved, then advance it to the next interval. We
+		// do this because
+		// during replication we never claim to have covered a time period that
+		// we haven't received
+		// data for. We may include extra data from a previous interval. By
+		// advancing the stated
+		// initial timestamp to the next interval our first replication will
+		// include some data from
+		// the previous interval.
+		if (alignedDate.compareTo(initialDate) < 0) {
+			alignedDate = new Date(alignedDate.getTime() + intervalLength);
+		}
+
+		// Create an initial replication state object.
+		currentDataState = new ReplicationState(alignedDate, 0);
+
+		// Write out the initial "0" state file.
+		replicationStore.saveState(currentDataState);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState) {
+		int intervalLength;
+		ReplicationFileMergerConfiguration configuration;
+
+		configuration = getConfiguration();
+
+		// Get the configured interval length.
+		intervalLength = configuration.getIntervalLength();
+
+		// If this is the first time through, initialise a writer for the next
+		// sequence number.
+		if (!sinkActive) {
+			// Increment the current sequence number.
+			currentDataState.setSequenceNumber(currentDataState.getSequenceNumber() + 1);
+
+			// Initialise an output file for the new sequence number.
+			LOG.finer("Opening change sink for interval with sequence number " + currentDataState.getSequenceNumber());
+			changeSink = buildResultWriter(currentDataState.getSequenceNumber());
+		}
+
+		if (intervalLength > 0) {
+			// If this is the first time through, align the timestamp at the
+			// next boundary.
+			if (!sinkActive) {
+				Date intervalEnd;
+
+				intervalEnd = new Date(currentDataState.getTimestamp().getTime() + intervalLength);
+				intervalEnd = alignDateToIntervalBoundary(intervalEnd, intervalLength);
+				currentDataState.setTimestamp(intervalEnd);
+				LOG.finer("End of current interval is " + intervalEnd);
+			}
+
+			// If the replication state has moved us past the current interval
+			// end point we need to
+			// open a new interval. This may occur many times if the current
+			// replication state moves
+			// us past several intervals.
+			while (replicationState.getTimestamp().compareTo(currentDataState.getTimestamp()) > 0) {
+
+				// If we have an open changeset writer, close it and save the
+				// current state.
+				LOG.finer("Closing change sink for interval with sequence number "
+						+ currentDataState.getSequenceNumber());
+				changeSink.complete();
+				changeSink.release();
+
+				replicationStore.saveState(currentDataState);
+
+				// Update the state to match the next interval.
+				currentDataState.setSequenceNumber(currentDataState.getSequenceNumber() + 1);
+				currentDataState.setTimestamp(new Date(currentDataState.getTimestamp().getTime()
+						+ configuration.getIntervalLength()));
+
+				// Begin a new interval.
+				LOG.finer("Opening change sink for interval with sequence number "
+						+ currentDataState.getSequenceNumber());
+				changeSink = buildResultWriter(currentDataState.getSequenceNumber());
+			}
+
+		} else {
+			// There is no maximum interval set, so simply update the current
+			// state based on the
+			// current replication state.
+			LOG.finer("End of current interval is " + replicationState.getTimestamp());
+			currentDataState.setTimestamp(replicationState.getTimestamp());
+		}
+
+		// Write the changeset to the writer.
+		writeChangeset(xmlReader);
+
+		// We are guaranteed to have an active writer at this point.
+		sinkActive = true;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processComplete() {
+		if (sinkActive) {
+			LOG.finer("Closing change sink for interval with sequence number " + currentDataState.getSequenceNumber());
+			changeSink.complete();
+			replicationStore.saveState(currentDataState);
+
+			changeSink.release();
+			changeSink = null;
+
+			sinkActive = false;
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void processRelease() {
+		if (sinkActive) {
+			changeSink.release();
+			sinkActive = false;
+		}
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerFactory.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializer.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializer.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializer.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializer.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializerFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializerFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializerFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMergerInitializerFactory.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java
new file mode 100644
index 0000000..f43ec31
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java
@@ -0,0 +1,159 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.common.RunnableTask;
+import org.openstreetmap.osmosis.core.util.FileBasedLock;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.replication.common.ServerStateReader;
+import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
+
+/**
+ * Compares the timestamp of a local replication directory and the timestamp on the 
+ * HTTP server that is configured to provide the replication files. It calculates 
+ * the number of seconds the local replication directory is behind the HTTP server
+ * and prints it to stdout.
+ * 
+ * @author Peter Koerner
+ */
+public class ReplicationLagReader implements RunnableTask {
+	private static final Logger LOG = Logger.getLogger(ReplicationLagReader.class.getName());
+	private static final String LOCK_FILE_NAME = "download.lock";
+	private static final String CONFIG_FILE = "configuration.txt";
+	private static final String LOCAL_STATE_FILE = "state.txt";
+	
+	private boolean humanReadable;
+	private File workingDirectory;
+	private ServerStateReader serverStateReader;
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 * @param humanReadable
+	 *            Print the replication lag in a Hours, Minutes and Seconds
+	 *            instead of the raw number of seconds
+	 */
+	public ReplicationLagReader(File workingDirectory, boolean humanReadable) {
+		this.workingDirectory = workingDirectory;
+		this.humanReadable = humanReadable;
+		
+		serverStateReader = new ServerStateReader();
+	}
+	
+	
+	/**
+	 * Calculate the replication lag and print it to stdout
+	 */
+	private void getLag() {
+		ReplicationDownloaderConfiguration configuration;
+		ReplicationState serverState;
+		ReplicationState localState;
+		PropertiesPersister localStatePersistor;
+		
+		// Instantiate utility objects.
+		configuration = new ReplicationDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
+		
+		// Obtain the server state.
+		LOG.fine("Reading current server state.");
+		serverState = serverStateReader.getServerState(configuration.getBaseUrl());
+		
+		// Build the local state persister which is used for both loading and storing local state.
+		localStatePersistor = new PropertiesPersister(new File(workingDirectory, LOCAL_STATE_FILE));
+		
+		// If local state isn't available we need to fail because no lag can be calculated.
+		if (!localStatePersistor.exists()) {
+			throw new OsmosisRuntimeException("Can't read local state.");
+		}
+		
+		// fetch the local state from the file
+		localState = new ReplicationState(localStatePersistor.loadMap());
+		
+		// extract the time of the local and the remote state files
+		long local = localState.getTimestamp().getTime();
+		long server = serverState.getTimestamp().getTime();
+		
+		// we assume the server being before the local state while calculating the difference
+		long lag = (server - local) / 1000;
+		
+		// check if a human readable version is requested
+		if (this.humanReadable) {
+			
+			if (lag > 86400) {
+				
+				// more than a day
+				Object[] args = {
+					new Long(lag / 86400), 
+					new Long((lag % 86400) / 3600)
+				};
+				System.out.println(
+					new MessageFormat("{0} day(s) and {1} hour(s)").format(args)
+				);
+				
+			} else if (lag > 3600) {
+				
+				// morte than an hour
+				Object[] args = {
+					new Long(lag / 3600), 
+					new Long((lag % 3600) / 60)
+				};
+				System.out.println(
+					new MessageFormat("{0} hour(s) and {1} minute(s)").format(args)
+				);
+				
+			} else if (lag > 60) {
+				
+				// more than a minute
+				Object[] args = {
+					new Long(lag / 60), 
+					new Long(lag % 60)
+				};
+				System.out.println(
+					new MessageFormat("{0} minute(s) and {1} second(s)").format(args)
+				);
+				
+			} else {
+				
+				// just some seconds
+				System.out.println(
+					new MessageFormat("{0} second(s)").format(lag)
+				);
+				
+			}
+			
+		} else {
+			
+			// print out the raw number of seconds
+			System.out.println(lag);
+			
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		FileBasedLock fileLock;
+		
+		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE_NAME));
+		
+		try {
+			fileLock.lock();
+			
+			getLag();
+			
+			fileLock.unlock();
+			
+		} finally {
+			fileLock.release();
+		}
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReaderFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReaderFactory.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReaderFactory.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReaderFactory.java
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationStateWriter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationStateWriter.java
new file mode 100644
index 0000000..82c845f
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationStateWriter.java
@@ -0,0 +1,121 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.util.FileBasedLock;
+import org.openstreetmap.osmosis.core.util.PropertiesPersister;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+
+
+/**
+ * This class manages persistence of state for replication streams into a state
+ * properties file. If used alone, it will store the state for a replication
+ * pipeline, and will discard the output. It can be used within a larger task
+ * performing processing on the replication data. It supports the initialize and
+ * complete method being called multiple times to signify multiple replication
+ * intervals being called within a single pipeline run.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationStateWriter implements ChangeSink {
+
+	private static final Logger LOG = Logger.getLogger(ReplicationStateWriter.class.getName());
+	private static final String LOCK_FILE = "replicate.lock";
+	private static final String STATE_FILE = "state.txt";
+
+	private FileBasedLock fileLock;
+	private boolean lockObtained;
+	private PropertiesPersister statePersistor;
+	private ReplicationState state;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public ReplicationStateWriter(File workingDirectory) {
+		// Create the lock object used to ensure only a single process attempts
+		// to write to the data directory.
+		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
+
+		// Create the object used to persist current state.
+		statePersistor = new PropertiesPersister(new File(workingDirectory, STATE_FILE));
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		if (lockObtained) {
+			throw new OsmosisRuntimeException("initialize has already been called");
+		}
+
+		// Lock the working directory.
+		fileLock.lock();
+		lockObtained = true;
+
+		// Get the replication state from the upstream task.
+		if (!metaData.containsKey(ReplicationState.META_DATA_KEY)) {
+			throw new OsmosisRuntimeException("No replication state has been provided in metadata key "
+					+ ReplicationState.META_DATA_KEY + ".");
+		}
+		state = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+
+		// Populate the state from the existing state if it exists.
+		if (statePersistor.exists()) {
+			state.load(statePersistor.loadMap());
+
+			// The current sequence number must now be incremented.
+			state.setSequenceNumber(state.getSequenceNumber() + 1);
+
+			if (LOG.isLoggable(Level.FINER)) {
+				LOG.finer("Replication sequence number is " + state.getSequenceNumber() + ".");
+			}
+		}
+	}
+
+
+	@Override
+	public void process(ChangeContainer change) {
+		if (!lockObtained) {
+			throw new OsmosisRuntimeException("initialize has not been called");
+		}
+
+		if (state.getSequenceNumber() == 0) {
+			throw new OsmosisRuntimeException("No changes can be included for replication sequence 0.");
+		}
+	}
+
+
+	@Override
+	public void complete() {
+		if (!lockObtained) {
+			throw new OsmosisRuntimeException("initialize has not been called");
+		}
+
+		// Write the global state file.
+		statePersistor.store(state.store());
+		state = null;
+
+		// Release the lock.
+		fileLock.unlock();
+		lockObtained = false;
+	}
+
+
+	@Override
+	public void release() {
+		state = null;
+
+		fileLock.release();
+		lockObtained = false;
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriter.java
new file mode 100644
index 0000000..44cae87
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriter.java
@@ -0,0 +1,96 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+
+
+/**
+ * This task allows a replication stream to be converted to a standard change
+ * stream. It handles the state persistence required by a replication sink, and
+ * then passes the replication data to a standard change sink destination. A
+ * typical use case would be receiving a replication stream live from a
+ * database, then applying those changes to another database where the change
+ * applier task doesn't support the replication metadata extensions.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationToChangeWriter implements ChangeSinkChangeSource {
+
+	/**
+	 * This handles and persists the replication metadata.
+	 */
+	private ReplicationStateWriter stateWriter;
+	private ReplicationState state;
+	private ChangeSink changeSink;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public ReplicationToChangeWriter(File workingDirectory) {
+		stateWriter = new ReplicationStateWriter(workingDirectory);
+	}
+
+
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		// Initialise the replication meta data.
+		stateWriter.initialize(metaData);
+
+		// Get the replication state for this pipeline run.
+		state = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+
+		// Initialise the downstream tasks passing everything except the
+		// replication state.
+		if (state.getSequenceNumber() > 0) {
+			Map<String, Object> downstreamMetaData = new HashMap<String, Object>(metaData);
+			downstreamMetaData.remove(ReplicationState.META_DATA_KEY);
+			changeSink.initialize(downstreamMetaData);
+		}
+	}
+
+
+	@Override
+	public void process(ChangeContainer change) {
+		// Perform replication checks.
+		stateWriter.process(change);
+
+		// Pass the change downstream.
+		changeSink.process(change);
+	}
+
+
+	@Override
+	public void complete() {
+		// We must complete downstream before we complete the replication writer
+		// so that we know the replication data has been committed before we
+		// persist replication state.
+		if (state.getSequenceNumber() > 0) {
+			changeSink.complete();
+		}
+		stateWriter.complete();
+	}
+
+
+	@Override
+	public void release() {
+		changeSink.release();
+		stateWriter.release();
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriterFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriterFactory.java
new file mode 100644
index 0000000..6b7e257
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationToChangeWriterFactory.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkChangeSourceManager;
+
+
+/**
+ * The task manager factory for a replication to change writer.
+ */
+public class ReplicationToChangeWriterFactory extends TaskManagerFactory {
+	private static final String ARG_WORKING_DIRECTORY = "workingDirectory";
+	private static final String DEFAULT_WORKING_DIRECTORY = "./";
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String workingDirectoryString;
+		File workingDirectory;
+
+		// Get the task arguments.
+		workingDirectoryString = getStringArgument(taskConfig, ARG_WORKING_DIRECTORY,
+				getDefaultStringArgument(taskConfig, DEFAULT_WORKING_DIRECTORY));
+
+		// Convert argument strings to strongly typed objects.
+		workingDirectory = new File(workingDirectoryString);
+
+		return new ChangeSinkChangeSourceManager(
+				taskConfig.getId(),
+				new ReplicationToChangeWriter(workingDirectory),
+				taskConfig.getPipeArgs());
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriter.java
new file mode 100644
index 0000000..2f1f4a4
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriter.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.replication.common.FileReplicationStore;
+import org.openstreetmap.osmosis.replication.common.ReplicationState;
+import org.openstreetmap.osmosis.replication.common.ReplicationStore;
+import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
+
+
+/**
+ * This class receives replication streams and writes them to replication files.
+ * It supports the initialize and complete method being called multiple times to
+ * signify multiple replication intervals and each will be written to a
+ * different replication file with a unique sequence number.
+ * 
+ * @author Brett Henderson
+ */
+public class ReplicationWriter implements ChangeSink {
+
+	private ReplicationStore replicationStore;
+	private ReplicationStateWriter stateWriter;
+	private ReplicationState state;
+	private XmlChangeWriter changeWriter;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param workingDirectory
+	 *            The directory containing configuration and tracking files.
+	 */
+	public ReplicationWriter(File workingDirectory) {
+		replicationStore = new FileReplicationStore(workingDirectory, false);
+		stateWriter = new ReplicationStateWriter(workingDirectory);
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		// Initialise the replication meta data.
+		stateWriter.initialize(metaData);
+
+		// Get the replication state for this pipeline run.
+		state = (ReplicationState) metaData.get(ReplicationState.META_DATA_KEY);
+
+		// Initialize a new change writer for the current sequence number.
+		if (state.getSequenceNumber() > 0) {
+			changeWriter = replicationStore.saveData(state.getSequenceNumber());
+		}
+	}
+
+
+	@Override
+	public void process(ChangeContainer change) {
+		changeWriter.process(change);
+	}
+
+
+	@Override
+	public void complete() {
+		if (state.getSequenceNumber() > 0) {
+			// Complete the writing of the change file.
+			changeWriter.complete();
+			changeWriter.release();
+			changeWriter = null;
+		}
+
+		// Write the sequenced state file.
+		replicationStore.saveState(state);
+
+		// We must only complete the state writer after we've finished writing
+		// the replication data and sequence numbered state.
+		stateWriter.complete();
+	}
+
+
+	@Override
+	public void release() {
+		if (changeWriter != null) {
+			changeWriter.release();
+			changeWriter = null;
+		}
+		stateWriter.release();
+	}
+}
diff --git a/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriterFactory.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriterFactory.java
new file mode 100644
index 0000000..65beeac
--- /dev/null
+++ b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationWriterFactory.java
@@ -0,0 +1,40 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.replication.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkManager;
+
+
+/**
+ * The task manager factory for a replication file downloader.
+ */
+public class ReplicationWriterFactory extends TaskManagerFactory {
+	private static final String ARG_WORKING_DIRECTORY = "workingDirectory";
+	private static final String DEFAULT_WORKING_DIRECTORY = "./";
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String workingDirectoryString;
+		File workingDirectory;
+
+		// Get the task arguments.
+		workingDirectoryString = getStringArgument(taskConfig, ARG_WORKING_DIRECTORY,
+				getDefaultStringArgument(taskConfig, DEFAULT_WORKING_DIRECTORY));
+
+		// Convert argument strings to strongly typed objects.
+		workingDirectory = new File(workingDirectoryString);
+
+		return new ChangeSinkManager(
+				taskConfig.getId(),
+				new ReplicationWriter(workingDirectory),
+				taskConfig.getPipeArgs());
+	}
+}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ChangesetFileNameFormatter.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ChangesetFileNameFormatter.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ChangesetFileNameFormatter.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ChangesetFileNameFormatter.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/IntervalDownloaderConfiguration.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/IntervalDownloaderConfiguration.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/IntervalDownloaderConfiguration.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/IntervalDownloaderConfiguration.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationDownloaderConfiguration.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationDownloaderConfiguration.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationDownloaderConfiguration.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationDownloaderConfiguration.java
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationFileMergerConfiguration.java b/osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationFileMergerConfiguration.java
similarity index 100%
rename from replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationFileMergerConfiguration.java
rename to osmosis-replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/impl/ReplicationFileMergerConfiguration.java
diff --git a/replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/intervalConfiguration.txt b/osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/intervalConfiguration.txt
similarity index 100%
rename from replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/intervalConfiguration.txt
rename to osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/intervalConfiguration.txt
diff --git a/replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationDownloaderConfiguration.txt b/osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationDownloaderConfiguration.txt
similarity index 100%
rename from replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationDownloaderConfiguration.txt
rename to osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationDownloaderConfiguration.txt
diff --git a/replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationFileMergerConfiguration.txt b/osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationFileMergerConfiguration.txt
similarity index 100%
rename from replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationFileMergerConfiguration.txt
rename to osmosis-replication/src/main/resources/org/openstreetmap/osmosis/replication/v0_6/impl/replicationFileMergerConfiguration.txt
diff --git a/replication/src/main/resources/osmosis-plugins.conf b/osmosis-replication/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from replication/src/main/resources/osmosis-plugins.conf
rename to osmosis-replication/src/main/resources/osmosis-plugins.conf
diff --git a/osmosis-set/.checkstyle b/osmosis-set/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-set/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-set/.gitignore b/osmosis-set/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-set/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-set/build.gradle b/osmosis-set/build.gradle
new file mode 100644
index 0000000..f6c7b53
--- /dev/null
+++ b/osmosis-set/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java
new file mode 100644
index 0000000..536a949
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java
@@ -0,0 +1,63 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.set.v0_6.ChangeAppenderFactory;
+import org.openstreetmap.osmosis.set.v0_6.ChangeApplierFactory;
+import org.openstreetmap.osmosis.set.v0_6.ChangeDeriverFactory;
+import org.openstreetmap.osmosis.set.v0_6.ChangeMergerFactory;
+import org.openstreetmap.osmosis.set.v0_6.ChangeSimplifierFactory;
+import org.openstreetmap.osmosis.set.v0_6.ChangeToFullHistoryConvertorFactory;
+import org.openstreetmap.osmosis.set.v0_6.EntityMergerFactory;
+import org.openstreetmap.osmosis.set.v0_6.FlattenFilterFactory;
+
+
+/**
+ * The plugin loader for the Set manipulation tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class SetPluginLoader implements PluginLoader {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		Map<String, TaskManagerFactory> factoryMap;
+		
+		factoryMap = new HashMap<String, TaskManagerFactory>();
+		
+		factoryMap.put("apply-change", new ChangeApplierFactory());
+		factoryMap.put("ac", new ChangeApplierFactory());
+		factoryMap.put("derive-change", new ChangeDeriverFactory());
+		factoryMap.put("dc", new ChangeDeriverFactory());
+		factoryMap.put("flatten", new FlattenFilterFactory());
+		factoryMap.put("f", new FlattenFilterFactory());
+		factoryMap.put("merge", new EntityMergerFactory());
+		factoryMap.put("m", new EntityMergerFactory());
+		factoryMap.put("merge-change", new ChangeMergerFactory());
+		factoryMap.put("mc", new ChangeMergerFactory());
+		factoryMap.put("append-change", new ChangeAppenderFactory());
+		factoryMap.put("apc", new ChangeAppenderFactory());
+		factoryMap.put("simplify-change", new ChangeSimplifierFactory());
+		factoryMap.put("simc", new ChangeSimplifierFactory());
+		factoryMap.put("convert-change-to-full-history", new ChangeToFullHistoryConvertorFactory());
+		factoryMap.put("cctfh", new ChangeToFullHistoryConvertorFactory());
+		
+		factoryMap.put("apply-change-0.6", new ChangeApplierFactory());
+		factoryMap.put("derive-change-0.6", new ChangeDeriverFactory());
+		factoryMap.put("flatten-0.6", new FlattenFilterFactory());
+		factoryMap.put("merge-0.6", new EntityMergerFactory());
+		factoryMap.put("merge-change-0.6", new ChangeMergerFactory());
+		factoryMap.put("append-change-0.6", new ChangeAppenderFactory());
+		factoryMap.put("simplify-change-0.6", new ChangeSimplifierFactory());
+		factoryMap.put("convert-change-to-full-history-0.6", new ChangeToFullHistoryConvertorFactory());
+		
+		return factoryMap;
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/BoundRemovedAction.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/BoundRemovedAction.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/BoundRemovedAction.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/BoundRemovedAction.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java
new file mode 100644
index 0000000..245fc47
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java
@@ -0,0 +1,116 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiChangeSinkRunnableChangeSource;
+
+/**
+ * Combines multiple change sources into a single data set. It is done by writing the contents of
+ * each of the input sources to the sink in order.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeAppender implements MultiChangeSinkRunnableChangeSource {
+	
+	private List<DataPostbox<ChangeContainer>> sources;
+	private ChangeSink changeSink;
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sourceCount
+	 *            The number of sources to be appended.
+	 * @param inputBufferCapacity
+	 *            The capacity of the buffer to use for each source, in objects.
+	 */
+	public ChangeAppender(int sourceCount, int inputBufferCapacity) {
+		sources = new ArrayList<DataPostbox<ChangeContainer>>(sourceCount);
+		
+		for (int i = 0; i < sourceCount; i++) {
+			sources.add(new DataPostbox<ChangeContainer>(inputBufferCapacity));
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public ChangeSink getChangeSink(int instance) {
+		if (instance < 0 || instance >= sources.size()) {
+			throw new OsmosisRuntimeException("Sink instance " + instance + " is not valid.");
+		}
+		
+		return new DataPostboxChangeSink(sources.get(instance));
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int getChangeSinkCount() {
+		return sources.size();
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void run() {
+		try {
+			Map<String, Object> metaData;
+			
+			metaData = new HashMap<String, Object>();
+			
+			// Get the initialization data from each source in turn and merge
+			// it. If the same data exists in multiple sources the last will
+			// win.
+			for (DataPostbox<ChangeContainer> source : sources) {
+				metaData.putAll(source.outputInitialize());
+			}
+			changeSink.initialize(metaData);
+			
+			// Write the data from each source to the sink in turn.
+			for (DataPostbox<ChangeContainer> source : sources) {
+				while (source.hasNext()) {
+					changeSink.process(source.getNext());
+				}
+			}
+			
+			changeSink.complete();
+			
+			// Complete all input sources.
+			for (DataPostbox<ChangeContainer> source : sources) {
+				source.outputComplete();
+			}
+		
+		} finally {
+			changeSink.release();
+
+			// Release all input sources.
+			for (DataPostbox<ChangeContainer> source : sources) {
+				source.outputRelease();
+			}
+		}
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java
new file mode 100644
index 0000000..00cdecc
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java
@@ -0,0 +1,244 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedDeltaChangePipeValidator;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkMultiChangeSinkRunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
+
+
+/**
+ * Applies a change set to an input source and produces an updated data set.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeApplier implements MultiSinkMultiChangeSinkRunnableSource {
+	
+	private Sink sink;
+	private DataPostbox<EntityContainer> basePostbox;
+	private SortedEntityPipeValidator sortedEntityValidator;
+	private DataPostbox<ChangeContainer> changePostbox;
+	private SortedDeltaChangePipeValidator sortedChangeValidator;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param inputBufferCapacity
+	 *            The size of the buffers to use for input sources.
+	 */
+	public ChangeApplier(int inputBufferCapacity) {
+		basePostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
+		sortedEntityValidator = new SortedEntityPipeValidator();
+		sortedEntityValidator.setSink(new DataPostboxSink(basePostbox));
+		changePostbox = new DataPostbox<ChangeContainer>(inputBufferCapacity);
+		sortedChangeValidator = new SortedDeltaChangePipeValidator();
+		sortedChangeValidator.setChangeSink(new DataPostboxChangeSink(changePostbox));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public Sink getSink(int instance) {
+		if (instance != 0) {
+			throw new OsmosisRuntimeException("Sink instance " + instance
+					+ " is not valid.");
+		}
+		
+		return sortedEntityValidator;
+	}
+
+
+	/**
+	 * This implementation always returns 1.
+	 * 
+	 * @return 1
+	 */
+	public int getSinkCount() {
+		return 1;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public ChangeSink getChangeSink(int instance) {
+		if (instance != 0) {
+			throw new OsmosisRuntimeException("Change sink instance " + instance
+					+ " is not valid.");
+		}
+		
+		return sortedChangeValidator;
+	}
+
+
+	/**
+	 * This implementation always returns 1.
+	 * 
+	 * @return 1
+	 */
+	public int getChangeSinkCount() {
+		return 1;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * Processes an entity that exists on the base source but not the change
+	 * source.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 */
+	private void processBaseOnlyEntity(EntityContainer entityContainer) {
+		// The base entity doesn't exist on the change source therefore we
+		// simply pass it through.
+		sink.process(entityContainer);
+	}
+	
+	
+	/**
+	 * Processes a change for an entity that exists on the change source but not
+	 * the base source.
+	 * 
+	 * @param changeContainer
+	 *            The change to be processed.
+	 */
+	private void processChangeOnlyEntity(ChangeContainer changeContainer) {
+		// This entity doesn't exist in the "base" source therefore
+		// we would normally expect a create.
+		// But to cover cases where the change is being re-applied or it is a
+		// previously deleted item which will show as a modify we need to be
+		// lenient with error checking.
+		// It is also possible that a delete will come through for a
+		// previously deleted item which can be ignored.
+		if (changeContainer.getAction().equals(ChangeAction.Create)
+				|| changeContainer.getAction().equals(ChangeAction.Modify)) {
+			
+			sink.process(changeContainer.getEntityContainer());
+		}
+	}
+	
+	
+	/**
+	 * Processes a change for an entity that exists on both the base source and
+	 * the change source.
+	 * 
+	 * @param changeContainer
+	 *            The change to be processed.
+	 */
+	private void processBothSourceEntity(EntityContainer entityContainer, ChangeContainer changeContainer) {
+		// The same entity exists in both sources therefore we are
+		// expecting a modify or delete. However a create is possible if the
+		// data is being re-applied so we need to be lenient.
+		if (changeContainer.getAction().equals(ChangeAction.Create)
+				|| changeContainer.getAction().equals(ChangeAction.Modify)) {
+			
+			sink.process(changeContainer.getEntityContainer());
+		}
+	}
+
+
+	/**
+	 * Processes the input sources and sends the updated data stream to the
+	 * sink.
+	 */
+	public void run() {
+		try {
+			EntityContainerComparator comparator;
+			EntityContainer base = null;
+			ChangeContainer change = null;
+			Map<String, Object> metaData;
+			
+			// Create a comparator for comparing two entities by type and identifier.
+			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
+			
+			// Initialise the pipeline with a combination of the metadata from
+			// both inputs. The change stream metadata will be applied second
+			// and will override any values with the same key.
+			metaData = new HashMap<String, Object>();
+			metaData.putAll(basePostbox.outputInitialize());
+			metaData.putAll(changePostbox.outputInitialize());
+			sink.initialize(metaData);
+			
+			// We continue in the comparison loop while both sources still have data.
+			while ((base != null || basePostbox.hasNext()) && (change != null || changePostbox.hasNext())) {
+				int comparisonResult;
+				
+				// Get the next input data where required.
+				if (base == null) {
+					base = basePostbox.getNext();
+				}
+				if (change == null) {
+					change = changePostbox.getNext();
+				}
+				
+				// Compare the two sources.
+				comparisonResult = comparator.compare(base, change.getEntityContainer());
+				
+				if (comparisonResult < 0) {
+					processBaseOnlyEntity(base);
+					base = null;
+					
+				} else if (comparisonResult > 0) {
+					processChangeOnlyEntity(change);
+					change = null;
+					
+				} else {
+					processBothSourceEntity(base, change);
+					base = null;
+					change = null;
+				}
+			}
+			
+			// Any remaining "base" entities are unmodified.
+			while (base != null || basePostbox.hasNext()) {
+				if (base == null) {
+					base = basePostbox.getNext();
+				}
+				processBaseOnlyEntity(base);
+				base = null;
+			}
+			
+			// Process any remaining "change" entities.
+			while (change != null || changePostbox.hasNext()) {
+				if (change == null) {
+					change = changePostbox.getNext();
+				}
+				processChangeOnlyEntity(change);
+				change = null;
+			}
+			
+			sink.complete();
+			basePostbox.outputComplete();
+			changePostbox.outputComplete();
+			
+		} finally {
+			sink.release();
+			
+			basePostbox.outputRelease();
+			changePostbox.outputRelease();
+		}
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java
new file mode 100644
index 0000000..007f483
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java
@@ -0,0 +1,184 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.change.v0_6.impl.TimestampSetter;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableChangeSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
+
+
+/**
+ * Compares two different data sources and produces a set of differences.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeDeriver implements MultiSinkRunnableChangeSource {
+
+	private ChangeSink changeSink;
+	private DataPostbox<EntityContainer> fromPostbox;
+	private DataPostboxSink fromSink;
+	private DataPostbox<EntityContainer> toPostbox;
+	private DataPostboxSink toSink;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param inputBufferCapacity
+	 *            The size of the buffers to use for input sources.
+	 */
+	public ChangeDeriver(int inputBufferCapacity) {
+		fromPostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
+		fromSink = new DataPostboxSink(fromPostbox);
+		toPostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
+		toSink = new DataPostboxSink(toPostbox);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Sink getSink(int instance) {
+		switch (instance) {
+		case 0:
+			return fromSink;
+		case 1:
+			return toSink;
+		default:
+			throw new OsmosisRuntimeException("Sink instance " + instance
+					+ " is not valid.");
+		}
+	}
+
+
+	/**
+	 * This implementation always returns 2.
+	 * 
+	 * @return 2
+	 */
+	public int getSinkCount() {
+		return 2;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * Processes the input sources and sends the changes to the change sink.
+	 */
+	public void run() {
+		try {
+			EntityContainerComparator comparator;
+			EntityContainer fromEntityContainer = null;
+			EntityContainer toEntityContainer = null;
+			TimestampSetter timestampSetter;
+			
+			// Create a comparator for comparing two entities by type and identifier.
+			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
+			
+			// Create an object for setting the current timestamp on entities being deleted.
+			timestampSetter = new TimestampSetter();
+			
+			// We can't get meaningful data from the initialize data on the
+			// input streams, so pass empty meta data to the sink and discard
+			// the input meta data.
+			fromPostbox.outputInitialize();
+			toPostbox.outputInitialize();
+			changeSink.initialize(Collections.<String, Object>emptyMap());
+			
+			// We continue in the comparison loop while both sources still have data.
+			while (
+					(fromEntityContainer != null || fromPostbox.hasNext())
+					&& (toEntityContainer != null || toPostbox.hasNext())) {
+				int comparisonResult;
+				
+				// Get the next input data where required.
+				if (fromEntityContainer == null) {
+					fromEntityContainer = fromPostbox.getNext();
+				}
+				if (toEntityContainer == null) {
+					toEntityContainer = toPostbox.getNext();
+				}
+				
+				// Compare the two sources.
+				comparisonResult = comparator.compare(fromEntityContainer, toEntityContainer);
+				
+				if (comparisonResult < 0) {
+					// The from entity doesn't exist on the to source therefore
+					// has been deleted. We don't know when the entity was
+					// deleted so set the delete time to the current time.
+					changeSink.process(
+							new ChangeContainer(
+									timestampSetter.updateTimestamp(fromEntityContainer),
+									ChangeAction.Delete));
+					fromEntityContainer = null;
+				} else if (comparisonResult > 0) {
+					// The to entity doesn't exist on the from source therefore has
+					// been created.
+					changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Create));
+					toEntityContainer = null;
+				} else {
+					// The entity exists on both sources, therefore we must
+					// compare
+					// the entities directly. If there is a difference, the
+					// entity has been modified.
+					if (!fromEntityContainer.getEntity().equals(toEntityContainer.getEntity())) {
+						changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Modify));
+					}
+					fromEntityContainer = null;
+					toEntityContainer = null;
+				}
+			}
+			
+			// Any remaining "from" entities are deletes.
+			while (fromEntityContainer != null || fromPostbox.hasNext()) {
+				if (fromEntityContainer == null) {
+					fromEntityContainer = fromPostbox.getNext();
+				}
+
+				// The from entity doesn't exist on the to source therefore
+				// has been deleted. We don't know when the entity was
+				// deleted so set the delete time to the current time.
+				changeSink.process(
+						new ChangeContainer(
+								timestampSetter.updateTimestamp(fromEntityContainer),
+								ChangeAction.Delete));
+				fromEntityContainer = null;
+			}
+			// Any remaining "to" entities are creates.
+			while (toEntityContainer != null || toPostbox.hasNext()) {
+				if (toEntityContainer == null) {
+					toEntityContainer = toPostbox.getNext();
+				}
+				changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Create));
+				toEntityContainer = null;
+			}
+			
+			changeSink.complete();
+			fromPostbox.outputComplete();
+			toPostbox.outputComplete();
+			
+		} finally {
+			changeSink.release();
+			
+			fromPostbox.outputRelease();
+			toPostbox.outputRelease();
+		}
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java
new file mode 100644
index 0000000..a0f0975
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java
@@ -0,0 +1,208 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Collections;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdThenVersionComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedHistoryChangePipeValidator;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiChangeSinkRunnableChangeSource;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
+
+
+/**
+ * Merges two change sources into a single data set. Conflicting elements are
+ * resolved by using either the latest timestamp (default) or always selecting
+ * the second source.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeMerger implements MultiChangeSinkRunnableChangeSource {
+	
+	private ChangeSink changeSink;
+	private DataPostbox<ChangeContainer> postbox0;
+	private SortedHistoryChangePipeValidator sortedChangeValidator0;
+	private DataPostbox<ChangeContainer> postbox1;
+	private SortedHistoryChangePipeValidator sortedChangeValidator1;
+	private ConflictResolutionMethod conflictResolutionMethod;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param conflictResolutionMethod
+	 *            The method to used to resolve conflict when two sources
+	 *            contain the same entity.
+	 * @param inputBufferCapacity
+	 *            The size of the buffers to use for input sources.
+	 */
+	public ChangeMerger(ConflictResolutionMethod conflictResolutionMethod, int inputBufferCapacity) {
+		this.conflictResolutionMethod = conflictResolutionMethod;
+		
+		postbox0 = new DataPostbox<ChangeContainer>(inputBufferCapacity);
+		sortedChangeValidator0 = new SortedHistoryChangePipeValidator();
+		sortedChangeValidator0.setChangeSink(new DataPostboxChangeSink(postbox0));
+		
+		postbox1 = new DataPostbox<ChangeContainer>(inputBufferCapacity);
+		sortedChangeValidator1 = new SortedHistoryChangePipeValidator();
+		sortedChangeValidator1.setChangeSink(new DataPostboxChangeSink(postbox1));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public ChangeSink getChangeSink(int instance) {
+		// Determine which postbox should be written to.
+		switch (instance) {
+		case 0:
+			return sortedChangeValidator0;
+		case 1:
+			return sortedChangeValidator1;
+		default:
+			throw new OsmosisRuntimeException("Sink instance " + instance + " is not valid.");
+		}
+	}
+
+
+	/**
+	 * This implementation always returns 2.
+	 * 
+	 * @return 2
+	 */
+	public int getChangeSinkCount() {
+		return 2;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void run() {
+		try {
+			EntityContainerComparator comparator;
+			ChangeContainer changeContainer0 = null;
+			ChangeContainer changeContainer1 = null;
+			
+			// Create a comparator for comparing two entities by type and identifier.
+			comparator = new EntityContainerComparator(new EntityByTypeThenIdThenVersionComparator());
+			
+			// We can't get meaningful data from the initialize data on the
+			// input streams, so pass empty meta data to the sink and discard
+			// the input meta data.
+			postbox0.outputInitialize();
+			postbox1.outputInitialize();
+			changeSink.initialize(Collections.<String, Object>emptyMap());
+			
+			// We continue in the comparison loop while both sources still have data.
+			while (
+					(changeContainer0 != null || postbox0.hasNext())
+					&& (changeContainer1 != null || postbox1.hasNext())) {
+				long comparisonResult;
+				
+				// Get the next input data where required.
+				if (changeContainer0 == null) {
+					changeContainer0 = postbox0.getNext();
+				}
+				if (changeContainer1 == null) {
+					changeContainer1 = postbox1.getNext();
+				}
+				
+				// Compare the two entities.
+				comparisonResult =
+					comparator.compare(changeContainer0.getEntityContainer(), changeContainer1.getEntityContainer());
+				
+				if (comparisonResult < 0) {
+					// Entity 0 doesn't exist on the other source and can be
+					// sent straight through.
+					changeSink.process(changeContainer0);
+					changeContainer0 = null;
+				} else if (comparisonResult > 0) {
+					// Entity 1 doesn't exist on the other source and can be
+					// sent straight through.
+					changeSink.process(changeContainer1);
+					changeContainer1 = null;
+				} else {
+					// The entity exists on both sources so we must resolve the conflict.
+					if (conflictResolutionMethod.equals(ConflictResolutionMethod.Timestamp)) {
+						int timestampComparisonResult;
+						
+						timestampComparisonResult =
+							changeContainer0.getEntityContainer().getEntity().getTimestamp()
+							.compareTo(changeContainer1.getEntityContainer().getEntity().getTimestamp());
+						
+						if (timestampComparisonResult < 0) {
+							changeSink.process(changeContainer1);
+						} else if (timestampComparisonResult > 0) {
+							changeSink.process(changeContainer0);
+						} else {
+							// If both have identical timestamps, use the second source.
+							changeSink.process(changeContainer1);
+						}
+						
+					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.LatestSource)) {
+						changeSink.process(changeContainer1);
+					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.Version)) {
+						int version0 = changeContainer0.getEntityContainer().getEntity().getVersion();
+						int version1 = changeContainer1.getEntityContainer().getEntity().getVersion();
+						if (version0 < version1) {
+							changeSink.process(changeContainer1);
+						} else if (version0 > version1) {
+							changeSink.process(changeContainer0);
+						} else {
+							// If both have identical versions, use the second source.
+							changeSink.process(changeContainer1);
+						}
+
+					} else {
+						throw new OsmosisRuntimeException(
+								"Conflict resolution method " + conflictResolutionMethod + " is not recognized.");
+					}
+					
+					changeContainer0 = null;
+					changeContainer1 = null;
+				}
+			}
+			
+			// Any remaining entities on either source can be sent straight through.
+			while (changeContainer0 != null || postbox0.hasNext()) {
+				if (changeContainer0 == null) {
+					changeContainer0 = postbox0.getNext();
+				}
+				changeSink.process(changeContainer0);
+				changeContainer0 = null;
+			}
+			while (changeContainer1 != null || postbox1.hasNext()) {
+				if (changeContainer1 == null) {
+					changeContainer1 = postbox1.getNext();
+				}
+				changeSink.process(changeContainer1);
+				changeContainer1 = null;
+			}
+			
+			changeSink.complete();
+			
+			postbox0.outputComplete();
+			postbox1.outputComplete();
+			
+		} finally {
+			changeSink.release();
+			
+			postbox0.outputRelease();
+			postbox1.outputRelease();
+		}
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMergerFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMergerFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMergerFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMergerFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java
new file mode 100644
index 0000000..fbf4f32
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java
@@ -0,0 +1,80 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.set.v0_6.impl.ChangeSimplifierImpl;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedHistoryChangePipeValidator;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * Looks at a sorted change stream and condenses multiple changes for a single entity into a single
+ * change.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeSimplifier implements ChangeSinkChangeSource {
+
+	private SortedHistoryChangePipeValidator orderingValidator;
+	private ChangeSimplifierImpl changeSimplifier;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public ChangeSimplifier() {
+		orderingValidator = new SortedHistoryChangePipeValidator();
+		changeSimplifier = new ChangeSimplifierImpl();
+		
+		orderingValidator.setChangeSink(changeSimplifier);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void initialize(Map<String, Object> metaData) {
+		orderingValidator.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		orderingValidator.process(change);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		orderingValidator.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		orderingValidator.release();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		changeSimplifier.setChangeSink(changeSink);
+	}
+
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertor.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertor.java
new file mode 100644
index 0000000..21edf7e
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertor.java
@@ -0,0 +1,74 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * Translates a change stream into a full history stream which is a normal
+ * entity stream with visible attributes.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeToFullHistoryConvertor implements ChangeSinkSource {
+	
+	private Sink sink;
+	
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		// Deleted entities are not visible, all others are.
+		boolean visible = (ChangeAction.Delete != change.getAction());
+		
+		// Set a visible meta-tag on the entity because the Osmosis data model
+		// doesn't natively support visible.
+		EntityContainer entityContainer = change.getEntityContainer().getWriteableInstance();
+		entityContainer.getEntity().getMetaTags().put("visible", visible);
+		
+		sink.process(entityContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		sink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		sink.release();
+	}
+}
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorFactory.java
new file mode 100644
index 0000000..30eabd1
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorFactory.java
@@ -0,0 +1,28 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkSourceManager;
+
+
+/**
+ * The task manager factory for a change to full-history convertor.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeToFullHistoryConvertorFactory extends TaskManagerFactory {
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		return new ChangeSinkSourceManager(
+			taskConfig.getId(),
+			new ChangeToFullHistoryConvertor(),
+			taskConfig.getPipeArgs()
+		);
+	}
+}
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java
new file mode 100644
index 0000000..9c2bfc1
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java
@@ -0,0 +1,296 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Collections;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
+
+
+/**
+ * Merges two sources into a single data set. Conflicting elements are resolved
+ * by using either the latest timestamp (default) or always selecting the second
+ * source.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityMerger implements MultiSinkRunnableSource {
+	
+	private static final Logger LOG = Logger.getLogger(EntityMerger.class.getName());
+
+	private Sink sink;
+	private DataPostbox<EntityContainer> postbox0;
+	private SortedEntityPipeValidator sortedEntityValidator0;
+	private DataPostbox<EntityContainer> postbox1;
+	private SortedEntityPipeValidator sortedEntityValidator1;
+	private ConflictResolutionMethod conflictResolutionMethod;
+	private BoundRemovedAction boundRemovedAction;
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param conflictResolutionMethod
+	 *            The method to used to resolve conflict when two sources
+	 *            contain the same entity.
+	 * @param inputBufferCapacity
+	 *            The size of the buffers to use for input sources.
+	 * @param boundRemovedAction
+	 *            The action to take if the merge operation removes 
+	 *            a bound entity.
+	 */
+	public EntityMerger(ConflictResolutionMethod conflictResolutionMethod, int inputBufferCapacity, 
+			BoundRemovedAction boundRemovedAction) {
+		
+		this.conflictResolutionMethod = conflictResolutionMethod;
+		
+		postbox0 = new DataPostbox<EntityContainer>(inputBufferCapacity);
+		sortedEntityValidator0 = new SortedEntityPipeValidator();
+		sortedEntityValidator0.setSink(new DataPostboxSink(postbox0));
+		
+		postbox1 = new DataPostbox<EntityContainer>(inputBufferCapacity);
+		sortedEntityValidator1 = new SortedEntityPipeValidator();
+		sortedEntityValidator1.setSink(new DataPostboxSink(postbox1));
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public Sink getSink(int instance) {
+		// Determine which postbox should be written to.
+		switch (instance) {
+		case 0:
+			return sortedEntityValidator0;
+		case 1:
+			return sortedEntityValidator1;
+		default:
+			throw new OsmosisRuntimeException("Sink instance " + instance
+					+ " is not valid.");
+		}
+	}
+
+
+	/**
+	 * This implementation always returns 2.
+	 * 
+	 * @return 2
+	 */
+	public int getSinkCount() {
+		return 2;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void run() {
+		try {
+			EntityContainerComparator comparator;
+			EntityContainer entityContainer0 = null;
+			EntityContainer entityContainer1 = null;
+			
+			// Create a comparator for comparing two entities by type and identifier.
+			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
+			
+			// We can't get meaningful data from the initialize data on the
+			// input streams, so pass empty meta data to the sink and discard
+			// the input meta data.
+			postbox0.outputInitialize();
+			postbox1.outputInitialize();
+			sink.initialize(Collections.<String, Object>emptyMap());
+			
+			// BEGIN bound special handling
+			
+			// If there is a bound, it's going to be the first object 
+			// in a properly sorted stream
+			entityContainer0 = nextOrNull(postbox0);
+			entityContainer1 = nextOrNull(postbox1);
+					
+			// There's only need for special processing if there actually is some data
+			// on both streams - no data implies no bound
+			if (entityContainer0 != null && entityContainer1 != null) {
+				Bound bound0 = null;
+				Bound bound1 = null;
+				
+				// If there are any bounds upstream, eat them up
+				if (entityContainer0.getEntity().getType() == EntityType.Bound) {
+					bound0 = (Bound) entityContainer0.getEntity();
+					entityContainer0 = nextOrNull(postbox0);
+				}
+				if (entityContainer1.getEntity().getType() == EntityType.Bound) {
+					bound1 = (Bound) entityContainer1.getEntity();
+					entityContainer1 = nextOrNull(postbox1);
+				}
+
+				// Only post a bound downstream if both upstream sources had a bound.
+				// (Otherwise there's either nothing to post or the posted bound is going
+				// to be smaller than the actual data, which is bad)
+				if (bound0 != null && bound1 != null) {
+					sink.process(new BoundContainer(bound0.union(bound1)));
+				} else if ((bound0 != null && bound1 == null)
+						|| (bound0 == null && bound1 != null)) {
+					handleBoundRemoved(bound0 == null);
+				}
+			}
+			
+			// END bound special handling
+			
+			// We continue in the comparison loop while both sources still have data.
+			while (
+					(entityContainer0 != null || postbox0.hasNext())
+					&& (entityContainer1 != null || postbox1.hasNext())) {
+				long comparisonResult;
+				
+				// Get the next input data where required.
+				if (entityContainer0 == null) {
+					entityContainer0 = postbox0.getNext();
+				}
+				if (entityContainer1 == null) {
+					entityContainer1 = postbox1.getNext();
+				}
+				
+				// Compare the two entities.
+				comparisonResult = comparator.compare(entityContainer0, entityContainer1);
+				
+				if (comparisonResult < 0) {
+					// Entity 0 doesn't exist on the other source and can be
+					// sent straight through.
+					sink.process(entityContainer0);
+					entityContainer0 = null;
+				} else if (comparisonResult > 0) {
+					// Entity 1 doesn't exist on the other source and can be
+					// sent straight through.
+					sink.process(entityContainer1);
+					entityContainer1 = null;
+				} else {
+					// The entity exists on both sources so we must resolve the conflict.
+					if (conflictResolutionMethod.equals(ConflictResolutionMethod.Timestamp)) {
+						int timestampComparisonResult;
+						
+						timestampComparisonResult =
+							entityContainer0.getEntity().getTimestamp()
+								.compareTo(entityContainer1.getEntity().getTimestamp());
+						
+						if (timestampComparisonResult < 0) {
+							sink.process(entityContainer1);
+						} else if (timestampComparisonResult > 0) {
+							sink.process(entityContainer0);
+						} else {
+							// If both have identical timestamps, use the second source.
+							sink.process(entityContainer1);
+						}
+						
+					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.LatestSource)) {
+						sink.process(entityContainer1);
+					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.Version)) {
+						int version0 = entityContainer0.getEntity().getVersion();
+						int version1 = entityContainer1.getEntity().getVersion();
+						if (version0 < version1) {
+							sink.process(entityContainer1);
+						} else if (version0 > version1) {
+							sink.process(entityContainer0);
+						} else {
+							// If both have identical versions, use the second source.
+							sink.process(entityContainer1);
+						}
+
+					} else {
+						throw new OsmosisRuntimeException(
+								"Conflict resolution method " + conflictResolutionMethod + " is not recognized.");
+					}
+					
+					entityContainer0 = null;
+					entityContainer1 = null;
+				}
+			}
+			
+			// Any remaining entities on either source can be sent straight through.
+			while (entityContainer0 != null || postbox0.hasNext()) {
+				if (entityContainer0 == null) {
+					entityContainer0 = postbox0.getNext();
+				}
+				sink.process(entityContainer0);
+				entityContainer0 = null;
+			}
+			while (entityContainer1 != null || postbox1.hasNext()) {
+				if (entityContainer1 == null) {
+					entityContainer1 = postbox1.getNext();
+				}
+				sink.process(entityContainer1);
+				entityContainer1 = null;
+			}
+			
+			sink.complete();
+			
+			postbox0.outputComplete();
+			postbox1.outputComplete();
+			
+		} finally {
+			sink.release();
+			
+			postbox0.outputRelease();
+			postbox1.outputRelease();
+		}
+	}
+		
+	private void handleBoundRemoved(boolean source0BoundMissing) {
+		
+		if (boundRemovedAction == BoundRemovedAction.Ignore) {
+			// Nothing to do
+			return;
+		}
+		
+		// Message for log or exception
+		String missingSourceID, otherSourceID;
+		
+		if (source0BoundMissing) {
+			missingSourceID = "0";
+			otherSourceID = "1";
+		} else {
+			missingSourceID = "1";
+			otherSourceID = "0";
+		}
+		
+		String message = String.format(
+				"Source %s of the merge task has an explicit bound set, but source %s has not. "
+				+ "Therefore the explicit bound has been removed from the merged stream.", 
+				missingSourceID, otherSourceID);
+		
+		// Now actually log or fail.
+		if (boundRemovedAction == BoundRemovedAction.Warn) {
+			LOG.warning(message);
+		} else if (boundRemovedAction == BoundRemovedAction.Fail) {
+			throw new OsmosisRuntimeException(message);
+		}
+	}
+
+
+	private static EntityContainer nextOrNull(DataPostbox<EntityContainer> postbox) {
+
+		if (postbox.hasNext()) {
+			return postbox.getNext();
+		}
+		
+		return null;
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java
new file mode 100644
index 0000000..d8e0278
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java
@@ -0,0 +1,90 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.sort.v0_6.SortedDuplicateEntityPipeValidator;
+
+
+/**
+ * Flatten / simplify a sorted entity stream. (similar to --simplify-change)
+ */
+public class FlattenFilter extends SortedDuplicateEntityPipeValidator {
+	private Sink sink;
+	
+	private Sink flattener = new Sink() {
+		private EntityContainer previousContainer;
+	    
+	    
+		@Override
+	    public void initialize(Map<String, Object> metaData) {
+			sink.initialize(metaData);
+		}
+		
+		/**
+		 * Process a node, way or relation.
+		 * 
+		 * @param currentContainer
+		 *            The entity container to be processed.
+		 */
+		@Override
+		public void process(EntityContainer currentContainer) {
+			if (previousContainer == null) {
+				previousContainer = currentContainer;
+				return;
+			}
+
+			Entity current = currentContainer.getEntity();
+			Entity previous = previousContainer.getEntity();
+
+			if (current.getId() != previous.getId() || !current.getType().equals(previous.getType())) {
+				sink.process(previousContainer);
+				previousContainer = currentContainer;
+				return;
+			}
+
+			if (current.getVersion() > previous.getVersion()) {
+				previousContainer = currentContainer;
+			}
+		}
+
+
+		@Override
+		public void complete() {
+			/*
+			 * If we've stored entities temporarily, we now need to forward the
+			 * stored ones to the output.
+			 */
+			if (previousContainer != null) {
+				sink.process(previousContainer);
+			}
+
+			sink.complete();
+		}
+
+
+		@Override
+		public void release() {
+			sink.release();
+		}
+	};
+
+
+	/**
+	 * Creates a new instance.
+	 */
+	public FlattenFilter() {
+		super.setSink(flattener);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterFactory.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterFactory.java
similarity index 100%
rename from set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterFactory.java
rename to osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterFactory.java
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java
new file mode 100644
index 0000000..d503779
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java
@@ -0,0 +1,125 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+
+
+/**
+ * Looks at a sorted change stream and condenses multiple changes for a single entity into a single
+ * change.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeSimplifierImpl implements ChangeSinkChangeSource {
+
+	private List<ChangeContainer> currentChanges;
+	private ChangeSink changeSink;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public ChangeSimplifierImpl() {
+		currentChanges = new ArrayList<ChangeContainer>();
+	}
+	
+	
+	private void flushCurrentChanges() {
+		ChangeContainer changeBegin;
+		ChangeContainer changeEnd;
+		ChangeAction actionBegin;
+		ChangeAction actionEnd;
+		ChangeAction actionResult;
+		
+		changeBegin = currentChanges.get(0);
+		changeEnd = currentChanges.get(currentChanges.size() - 1);
+		
+		actionBegin = changeBegin.getAction();
+		actionEnd = changeEnd.getAction();
+		
+		// If the final action is a delete we'll send a delete action regardless of whether the
+		// first action was a create just in case the create should have been a modify.
+		// If the first action is a create, then the result is always a create (except for delete
+		// case above).
+		// Everything else is treated as a modify.
+		if (actionEnd.equals(ChangeAction.Delete)) {
+			actionResult = ChangeAction.Delete;
+		} else if (actionBegin.equals(ChangeAction.Create)) {
+			actionResult = ChangeAction.Create;
+		} else {
+			actionResult = ChangeAction.Modify;
+		}
+		
+		changeSink.process(new ChangeContainer(changeEnd.getEntityContainer(), actionResult));
+		
+		currentChanges.clear();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void initialize(Map<String, Object> metaData) {
+		changeSink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		// If the current change is for a different entity to those in our current changes list,
+		// then we must process the current changes.
+		if (currentChanges.size() > 0) {
+			long currentId;
+			
+			currentId = currentChanges.get(0).getEntityContainer().getEntity().getId();
+			
+			if (currentId != change.getEntityContainer().getEntity().getId()) {
+				flushCurrentChanges();
+			}
+		}
+		
+		currentChanges.add(change);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		if (!currentChanges.isEmpty()) {
+			flushCurrentChanges();
+		}
+		
+		changeSink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		changeSink.release();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+}
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java
new file mode 100644
index 0000000..8c71600
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java
@@ -0,0 +1,65 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6.impl;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+
+/**
+ * A change sink that writes all of its data to a postbox to be read by another thread.
+ * 
+ * @author Brett Henderson
+ */
+public class DataPostboxChangeSink implements ChangeSink {
+	private DataPostbox<ChangeContainer> postbox;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param postbox
+	 *            The postbox to write all incoming data into.
+	 */
+	public DataPostboxChangeSink(DataPostbox<ChangeContainer> postbox) {
+		this.postbox = postbox;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void initialize(Map<String, Object> metaData) {
+		postbox.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(ChangeContainer change) {
+		postbox.put(change);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		postbox.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		postbox.release();
+	}
+}
diff --git a/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java
new file mode 100644
index 0000000..a54fc52
--- /dev/null
+++ b/osmosis-set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java
@@ -0,0 +1,65 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6.impl;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.store.DataPostbox;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+
+/**
+ * A sink that writes all of its data to a postbox to be read by another thread.
+ * 
+ * @author Brett Henderson
+ */
+public class DataPostboxSink implements Sink {
+	private DataPostbox<EntityContainer> postbox;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param postbox
+	 *            The postbox to write all incoming data into.
+	 */
+	public DataPostboxSink(DataPostbox<EntityContainer> postbox) {
+		this.postbox = postbox;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+    public void initialize(Map<String, Object> metaData) {
+		postbox.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(EntityContainer entity) {
+		postbox.put(entity);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		postbox.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		postbox.release();
+	}
+}
diff --git a/set/src/main/resources/osmosis-plugins.conf b/osmosis-set/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from set/src/main/resources/osmosis-plugins.conf
rename to osmosis-set/src/main/resources/osmosis-plugins.conf
diff --git a/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderTest.java
similarity index 100%
rename from set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderTest.java
rename to osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppenderTest.java
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierTest.java
new file mode 100644
index 0000000..fc294ae
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplierTest.java
@@ -0,0 +1,223 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+/**
+ * Test the --apply-change task.
+ * 
+ * @author Igor Podolskiy
+ */
+public class ChangeApplierTest extends AbstractDataTest {
+
+	
+	/**
+	 * Test the application of an empty change to a non-empty stream.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void emptyChange() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/empty-change.osc",
+				"v0_6/apply_change/apply-change-base.osm");
+	}
+	
+	/**
+	 * Test the application of a non-empty change to an empty stream.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void emptyBase() throws Exception {
+		applyChange("v0_6/empty-entity.osm",
+				"v0_6/apply_change/change-delete.osc",
+				"v0_6/empty-entity.osm");
+	}
+	
+	/**
+	 * Test the application of an empty change to an empty stream.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void emptyBoth() throws Exception {
+		applyChange("v0_6/empty-entity.osm",
+				"v0_6/empty-change.osc",
+				"v0_6/empty-entity.osm");
+	}
+
+	/**
+	 * Test the creation of a node.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void createNode() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/apply_change/change-create.osc",
+				"v0_6/apply_change/apply-change-create.osm");
+	}
+	
+	/**
+	 * Test the modification of a node.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void modifyNode() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/apply_change/change-modify.osc",
+				"v0_6/apply_change/apply-change-modify.osm");
+	}
+	
+	/**
+	 * Test the deletion of a node.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void deleteNode() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/apply_change/change-delete.osc",
+				"v0_6/apply_change/apply-change-delete.osm");
+	}
+	
+	/**
+	 * Test the creation, modification and deletion of the same entity in a single stream.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public void createModifyDelete() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/apply_change/change-create-modify-delete.osc",
+				"v0_6/apply_change/apply-change-base.osm");
+	}
+
+	/**
+	 * Test the deletion of an entity that does not exist in the source stream.
+	 *
+	 * Deletion of a non-existent entity doesn't change anything.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void deleteNonExistent() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm",
+				"v0_6/apply_change/change-delete-nonexistent.osc",
+				"v0_6/apply_change/apply-change-base.osm");
+	}
+	
+	/**
+	 * Test the modification of an entity that does not exist in the source stream.
+	 * 
+	 * Modification of a non-existent entity has the same effect as its creation.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void modifyNonExistent() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm", 
+				"v0_6/apply_change/change-modify-nonexistent.osc", 
+				"v0_6/apply_change/apply-change-modify-nonexistent.osm");
+	}
+	
+	/**
+	 * Test the creation of an entity that already exists in the source stream.
+	 * 
+	 * Creation of an existent entity has the same effect as a modification.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void createExistent() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base.osm", 
+				"v0_6/apply_change/change-create-existent.osc", 
+				"v0_6/apply_change/apply-change-base.osm");
+	}
+
+	/**
+	 * Test the case when the version in the change stream is lower than in the
+	 * source stream.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void modifyHigherVersion() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base-high.osm", 
+				"v0_6/apply_change/change-modify.osc", 
+				"v0_6/apply_change/apply-change-modify-higher.osm");
+	}
+	
+	/**
+	 * Test the case when the change is longer than the source stream 
+	 * and consists of creates.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void longChangeCreate() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base-node-only.osm", 
+				"v0_6/apply_change/change-big-create.osc", 
+				"v0_6/apply_change/apply-change-big.osm");
+	}
+	
+	/**
+	 * Test the case when the change is longer than the source 
+	 * stream and consists of deletes.
+	 * 
+	 * @throws Exception
+	 *             if something goes wrong
+	 */
+	@Test
+	public void longChangeDelete() throws Exception {
+		applyChange("v0_6/apply_change/apply-change-base-node-only.osm", 
+				"v0_6/apply_change/change-big-delete.osc", 
+				"v0_6/apply_change/apply-change-base-node-only.osm");
+	}
+
+
+	private void applyChange(String sourceFileName, String changeFileName, 
+			String expectedOutputFileName) throws IOException {
+		File sourceFile;
+		File changeFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		sourceFile = dataUtils.createDataFile(sourceFileName);
+		changeFile = dataUtils.createDataFile(changeFileName);
+		expectedOutputFile = dataUtils.createDataFile(expectedOutputFileName);
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-xml-change-0.6", changeFile.getPath(),
+					"--read-xml-0.6", sourceFile.getPath(),
+					"--apply-change-0.6",
+					"--write-xml-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverTest.java
new file mode 100644
index 0000000..79170dd
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriverTest.java
@@ -0,0 +1,135 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReader;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+import org.openstreetmap.osmosis.testutil.v0_6.RunTaskUtilities;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkChangeInspector;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlReader;
+
+/**
+ * Tests for --derive-change task.
+ * 
+ * @author Igor Podolskiy
+ */
+public class ChangeDeriverTest extends AbstractDataTest {
+	
+	/**
+	 * Empty inputs should yield empty change.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test
+	public void emptyInputs() throws Exception {
+		deriveChange("v0_6/empty-entity.osm", 
+				"v0_6/empty-entity.osm", 
+				"v0_6/empty-change.osc");
+	}
+	
+	/**
+	 * Same inputs should yield empty change.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test
+	public void sameInputs() throws Exception {
+		deriveChange("v0_6/derive_change/simple.osm", 
+				"v0_6/derive_change/simple.osm", 
+				"v0_6/empty-change.osc");
+	}
+	
+	/**
+	 * Deriving change with an empty left input should yield
+	 * a change with deletes only.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test
+	public void leftEmpty() throws Exception {
+		deriveChange("v0_6/empty-entity.osm", 
+				"v0_6/derive_change/simple.osm", 
+				"v0_6/derive_change/full-create.osc");
+	}
+	
+	/**
+	 * Deriving change with an empty right input should yield
+	 * a change with creates only.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test
+	public void rightEmpty() throws Exception {
+		// Cannot be tested with file comparison as the derived 
+		// change contains deletes which have a current timestamp 
+		// that cannot be reliably predicted.
+		// Therefore, check all relevant attributes manually.
+		
+		ChangeDeriver deriver = new ChangeDeriver(1);
+		RunnableSource left = new XmlReader(dataUtils.createDataFile(
+				"v0_6/derive_change/simple.osm"), true, CompressionMethod.None);
+		RunnableSource right = new EmptyReader();
+		
+		SinkChangeInspector result = RunTaskUtilities.run(deriver, left, right);
+		List<ChangeContainer> changes = result.getProcessedChanges();
+		
+		Assert.assertEquals(3, changes.size());
+		for (ChangeContainer changeContainer : changes) {
+			Assert.assertEquals(ChangeAction.Delete, changeContainer.getAction());
+		}
+		
+		Entity e;
+		e = changes.get(0).getEntityContainer().getEntity();
+		Assert.assertEquals(EntityType.Node, e.getType());
+		Assert.assertEquals(10, e.getId());
+		Assert.assertEquals(34, e.getVersion());
+		
+		e = changes.get(1).getEntityContainer().getEntity();
+		Assert.assertEquals(EntityType.Way, e.getType());
+		Assert.assertEquals(100, e.getId());
+		Assert.assertEquals(56, e.getVersion());
+
+		e = changes.get(2).getEntityContainer().getEntity();
+		Assert.assertEquals(EntityType.Relation, e.getType());
+		Assert.assertEquals(1000, e.getId());
+		Assert.assertEquals(78, e.getVersion());
+	}
+
+	
+	private void deriveChange(String leftFileName, String rightFileName, 
+			String expectedOutputFileName) throws IOException {
+		File leftFile;
+		File rightFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		leftFile = dataUtils.createDataFile(leftFileName);
+		rightFile = dataUtils.createDataFile(rightFileName);
+		expectedOutputFile = dataUtils.createDataFile(expectedOutputFileName);
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-xml-0.6", rightFile.getPath(),
+					"--read-xml-0.6", leftFile.getPath(),
+					"--derive-change-0.6",
+					"--write-xml-change-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java
new file mode 100644
index 0000000..8dabf8b
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java
@@ -0,0 +1,203 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.misc.v0_6.NullChangeWriter;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the change simplifier task.
+ */
+public class ChangeSimplifierTest extends AbstractDataTest {
+	
+	/**
+	 * Tests that a set of changes is simplified correctly.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void commonCase() throws IOException {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/simplify-change-in.osc");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/simplify-change-out.osc");
+		actualOutputFile = dataUtils.newFile();
+		
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-change-0.6",
+				sourceFile.getPath(),
+				"--simplify-change-0.6",
+				"--write-xml-change-0.6",
+				actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests that simplifying an already simple change successfully 
+	 * yields the same change.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void alreadySimple() throws Exception {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		sourceFile = dataUtils.createDataFile("v0_6/simplify-change-out.osc");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/simplify-change-out.osc");
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-xml-change-0.6", sourceFile.getPath(),
+					"--simplify-change-0.6",
+					"--write-xml-change-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+
+	/**
+	 * Tests that simplifying an empty change successfully 
+	 * yields an empty change.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void empty() throws Exception {
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		expectedOutputFile = dataUtils.createDataFile("v0_6/empty-change.osc");
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-empty-change-0.6",
+					"--simplify-change-0.6",
+					"--write-xml-change-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests that badly ordered input (with respect to the version) is detected correctly.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void badSortOrderVersion() throws Exception {
+		ChangeSimplifier simplifier = new ChangeSimplifier();
+		simplifier.setChangeSink(new NullChangeWriter());
+		simplifier.initialize(new HashMap<String, Object>());
+		Node node;
+
+		node = new Node(new CommonEntityData(1, 2, new Date(), OsmUser.NONE, 2), 1, 1);
+		simplifier.process(new ChangeContainer(new NodeContainer(node), ChangeAction.Modify));
+
+		try {
+			node = new Node(new CommonEntityData(1, 1, new Date(), OsmUser.NONE, 1), 1, 1);
+			simplifier.process(new ChangeContainer(new NodeContainer(node), ChangeAction.Modify));
+		} catch (OsmosisRuntimeException e) {
+			if (e.getMessage().startsWith("Pipeline entities are not sorted")) {
+				return;
+			}
+			throw e;
+		}
+		Assert.fail("Expected exception not thrown");
+	}
+	
+	/**
+	 * Tests that badly ordered input (with respect to the ids) is detected correctly.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void badSortOrderId() throws Exception {
+		ChangeSimplifier simplifier = new ChangeSimplifier();
+		simplifier.setChangeSink(new NullChangeWriter());
+		simplifier.initialize(new HashMap<String, Object>());
+		Node node;
+
+		node = new Node(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 2), 1, 1);
+		simplifier.process(new ChangeContainer(new NodeContainer(node), ChangeAction.Modify));
+
+		try {
+			node = new Node(new CommonEntityData(1, 2, new Date(), OsmUser.NONE, 1), 1, 1);
+			simplifier.process(new ChangeContainer(new NodeContainer(node), ChangeAction.Modify));
+		} catch (OsmosisRuntimeException e) {
+			if (e.getMessage().startsWith("Pipeline entities are not sorted")) {
+				return;
+			}
+			throw e;
+		}
+		Assert.fail("Expected exception not thrown");
+	}
+	
+	/**
+	 * Tests that badly ordered input (with respect to the ids) is detected correctly.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void badSortOrderType() throws Exception {
+		ChangeSimplifier simplifier = new ChangeSimplifier();
+		simplifier.setChangeSink(new NullChangeWriter());
+		simplifier.initialize(new HashMap<String, Object>());
+		Node node;
+		Way way;
+		
+		way = new Way(new CommonEntityData(2, 2, new Date(), OsmUser.NONE, 2));
+		simplifier.process(new ChangeContainer(new WayContainer(way), ChangeAction.Modify));
+
+		try {
+			node = new Node(new CommonEntityData(1, 2, new Date(), OsmUser.NONE, 1), 1, 1);
+			simplifier.process(new ChangeContainer(new NodeContainer(node), ChangeAction.Modify));
+		} catch (OsmosisRuntimeException e) {
+			if (e.getMessage().startsWith("Pipeline entities are not sorted")) {
+				return;
+			}
+			throw e;
+		}
+		Assert.fail("Expected exception not thrown");
+	}
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorTest.java
new file mode 100644
index 0000000..da4d32b
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeToFullHistoryConvertorTest.java
@@ -0,0 +1,45 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the change to full history convertor task.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeToFullHistoryConvertorTest extends AbstractDataTest {
+
+	/**
+	 * Tests appending two change files into a single file.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testConvert() throws IOException {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/change-to-full-history-in.osc");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/change-to-full-history-out.osm");
+		actualOutputFile = dataUtils.newFile();
+
+		// Append the two source files into the destination file.
+		Osmosis.run(new String[] {"-q", "--read-xml-change-0.6",
+				sourceFile.getPath(), "--convert-change-to-full-history-0.6",
+				"--write-xml-0.6", actualOutputFile.getPath() });
+
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerTest.java
new file mode 100644
index 0000000..bb9510e
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/EntityMergerTest.java
@@ -0,0 +1,408 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
+import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReader;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+import org.openstreetmap.osmosis.testutil.v0_6.RunTaskUtilities;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.XmlReader;
+
+/**
+ * Test the --merge task.
+ * 
+ * @author Igor Podolskiy
+ */
+public class EntityMergerTest extends AbstractDataTest {
+	
+	/**
+	 * Tests empty + X == X.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void firstEmpty() throws Exception {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile.getPath(),
+				"--read-empty-0.6",
+				"--merge",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests X + empty == X.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void secondEmpty() throws Exception {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-empty-0.6",
+				"--read-xml-0.6", sourceFile.getPath(),
+				"--merge",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests X + X == X.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void selfMerge() throws Exception {
+		File sourceFile1;
+		File sourceFile2;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--merge",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests the timestamp conflict resolution strategy.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void timestampConflictResolution() throws Exception {
+		File sourceFile1;
+		File sourceFile2;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-timestamp.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-timestamp.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--merge",
+				"conflictResolutionMethod=timestamp",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+		// Timestamp conflict resolution should be commutative.
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--merge",
+				"conflictResolutionMethod=timestamp",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+	}
+	
+	/**
+	 * Tests the version conflict resolution strategy.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void versionConflictResolution() throws Exception {
+		File sourceFile1;
+		File sourceFile2;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-version.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-version.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--merge",
+				"conflictResolutionMethod=version",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+		// Version conflict resolution should be commutative.
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--merge",
+				"conflictResolutionMethod=version",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+	}
+	
+	/**
+	 * Tests the version conflict resolution strategy.
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void secondSourceConflictResolution() throws Exception {
+		File sourceFile1;
+		File sourceFile2;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-secondSource.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-secondSource.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--merge",
+				"conflictResolutionMethod=lastSource",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+		// Timestamp conflict resolution is NOT commutative, 
+		// but it deserves testing as well.
+		// As the mergen-in-2 input does not contain any entities that are not 
+		// in the second source, the output should be identical to the first source.
+		
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--merge",
+				"conflictResolutionMethod=lastSource",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+	}
+	
+	/**
+	 * Tests merging two completely disjunct datasets (no conflicts).
+	 * 
+	 * @throws Exception if something fails
+	 */
+	@Test
+	public void disjunctDatasets() throws Exception {
+		File sourceFile1;
+		File sourceFile2;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile1 = dataUtils.createDataFile("v0_6/merge/merge-in-1.osm");
+		sourceFile2 = dataUtils.createDataFile("v0_6/merge/merge-in-2-disjunct.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/merge/merge-out-disjunct.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--merge",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+		// Merging of disjunct datasets should be commutative.
+		
+		// Run the merge.
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile1.getPath(),
+				"--read-xml-0.6", sourceFile2.getPath(),
+				"--merge",
+				"conflictResolutionMethod=version",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+
+	}
+	
+	/**
+	 * Tests bad sort order in an input stream (node, way, relations not in
+	 * order).
+	 * 
+	 * @throws Exception
+	 *             if something fails
+	 */
+	@Test
+	public void badSortOrderType() throws Exception {
+		File sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-badorder-type.osm");
+
+		mergeAndLookForException(sourceFile, "Pipeline entities are not sorted");
+	}
+	
+	/**
+	 * Tests bad sort order in an input stream (ids not sorted).
+	 * 
+	 * @throws Exception
+	 *             if something fails
+	 */
+	@Test
+	public void badSortOrderId() throws Exception {
+		File sourceFile = dataUtils.createDataFile("v0_6/merge/merge-in-badorder-id.osm");
+
+		mergeAndLookForException(sourceFile, "Pipeline entities are not sorted");
+	}
+
+	/**
+	 * Runs a merge and records the exceptions that happened during the merge.
+	 * 
+	 * The test is considered passed iff at least one exception was thrown and 
+	 * the exception message begins with a given string.
+	 * 
+	 * This method does not use command line parsing because it is impossible 
+	 * to check whether the right exception has been thrown. Also, as all 
+	 * exceptions are OsmosisRuntimeExceptions, we need to check the message
+	 * so the JUnit expected exception facility is no good here. 
+	 * 
+	 * To add insult to injury, running a merge task involves three worker threads; 
+	 * the exception we expect is thrown on one of those worker threads which brings 
+	 * the pipeline down. But we want to check for that one source exception
+	 * is thrown for the correct reason, otherwise the test becomes very unspecific.
+	 * 
+	 */
+	private void mergeAndLookForException(File sourceFile, String exceptionMessagePrefix) throws Exception {
+		final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<Throwable>());
+		
+		XmlReader reader = new XmlReader(sourceFile, false, CompressionMethod.None);
+
+		EntityMerger merger = new EntityMerger(
+				ConflictResolutionMethod.LatestSource, 1, BoundRemovedAction.Ignore);
+
+		RunTaskUtilities.run(merger, reader, new EmptyReader(), new Thread.UncaughtExceptionHandler() {
+			@Override
+			public void uncaughtException(Thread t, Throwable e) {
+				exceptions.add(e);
+			}
+		});
+		
+		// At least one of those exceptions should be a "Pipeline not sorted" one
+		boolean sortExceptionFound = false;
+		for (Throwable t : exceptions) {
+			if (!(t instanceof OsmosisRuntimeException)) {
+				Assert.fail("Unexpected exception thrown: " + t);
+			}
+			
+			sortExceptionFound |= t.getMessage().startsWith(exceptionMessagePrefix);
+		}
+		
+		if (!sortExceptionFound) {
+			Assert.fail("Expected exception not thrown");
+		}
+	}
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterTest.java
new file mode 100644
index 0000000..a484d32
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilterTest.java
@@ -0,0 +1,102 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+/**
+ * Tests the flatten filter.
+ * 
+ * @author Igor Podolskiy
+ */
+public class FlattenFilterTest extends AbstractDataTest {
+	/**
+	 * Tests that a set of changes is simplified correctly.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void commonCase() throws IOException {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/flatten-in.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/flatten-out.osm");
+		actualOutputFile = dataUtils.newFile();
+		
+		Osmosis.run(
+			new String [] {
+				"-q",
+				"--read-xml-0.6", sourceFile.getPath(),
+				"--flatten-0.6",
+				"--write-xml-0.6", actualOutputFile.getPath()
+			}
+		);
+		
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+	
+	/**
+	 * Tests that simplifying an already simple change successfully 
+	 * yields the same change.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void alreadyFlattened() throws Exception {
+		File sourceFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		sourceFile = dataUtils.createDataFile("v0_6/flatten-out.osm");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/flatten-out.osm");
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-xml-0.6", sourceFile.getPath(),
+					"--flatten-0.6",
+					"--write-xml-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+
+	/**
+	 * Tests that simplifying an empty change successfully 
+	 * yields an empty change.
+	 * 
+	 * @throws Exception
+	 *             if anything fails.
+	 */
+	@Test
+	public void empty() throws Exception {
+		File expectedOutputFile;
+		File actualOutputFile;
+		
+		expectedOutputFile = dataUtils.createDataFile("v0_6/empty-entity.osm");
+		actualOutputFile = dataUtils.newFile();
+
+		Osmosis.run(
+				new String [] {
+					"-q",
+					"--read-empty-0.6",
+					"--flatten-0.6",
+					"--write-xml-0.6", actualOutputFile.getPath()
+				}
+			);
+
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+}
diff --git a/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java
new file mode 100644
index 0000000..a705e57
--- /dev/null
+++ b/osmosis-set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java
@@ -0,0 +1,236 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.set.v0_6;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
+import org.openstreetmap.osmosis.core.misc.v0_6.EmptyReader;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.testutil.v0_6.RunTaskUtilities;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+
+/**
+ * Tests bounding box processing in merge tasks.
+ * 
+ * @author Igor Podolskiy
+ */
+public class MergeBoundTest {
+
+	/**
+	 * A simple dummy ID generator for the helper source class.
+	 */
+	private static AtomicInteger idGenerator = new AtomicInteger(1000);
+	
+	/**
+	 * Tests the proper working of the merge task if neither 
+	 * source has a declared bound.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testNeitherHasBound() throws Exception {
+		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), false);
+		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), false);
+
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
+
+		Assert.assertEquals(2, mergedList.size());
+		for (EntityContainer entityContainer : mergedList) {
+			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
+		}
+	}
+	
+	/**
+	 * Tests whether merge will delete the declared bound if only source 0 
+	 * has a declared bound.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testSource0HasBound() throws Exception {
+		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), true);
+		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), false);
+
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+
+		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
+		Assert.assertEquals(2, mergedList.size());
+		for (EntityContainer entityContainer : mergedList) {
+			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
+		}
+	}
+	
+	/**
+	 * Tests whether merge will delete the declared bound if only source 1 
+	 * has a declared bound.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testSource1HasBound() throws Exception {
+		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), false);
+		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), true);
+
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
+		
+		Assert.assertEquals(2, mergedList.size());
+		for (EntityContainer entityContainer : mergedList) {
+			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
+		}
+	}
+	
+	/**
+	 * Test the proper computation of the union bound iff both sources
+	 * have bounds.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testBothHaveBounds() throws Exception {
+		Bound bound0 = new Bound(1, 2, 4, 3, "source1");
+		RunnableSource source0 = new BoundSource(bound0, true);
+
+		Bound bound1 = new Bound(5, 6, 8, 7, "source2");
+		RunnableSource source1 = new BoundSource(bound1, true);
+
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
+		Assert.assertEquals(3, mergedList.size());
+		Assert.assertEquals(EntityType.Bound, mergedList.get(0).getEntity().getType());
+		
+		// Check the bound
+		Bound bound01 = (Bound) mergedList.get(0).getEntity();
+		Assert.assertEquals(bound0.union(bound1), bound01);
+
+		for (int i = 1; i < mergedList.size(); i++) {
+			Assert.assertEquals(EntityType.Node, mergedList.get(i).getEntity().getType());
+		}
+	}
+	
+	/**
+	 * Tests the proper working of the merge task if both sources are
+	 * empty.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testBothEmpty() throws Exception {
+		RunnableSource source0 = new EmptyReader();
+		RunnableSource source1 = new EmptyReader();
+
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+		Assert.assertTrue("Expected empty result set but got some data", merged.getLastEntityContainer() == null);
+	}
+	
+	/**
+	 * Tests the proper working of the merge task if exactly one source is
+	 * empty with respect to the declared bound.
+	 * 
+	 * @throws Exception if something goes wrong
+	 */
+	@Test
+	public void testOneSourceEmpty() throws Exception {
+		RunnableSource source0 = new EmptyReader();
+
+		Bound bound1 = new Bound(5, 6, 8, 7, "source2");
+		RunnableSource source1 = new BoundSource(bound1, true);
+		
+		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
+				BoundRemovedAction.Ignore);
+		
+		SinkEntityInspector merged = RunTaskUtilities.run(merger, source0, source1);
+		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
+		
+		Assert.assertEquals(2, mergedList.size());
+		Assert.assertEquals(bound1, mergedList.get(0).getEntity());
+		Assert.assertEquals(EntityType.Node, mergedList.get(1).getEntity().getType());
+	}
+
+	private static <T> List<T> createList(Iterable<T> t) {
+		List<T> list = new ArrayList<T>();
+		for (T elem : t) {
+			list.add(elem);
+		}
+		return list;
+	}
+
+	/**
+	 * A simple source which provides a single node in the center of a given
+	 * bounding box, and optionally, a declared bounding box.
+	 */
+	private static class BoundSource implements RunnableSource {
+
+		private Sink sink;
+		private Bound bound;
+		private boolean publishBound;
+
+		public BoundSource(Bound bound, boolean publishBound) {
+			if (bound == null) {
+				throw new IllegalArgumentException("bound must not be null");
+			}
+			this.publishBound = publishBound;
+			this.bound = bound;
+		}
+
+		@Override
+		public void setSink(Sink sink) {
+			this.sink = sink;
+		}
+
+
+		@Override
+		public void run() {
+			try {
+				sink.initialize(Collections.<String, Object>emptyMap());
+				if (publishBound) {
+					sink.process(new BoundContainer(bound));
+				}
+				sink.process(new NodeContainer(createNode()));
+				sink.complete();
+			} finally {
+				sink.release();
+			}
+		}
+		
+		private Node createNode() {
+			double lon = (bound.getRight() - bound.getLeft()) / 2;
+			double lat = (bound.getTop() - bound.getBottom()) / 2;
+			return new Node(
+					new CommonEntityData(idGenerator.incrementAndGet(), 1, new Date(), OsmUser.NONE, 1),
+					lat, lon);
+		}
+	}
+}
diff --git a/set/src/test/resources/data/template/v0_6/append-change-in1.osc b/osmosis-set/src/test/resources/data/template/v0_6/append-change-in1.osc
similarity index 100%
rename from set/src/test/resources/data/template/v0_6/append-change-in1.osc
rename to osmosis-set/src/test/resources/data/template/v0_6/append-change-in1.osc
diff --git a/set/src/test/resources/data/template/v0_6/append-change-in2.osc b/osmosis-set/src/test/resources/data/template/v0_6/append-change-in2.osc
similarity index 100%
rename from set/src/test/resources/data/template/v0_6/append-change-in2.osc
rename to osmosis-set/src/test/resources/data/template/v0_6/append-change-in2.osc
diff --git a/set/src/test/resources/data/template/v0_6/append-change-out.osc b/osmosis-set/src/test/resources/data/template/v0_6/append-change-out.osc
similarity index 100%
rename from set/src/test/resources/data/template/v0_6/append-change-out.osc
rename to osmosis-set/src/test/resources/data/template/v0_6/append-change-out.osc
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-high.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-high.osm
new file mode 100644
index 0000000..bf38718
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-high.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1000" timestamp="2009-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1000" timestamp="2009-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1000" timestamp="2009-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-node-only.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-node-only.osm
new file mode 100644
index 0000000..49691f9
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base-node-only.osm
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base.osm
new file mode 100644
index 0000000..9af3c60
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-base.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-big.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-big.osm
new file mode 100644
index 0000000..ada8a00
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-big.osm
@@ -0,0 +1,24 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="3" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="4" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-7" lon="-8">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="5" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-9" lon="-10">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="6" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-11" lon="-12">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="7" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-13" lon="-14">
+    <tag k="created_by" v="Me1"/>
+  </node>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-create.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-create.osm
new file mode 100644
index 0000000..15e58ac
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-create.osm
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-delete.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-delete.osm
new file mode 100644
index 0000000..9999cc6
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-delete.osm
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-higher.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-higher.osm
new file mode 100644
index 0000000..1ab5087
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-higher.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1000" timestamp="2009-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1000" timestamp="2009-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-nonexistent.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-nonexistent.osm
new file mode 100644
index 0000000..c40d206
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify-nonexistent.osm
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify.osm b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify.osm
new file mode 100644
index 0000000..fab8dda
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/apply-change-modify.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-create.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-create.osc
new file mode 100644
index 0000000..a39bb93
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-create.osc
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="3" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="4" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-7" lon="-8">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="5" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-9" lon="-10">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="6" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-11" lon="-12">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="7" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-13" lon="-14">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </create>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-delete.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-delete.osc
new file mode 100644
index 0000000..67923b7
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-big-delete.osc
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <delete>
+    <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="3" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="4" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-7" lon="-8">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="5" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-9" lon="-10">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="6" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-11" lon="-12">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <node id="7" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-13" lon="-14">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </delete>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-existent.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-existent.osc
new file mode 100644
index 0000000..800ce56
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-existent.osc
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </create>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-modify-delete.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-modify-delete.osc
new file mode 100644
index 0000000..2f36240
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create-modify-delete.osc
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </create>
+  <modify>
+    <node id="2" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </modify>
+  <delete>
+    <node id="2" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </delete>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create.osc
new file mode 100644
index 0000000..02be3a1
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-create.osc
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </create>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete-nonexistent.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete-nonexistent.osc
new file mode 100644
index 0000000..cadec34
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete-nonexistent.osc
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <delete>
+    <node id="2000" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    </node>
+  </delete>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete.osc
new file mode 100644
index 0000000..aced155
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-delete.osc
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <delete>
+    <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    </node>
+  </delete>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify-nonexistent.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify-nonexistent.osc
new file mode 100644
index 0000000..dbe6614
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify-nonexistent.osc
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <modify>
+    <node id="2" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </modify>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify.osc b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify.osc
new file mode 100644
index 0000000..f27f771
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/apply_change/change-modify.osc
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <modify>
+    <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+      <tag k="created_by" v="Me1"/>
+    </node>
+  </modify>
+</osmChange>
\ No newline at end of file
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-in.osc b/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-in.osc
new file mode 100644
index 0000000..12791d6
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-in.osc
@@ -0,0 +1,45 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <nd ref="1"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <member type="node" ref="1" role="noderole"/>
+      <member type="way" ref="1" role="wayrole"/>
+      <tag k="created_by" v="Me1"/>
+    </relation>
+  </create>
+  <modify>
+    <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <way id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <nd ref="1"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <relation id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <member type="node" ref="1" role="noderole"/>
+      <member type="way" ref="1" role="wayrole"/>
+      <tag k="created_by" v="Me1"/>
+    </relation>
+  </modify>
+  <delete>
+    <node id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <way id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <nd ref="1"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <relation id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <member type="node" ref="1" role="noderole"/>
+      <member type="way" ref="1" role="wayrole"/>
+      <tag k="created_by" v="Me1"/>
+    </relation>
+  </delete>
+</osmChange>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-out.osm b/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-out.osm
new file mode 100644
index 0000000..87d39f6
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/change-to-full-history-out.osm
@@ -0,0 +1,39 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2" visible="true">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="true">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="true">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+  <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2" visible="true">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="true">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="true">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+  <node id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2" visible="false">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="false">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" visible="false">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/derive_change/full-create.osc b/osmosis-set/src/test/resources/data/template/v0_6/derive_change/full-create.osc
new file mode 100644
index 0000000..b92e021
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/derive_change/full-create.osc
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="10" version="34" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+      <tag k="created_by" v="Me1"/>
+    </node>
+    <way id="100" version="56" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <nd ref="1"/>
+      <tag k="created_by" v="Me1"/>
+    </way>
+    <relation id="1000" version="78" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+      <member type="node" ref="1" role="noderole"/>
+      <member type="way" ref="1" role="wayrole"/>
+      <tag k="created_by" v="Me1"/>
+    </relation>
+  </create>
+</osmChange>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/derive_change/simple.osm b/osmosis-set/src/test/resources/data/template/v0_6/derive_change/simple.osm
new file mode 100644
index 0000000..de74332
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/derive_change/simple.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="10" version="34" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="100" version="56" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1000" version="78" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/empty-change.osc b/osmosis-set/src/test/resources/data/template/v0_6/empty-change.osc
new file mode 100644
index 0000000..da71f33
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/empty-change.osc
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+</osmChange>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/empty-entity.osm b/osmosis-set/src/test/resources/data/template/v0_6/empty-entity.osm
new file mode 100644
index 0000000..df4ee0f
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/empty-entity.osm
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/flatten-in.osm b/osmosis-set/src/test/resources/data/template/v0_6/flatten-in.osm
new file mode 100644
index 0000000..ba689ca
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/flatten-in.osm
@@ -0,0 +1,39 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me2"/>
+  </way>
+  <way id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me3"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+  <relation id="1" version="2" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me2"/>
+  </relation>
+  <relation id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me3"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/flatten-out.osm b/osmosis-set/src/test/resources/data/template/v0_6/flatten-out.osm
new file mode 100644
index 0000000..3b22caf
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/flatten-out.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-5" lon="-6">
+    <tag k="created_by" v="Me3"/>
+  </node>
+  <way id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me3"/>
+  </way>
+  <relation id="1" version="3" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me3"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-1.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-1.osm
new file mode 100644
index 0000000..9af3c60
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-1.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-disjunct.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-disjunct.osm
new file mode 100644
index 0000000..c663ce3
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-disjunct.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="2"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="2" role="noderole"/>
+    <member type="way" ref="2" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-secondSource.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-secondSource.osm
new file mode 100644
index 0000000..347ae08
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-secondSource.osm
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:06Z" uid="10" user="user10" lat="-11" lon="-22">
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:04Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </way>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-timestamp.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-timestamp.osm
new file mode 100644
index 0000000..347ae08
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-timestamp.osm
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:06Z" uid="10" user="user10" lat="-11" lon="-22">
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:04Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </way>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-version.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-version.osm
new file mode 100644
index 0000000..92000b2
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-2-version.osm
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="0" timestamp="2008-01-02T03:04:06Z" uid="10" user="user10" lat="-11" lon="-22">
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </node>
+  <way id="1" version="3" timestamp="2008-01-02T03:04:04Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </way>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-id.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-id.osm
new file mode 100644
index 0000000..8577d87
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-id.osm
@@ -0,0 +1,21 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="4" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="3" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-type.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-type.osm
new file mode 100644
index 0000000..0902f47
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-in-badorder-type.osm
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-disjunct.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-disjunct.osm
new file mode 100644
index 0000000..87099c9
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-disjunct.osm
@@ -0,0 +1,27 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <node id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="2"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+  <relation id="2" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="2" role="noderole"/>
+    <member type="way" ref="2" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-secondSource.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-secondSource.osm
new file mode 100644
index 0000000..1979e2a
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-secondSource.osm
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:06Z" uid="10" user="user10" lat="-11" lon="-22">
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:04Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-timestamp.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-timestamp.osm
new file mode 100644
index 0000000..7c377e4
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-timestamp.osm
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:06Z" uid="10" user="user10" lat="-11" lon="-22">
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </node>
+  <way id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-version.osm b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-version.osm
new file mode 100644
index 0000000..623409c
--- /dev/null
+++ b/osmosis-set/src/test/resources/data/template/v0_6/merge/merge-out-version.osm
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="created_by" v="Me1"/>
+  </node>
+  <way id="1" version="3" timestamp="2008-01-02T03:04:04Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="merge_source" v="second"/>
+  </way>
+  <relation id="1" version="1" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="1" role="noderole"/>
+    <member type="way" ref="1" role="wayrole"/>
+    <tag k="created_by" v="Me1"/>
+  </relation>
+</osm>
diff --git a/set/src/test/resources/data/template/v0_6/simplify-change-in.osc b/osmosis-set/src/test/resources/data/template/v0_6/simplify-change-in.osc
similarity index 100%
rename from set/src/test/resources/data/template/v0_6/simplify-change-in.osc
rename to osmosis-set/src/test/resources/data/template/v0_6/simplify-change-in.osc
diff --git a/set/src/test/resources/data/template/v0_6/simplify-change-out.osc b/osmosis-set/src/test/resources/data/template/v0_6/simplify-change-out.osc
similarity index 100%
rename from set/src/test/resources/data/template/v0_6/simplify-change-out.osc
rename to osmosis-set/src/test/resources/data/template/v0_6/simplify-change-out.osc
diff --git a/osmosis-tagfilter/.checkstyle b/osmosis-tagfilter/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-tagfilter/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-tagfilter/.gitignore b/osmosis-tagfilter/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-tagfilter/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-tagfilter/build.gradle b/osmosis-tagfilter/build.gradle
new file mode 100644
index 0000000..f6c7b53
--- /dev/null
+++ b/osmosis-tagfilter/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    testCompile project(':osmosis-testutil')
+    testCompile project(':osmosis-xml')
+}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/TagFilterPluginLoader.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/TagFilterPluginLoader.java
similarity index 100%
rename from tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/TagFilterPluginLoader.java
rename to osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/TagFilterPluginLoader.java
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReader.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReader.java
new file mode 100644
index 0000000..fe149e4
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReader.java
@@ -0,0 +1,89 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.common;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Reads the content of a file containing "key.value" tags.
+ * 
+ * <p>
+ * The content of the file contains one key.value pair per line. Example:<br/>
+ * 
+ * <code>
+ * railway.tram
+ * railway.tram_stop
+ * </code>
+ * 
+ * @author Raluca Martinescu
+ */
+public class KeyValueFileReader {
+
+	/**
+	 * Our logger for debug and error -output.
+	 */
+	private static final Logger LOG = Logger.getLogger(KeyValueFileReader.class.getName());
+
+	/**
+	 * Where we read from.
+	 */
+	private BufferedReader reader;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param keyValueFile
+	 *            The file to read key.value tags from.
+	 * @throws FileNotFoundException
+	 *             in case the specified file does not exist
+	 */
+	public KeyValueFileReader(final File keyValueFile) throws FileNotFoundException {
+		this.reader = new BufferedReader(new FileReader(keyValueFile));
+	}
+
+
+	/**
+	 * Reads the file and returns an array of key.value tags.
+	 * 
+	 * @return an array of key.value tags
+	 * @throws IOException
+	 *             in case the file could not be read
+	 */
+	public String[] loadKeyValues() throws IOException {
+		List<String> result = new LinkedList<String>();
+		try {
+			String line;
+			while ((line = reader.readLine()) != null) {
+				result.add(line);
+			}
+		} finally {
+			cleanup();
+		}
+		return result.toArray(new String[0]);
+	}
+
+
+	/**
+	 * Releases any resources remaining open.
+	 */
+	private void cleanup() {
+		if (reader != null) {
+			try {
+				reader.close();
+			} catch (Exception e) {
+				LOG.log(Level.SEVERE, "Unable to close file reader.", e);
+			} finally {
+				reader = null;
+			}
+		}
+	}
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java
new file mode 100644
index 0000000..d658eff
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java
@@ -0,0 +1,131 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.HashSet;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * A class filtering everything but allowed nodes.
+ *
+ * @author Aurelien Jacobs
+ */
+public class NodeKeyFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private HashSet<String> allowedKeys;
+
+	/**
+	 * Creates a new instance.
+	 *
+	 * @param keyList
+	 *            Comma-separated list of allowed key,
+	 *            e.g. "place,amenity"
+	 */
+	public NodeKeyFilter(String keyList) {
+
+		allowedKeys = new HashSet<String>();
+		String[] keys = keyList.split(",");
+		for (int i = 0; i < keys.length; i++) {
+			allowedKeys.add(keys[i]);
+		}
+
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		Node node = container.getEntity();
+
+		boolean matchesFilter = false;
+		for (Tag tag : node.getTags()) {
+			if (allowedKeys.contains(tag.getKey())) {
+				matchesFilter = true;
+				break;
+			}
+		}
+
+		if (matchesFilter) {
+			sink.process(container);
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilterFactory.java
similarity index 100%
rename from tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilterFactory.java
rename to osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilterFactory.java
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java
new file mode 100644
index 0000000..37530da
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java
@@ -0,0 +1,165 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+import org.openstreetmap.osmosis.tagfilter.common.KeyValueFileReader;
+
+
+/**
+ * A class filtering everything but allowed nodes.
+ *
+ * @author Aurelien Jacobs
+ */
+public class NodeKeyValueFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private HashSet<String> allowedKeyValues;
+
+	/**
+	 * Creates a new instance.
+	 *
+	 * @param keyValueList
+	 *            Comma-separated list of allowed key-value combinations,
+	 *            e.g. "place.city,place.town"
+	 */
+	public NodeKeyValueFilter(String keyValueList) {
+
+		allowedKeyValues = new HashSet<String>();
+		String[] keyValues = keyValueList.split(",");
+		for (int i = 0; i < keyValues.length; i++) {
+			allowedKeyValues.add(keyValues[i]);
+		}
+
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param keyValueListFile
+	 *            File containing one key-value combination per line
+	 */
+	public NodeKeyValueFilter(File keyValueListFile) {
+		
+		String[] keyValues;
+		try {
+			KeyValueFileReader reader = new KeyValueFileReader(keyValueListFile);
+			keyValues = reader.loadKeyValues();
+		} catch (FileNotFoundException ex) {
+			throw new OsmosisRuntimeException("Unable to find key.value file " + keyValueListFile.getAbsolutePath()
+					+ ".", ex);
+		} catch (IOException ex) {
+			throw new OsmosisRuntimeException("Unable to read from key.value file "
+					+ keyValueListFile.getAbsolutePath() + ".", ex);
+		}
+
+		allowedKeyValues = new HashSet<String>();
+		for (int i = 0; i < keyValues.length; i++) {
+			allowedKeyValues.add(keyValues[i]);
+		}
+
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		Node node = container.getEntity();
+
+		boolean matchesFilter = false;
+		for (Tag tag : node.getTags()) {
+			String keyValue = tag.getKey() + "." + tag.getValue();
+			if (allowedKeyValues.contains(keyValue)) {
+				matchesFilter = true;
+				break;
+			}
+		}
+
+		if (matchesFilter) {
+			sink.process(container);
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		// Do nothing.
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java
new file mode 100644
index 0000000..a4285db
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java
@@ -0,0 +1,44 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+
+/**
+ * Extends the basic task manager factory functionality with used-node filter task
+ * specific common methods.
+ *
+ * @author Brett Henderson
+ * @author Christoph Sommer
+ */
+public class NodeKeyValueFilterFactory extends TaskManagerFactory {
+    private static final String ARG_KEY_VALUE_LIST = "keyValueList";
+    private static final String ARG_KEY_VALUE_LIST_FILE = "keyValueListFile";
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		NodeKeyValueFilter nodeKeyValueFilter;
+
+		if (doesArgumentExist(taskConfig, ARG_KEY_VALUE_LIST)) {
+			String keyValueList = getStringArgument(taskConfig, ARG_KEY_VALUE_LIST);
+			nodeKeyValueFilter = new NodeKeyValueFilter(keyValueList);
+		} else {
+			String keyValueListFile = getStringArgument(taskConfig, ARG_KEY_VALUE_LIST_FILE);
+			nodeKeyValueFilter = new NodeKeyValueFilter(new File(keyValueListFile));
+		}
+		
+		return new SinkSourceManager(
+			taskConfig.getId(),
+			nodeKeyValueFilter,
+			taskConfig.getPipeArgs()
+		);
+	}
+
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java
new file mode 100644
index 0000000..d6e52c1
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java
@@ -0,0 +1,168 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+/**
+ * A simple class to filter node, way, and relation entities by their tag keys and/or values.
+ * 
+ * @author Andrew Byrd
+ */
+public class TagFilter implements SinkSource {
+    private Sink sink;
+    private Set<String> tagKeys;
+    private Map<String, Set<String>> tagKeyValues;
+    private Class<? extends EntityContainer> filterClass;
+    private boolean reject;
+    private boolean matchesEverything;
+    private static final Logger LOG = Logger.getLogger(TagFilter.class.getName());
+    
+
+    /**
+     * Creates a new instance. 
+     *
+     * @param filterMode
+     *          A 2-field dash-separated string specifying:
+     *          1. Whether the filter accepts or rejects entities
+     *          2. The entity type upon which it operates
+     *
+     * @param tagKeys
+     *          A Set of tag key Strings. The filter will match these tags irrespective of tag values.
+     *
+     * @param tagKeyValues
+     *          A map of tag key Strings to Sets of tag key values. These key-value pairs are checked 
+     *          against each entity's tags to determine whether or not it matches the filter.
+     */
+    public TagFilter(String filterMode, Set<String> tagKeys, Map<String, Set<String>> tagKeyValues) {        
+        String[] filterModeSplit = filterMode.toLowerCase().split("-");
+        if (filterModeSplit.length != 2) { 
+            throw new OsmosisRuntimeException(
+            "The TagFilter task's default parameter must consist of an action and an entity type separated by '-'."); 
+        }
+
+        String action = filterModeSplit[0];
+        if (action.equals("accept")) { 
+            reject = false; 
+        } else if (action.equals("reject")) { 
+            reject = true;  
+        } else { 
+            throw new OsmosisRuntimeException(
+            "The TagFilter action must be either 'accept' or 'reject'. '" + action + "' is not a supported mode."); 
+        }
+
+        String entity = filterModeSplit[1];
+        if  (entity.endsWith("s")) { 
+            entity = entity.substring(0, entity.length() - 1); 
+        }
+        if  (entity.equals("node")) { 
+            filterClass = NodeContainer.class; 
+        } else if (entity.equals("way")) { 
+            filterClass = WayContainer.class;      
+        } else if (entity.equals("relation")) { 
+            filterClass = RelationContainer.class; 
+        } else { 
+            throw new OsmosisRuntimeException(
+            "The TagFilter entity type must be one of 'node', 'way', or 'relation'. '" + entity 
+            + "' is not a supported entity type."); 
+        }
+        
+        matchesEverything = (tagKeys.size() == 0 && tagKeyValues.size() == 0);
+        this.tagKeys = tagKeys;
+        this.tagKeyValues = tagKeyValues;
+        
+        String logString = "New TagFilter ";
+        if (reject) {
+            logString += "rejects ";
+        } else {
+            logString += "accepts ";
+        }    
+        logString += filterClass;
+        if (matchesEverything) {
+            logString += " (no tag-based filtering).";
+        } else {
+            logString += " having tag keys " + tagKeys + " or tag key-value pairs " + tagKeyValues + ".";
+        }
+        LOG.finer(logString);
+    }
+
+
+    /**
+     * Checks whether the Entity in a container has tags that match this filter.
+     *
+     * @param container
+     *      The container holding the entity whose tags shall be examined.
+     */
+    private boolean matches(EntityContainer container) {
+        boolean matched = false;
+        for (Tag tag : container.getEntity().getTags()) {
+            String key = tag.getKey();
+            if (tagKeys.contains(key)) {
+                matched = true;
+                break; 
+            }
+            Set<String> values = tagKeyValues.get(key);
+            if ((values != null) && values.contains(tag.getValue())) {
+                matched = true;
+                break; 
+            } 
+        }
+        return matched;    
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void process(EntityContainer container) {
+        if (filterClass.isInstance(container)) {
+            if (reject ^ (matchesEverything || matches(container))) {
+                sink.process(container);
+            }
+        } else {
+            sink.process(container);
+        }
+    }
+        
+
+    /**
+     * {@inheritDoc}
+     */
+    public void complete() {
+        sink.complete();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void release() {
+        sink.release();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setSink(Sink sink) {
+        this.sink = sink;
+    }
+}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterFactory.java
similarity index 100%
rename from tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterFactory.java
rename to osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterFactory.java
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java
new file mode 100644
index 0000000..c00ba52
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java
@@ -0,0 +1,111 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Filters a set of tags from all entities. This allows unwanted tags to be
+ * removed from the data.
+ * 
+ * @author Jochen Topf
+ * @author Brett Henderson
+ */
+public class TagRemover implements SinkSource {
+	private Sink sink;
+	private HashSet<String> keysToDrop;
+	private String[] keyPrefixesToDrop;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param keyList
+	 *            Comma separated list of keys of tags to be removed.
+	 * @param keyPrefixList
+	 *            Comma separated list of key prefixes of tags to be removed.
+	 */
+	public TagRemover(String keyList, String keyPrefixList) {
+		keysToDrop = new HashSet<String>();
+		String[] keys = keyList.split(",");
+		for (int i = 0; i < keys.length; i++) {
+			keysToDrop.add(keys[i]);
+		}
+		keyPrefixesToDrop = keyPrefixList.split(",");
+		if (keyPrefixesToDrop[0] == "") {
+			keyPrefixesToDrop = new String[] {};
+		}
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void process(EntityContainer entityContainer) {
+		EntityContainer writeableContainer;
+		Entity entity;
+		
+		writeableContainer = entityContainer.getWriteableInstance();
+		entity = writeableContainer.getEntity();
+		
+		for (Iterator<Tag> i = entity.getTags().iterator(); i.hasNext();) {
+			Tag tag;
+			
+			tag = i.next();
+			
+			if (keysToDrop.contains(tag.getKey())) {
+				i.remove();
+			} else {
+				for (String prefix : keyPrefixesToDrop) {
+					if (tag.getKey().startsWith(prefix)) {
+						i.remove();
+					   	break;
+					}
+				}
+			}
+		}
+		
+		sink.process(writeableContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverFactory.java
similarity index 100%
rename from tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverFactory.java
rename to osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverFactory.java
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java
new file mode 100644
index 0000000..ae73db7
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java
@@ -0,0 +1,195 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.filter.common.IdTracker;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Restricts output of nodes to those that are used in ways and relations.
+ * 
+ * @author Brett Henderson
+ * @author Karl Newman
+ * @author Christoph Sommer 
+ * @author Bartosz Fabianowski
+ */
+public class UsedNodeFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private SimpleObjectStore<NodeContainer> allNodes;
+	private SimpleObjectStore<WayContainer> allWays;
+	private SimpleObjectStore<RelationContainer> allRelations;
+	private IdTracker requiredNodes;
+	
+	
+	/**
+	 * Creates a new instance.
+	 *
+	 * @param idTrackerType
+	 *            Defines the id tracker implementation to use.
+	 */
+	public UsedNodeFilter(IdTrackerType idTrackerType) {
+		allNodes = new SimpleObjectStore<NodeContainer>(
+				new SingleClassObjectSerializationFactory(NodeContainer.class), "afnd", true);
+		allWays = new SimpleObjectStore<WayContainer>(
+				new SingleClassObjectSerializationFactory(WayContainer.class), "afwy", true);
+		allRelations = new SimpleObjectStore<RelationContainer>(
+				new SingleClassObjectSerializationFactory(RelationContainer.class), "afrl", true);
+
+		requiredNodes = IdTrackerFactory.createInstance(idTrackerType);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		allNodes.add(container);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		Way way;
+
+		// mark all nodes as required		
+		way = container.getEntity();
+		for (WayNode nodeReference : way.getWayNodes()) {
+			long nodeId = nodeReference.getNodeId();
+			requiredNodes.set(nodeId);
+		}
+
+		allWays.add(container);
+
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		Relation relation;
+
+		// mark all nodes as required
+		relation = container.getEntity();
+		for (RelationMember memberReference : relation.getMembers()) {
+			if (memberReference.getMemberType() == EntityType.Node) {
+				long nodeId = memberReference.getMemberId();
+				requiredNodes.set(nodeId);
+			}
+		}
+
+		allRelations.add(container);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+
+		// send on all required nodes
+		ReleasableIterator<NodeContainer> nodeIterator = allNodes.iterate();
+		while (nodeIterator.hasNext()) {
+			NodeContainer nodeContainer = nodeIterator.next();
+			long nodeId = nodeContainer.getEntity().getId();
+			if (!requiredNodes.get(nodeId)) {
+				continue;
+			}
+			sink.process(nodeContainer);
+		}
+		nodeIterator.release();
+		nodeIterator = null;
+
+		// send on all ways
+		ReleasableIterator<WayContainer> wayIterator = allWays.iterate();
+		while (wayIterator.hasNext()) {
+			sink.process(wayIterator.next());
+		}
+		wayIterator.release();
+		wayIterator = null;
+
+		// send on all relations
+		ReleasableIterator<RelationContainer> relationIterator = allRelations.iterate();
+		while (relationIterator.hasNext()) {
+			sink.process(relationIterator.next());
+		}
+		relationIterator.release();
+		relationIterator = null;
+
+		// done
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		if (allNodes != null) {
+			allNodes.release();
+		}
+		if (allWays != null) {
+			allWays.release();			
+		}
+		if (allRelations != null) {
+			allRelations.release();
+		}
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java
new file mode 100644
index 0000000..c179673
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java
@@ -0,0 +1,51 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+
+/**
+ * Extends the basic task manager factory functionality with used-node filter task
+ * specific common methods.
+ * 
+ * @author Brett Henderson
+ * @author Christoph Sommer
+ */
+public class UsedNodeFilterFactory extends TaskManagerFactory {
+	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.Dynamic;
+	
+	
+	/**
+	 * Utility method that returns the IdTrackerType to use for a given taskConfig.
+	 * 
+	 * @param taskConfig
+	 *            Contains all information required to instantiate and configure
+	 *            the task.
+	 * @return The entity identifier tracker type.
+	 */
+	protected IdTrackerType getIdTrackerType(
+			TaskConfiguration taskConfig) {
+		
+		return DEFAULT_ID_TRACKER_TYPE;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+
+		IdTrackerType idTrackerType = getIdTrackerType(taskConfig);
+
+		return new SinkSourceManager(
+			taskConfig.getId(),
+			new UsedNodeFilter(idTrackerType),
+			taskConfig.getPipeArgs()
+		);
+	}
+
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java
new file mode 100644
index 0000000..01bb2a7
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java
@@ -0,0 +1,182 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.filter.common.IdTracker;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
+import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
+import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * Restricts output of ways to those that are used in relations.
+ * 
+ * @author Brett Henderson
+ * @author Karl Newman
+ * @author Christoph Sommer 
+ * @author Bartosz Fabianowski
+ */
+public class UsedWayFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private SimpleObjectStore<NodeContainer> allNodes;
+	private SimpleObjectStore<WayContainer> allWays;
+	private SimpleObjectStore<RelationContainer> allRelations;
+	private IdTracker requiredWays;
+	
+	
+	/**
+	 * Creates a new instance.
+	 *
+	 * @param idTrackerType
+	 *            Defines the id tracker implementation to use.
+	 */
+	public UsedWayFilter(IdTrackerType idTrackerType) {
+		allNodes = new SimpleObjectStore<NodeContainer>(
+				new SingleClassObjectSerializationFactory(NodeContainer.class), "afnd", true);
+		allWays = new SimpleObjectStore<WayContainer>(
+				new SingleClassObjectSerializationFactory(WayContainer.class), "afwy", true);
+		allRelations = new SimpleObjectStore<RelationContainer>(
+				new SingleClassObjectSerializationFactory(RelationContainer.class), "afrl", true);
+
+		requiredWays = IdTrackerFactory.createInstance(idTrackerType);
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		allNodes.add(container);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		allWays.add(container);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		Relation relation;
+
+		// mark all nodes as required
+		relation = container.getEntity();
+		for (RelationMember memberReference : relation.getMembers()) {
+			if (memberReference.getMemberType() == EntityType.Way) {
+				long wayId = memberReference.getMemberId();
+				requiredWays.set(wayId);
+			}
+		}
+
+		allRelations.add(container);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+    // send on all nodes
+    ReleasableIterator<NodeContainer> nodeIterator = allNodes.iterate();
+    while (nodeIterator.hasNext()) {
+      sink.process(nodeIterator.next());
+    }
+    nodeIterator.release();
+    nodeIterator = null;
+
+		// send on all required ways
+		ReleasableIterator<WayContainer> wayIterator = allWays.iterate();
+		while (wayIterator.hasNext()) {
+			WayContainer wayContainer = wayIterator.next();
+			long wayId = wayContainer.getEntity().getId();
+			if (!requiredWays.get(wayId)) {
+				continue;
+			}
+			sink.process(wayContainer);
+		}
+		wayIterator.release();
+		wayIterator = null;
+
+		// send on all relations
+		ReleasableIterator<RelationContainer> relationIterator = allRelations.iterate();
+		while (relationIterator.hasNext()) {
+			sink.process(relationIterator.next());
+		}
+		relationIterator.release();
+		relationIterator = null;
+
+		// done
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		if (allNodes != null) {
+			allNodes.release();
+		}
+		if (allWays != null) {
+			allWays.release();			
+		}
+		if (allRelations != null) {
+			allRelations.release();
+		}
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java
new file mode 100644
index 0000000..efa9184
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java
@@ -0,0 +1,50 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+/**
+ * Extends the basic task manager factory functionality with used-way filter task
+ * specific common methods.
+ * 
+ * @author Brett Henderson
+ * @author Christoph Sommer
+ * @author Bartosz Fabianowski
+ */
+public class UsedWayFilterFactory extends TaskManagerFactory {
+	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.Dynamic;
+	
+	
+	/**
+	 * Utility method that returns the IdTrackerType to use for a given taskConfig.
+	 * 
+	 * @param taskConfig
+	 *            Contains all information required to instantiate and configure
+	 *            the task.
+	 * @return The entity identifier tracker type.
+	 */
+	protected IdTrackerType getIdTrackerType(
+			TaskConfiguration taskConfig) {
+		return DEFAULT_ID_TRACKER_TYPE;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+
+		IdTrackerType idTrackerType = getIdTrackerType(taskConfig);
+
+		return new SinkSourceManager(
+			taskConfig.getId(),
+			new UsedWayFilter(idTrackerType),
+			taskConfig.getPipeArgs()
+		);
+	}
+
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java
new file mode 100644
index 0000000..4581007
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java
@@ -0,0 +1,132 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.util.HashSet;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+
+
+/**
+ * A simple class to filter way entities by their tag keys.
+ * Based on work by Brett Henderson, Karl Newman, Christoph Sommer, Aurelien Jacobs
+ * 
+ * @author Andrew Byrd
+ */
+public class WayKeyFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private HashSet<String> allowedKeys;
+
+	/**
+	 * Creates a new instance.
+	 *
+	 * @param keyList
+	 *            Comma-separated list of allowed keys,
+	 *            e.g. "highway,place"
+	 */
+	public WayKeyFilter(String keyList) {
+
+		allowedKeys = new HashSet<String>();
+		String[] keys = keyList.split(",");
+		for (int i = 0; i < keys.length; i++) {
+			allowedKeys.add(keys[i]);
+		}
+
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		sink.process(container);
+	}
+	
+    
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		Way way = container.getEntity();
+
+		boolean matchesFilter = false;
+		for (Tag tag : way.getTags()) {
+			if (allowedKeys.contains(tag.getKey())) {
+				matchesFilter = true;
+				break;
+			}
+		}
+
+		if (matchesFilter) {
+			sink.process(container);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		sink.process(container);
+	}
+    
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilterFactory.java
similarity index 100%
rename from tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilterFactory.java
rename to osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilterFactory.java
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java
new file mode 100644
index 0000000..5a01381
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java
@@ -0,0 +1,167 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+import org.openstreetmap.osmosis.tagfilter.common.KeyValueFileReader;
+
+
+/**
+ * A simple class to filter way entities by their tags.
+ * 
+ * @author Brett Henderson
+ * @author Karl Newman
+ * @author Christoph Sommer 
+ */
+public class WayKeyValueFilter implements SinkSource, EntityProcessor {
+	private Sink sink;
+	private HashSet<String> allowedKeyValues;
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param keyValueList
+	 *            Comma-separated list of allowed key-value combinations,
+	 *            e.g. "highway.motorway,highway.motorway_link" 
+	 */
+	public WayKeyValueFilter(String keyValueList) {
+
+		allowedKeyValues = new HashSet<String>();
+		String[] keyValues = keyValueList.split(",");
+		for (int i = 0; i < keyValues.length; i++) {
+			allowedKeyValues.add(keyValues[i]);
+		}
+
+	}
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param keyValueListFile
+	 *            File containing one key-value combination per line
+	 */
+	public WayKeyValueFilter(File keyValueListFile) {
+
+		String[] keyValues;
+		try {
+			KeyValueFileReader reader = new KeyValueFileReader(keyValueListFile);
+			keyValues = reader.loadKeyValues();
+		} catch (FileNotFoundException ex) {
+			throw new OsmosisRuntimeException("Unable to find key.value file " + keyValueListFile.getAbsolutePath()
+					+ ".", ex);
+		} catch (IOException ex) {
+			throw new OsmosisRuntimeException("Unable to read from key.value file "
+					+ keyValueListFile.getAbsolutePath() + ".", ex);
+		}
+
+		allowedKeyValues = new HashSet<String>();
+		for (int i = 0; i < keyValues.length; i++) {
+			allowedKeyValues.add(keyValues[i]);
+		}
+
+	}
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		// Ask the entity container to invoke the appropriate processing method
+		// for the entity type.
+		entityContainer.process(this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(BoundContainer boundContainer) {
+		// By default, pass it on unchanged
+		sink.process(boundContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(NodeContainer container) {
+		sink.process(container);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(WayContainer container) {
+		Way way = container.getEntity();
+
+		boolean matchesFilter = false;
+		for (Tag tag : way.getTags()) {
+			String keyValue = tag.getKey() + "." + tag.getValue();
+			if (allowedKeyValues.contains(keyValue)) {
+				matchesFilter = true;
+				break;
+			}
+		}
+
+		if (matchesFilter) {
+			sink.process(container);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(RelationContainer container) {
+		sink.process(container);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void complete() {
+		sink.complete();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void release() {
+		sink.release();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+}
diff --git a/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java
new file mode 100644
index 0000000..31db0bb
--- /dev/null
+++ b/osmosis-tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java
@@ -0,0 +1,50 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+
+/**
+ * Extends the basic task manager factory functionality with used-node filter task
+ * specific common methods.
+ * 
+ * @author Brett Henderson
+ * @author Christoph Sommer
+ */
+public class WayKeyValueFilterFactory extends TaskManagerFactory {
+	private static final String ARG_KEY_VALUE_LIST = "keyValueList";
+	private static final String ARG_KEY_VALUE_LIST_FILE = "keyValueListFile";
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		WayKeyValueFilter wayKeyValueFilter;
+		
+		if (doesArgumentExist(taskConfig, ARG_KEY_VALUE_LIST)) {
+			String keyValueList = getStringArgument(taskConfig, ARG_KEY_VALUE_LIST);
+			wayKeyValueFilter = new WayKeyValueFilter(keyValueList);
+		} else if (doesArgumentExist(taskConfig, ARG_KEY_VALUE_LIST_FILE)) {
+			String keyValueListFile = getStringArgument(taskConfig, ARG_KEY_VALUE_LIST_FILE);
+			wayKeyValueFilter = new WayKeyValueFilter(new File(keyValueListFile));
+		} else {
+			String keyValueList = getDefaultStringArgument(taskConfig,
+					"highway.motorway,highway.motorway_link,highway.trunk,highway.trunk_link");
+			wayKeyValueFilter = new WayKeyValueFilter(keyValueList);
+		}
+		
+		return new SinkSourceManager(
+			taskConfig.getId(),
+			wayKeyValueFilter,
+			taskConfig.getPipeArgs()
+		);
+	}
+
+}
diff --git a/tagfilter/src/main/resources/osmosis-plugins.conf b/osmosis-tagfilter/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from tagfilter/src/main/resources/osmosis-plugins.conf
rename to osmosis-tagfilter/src/main/resources/osmosis-plugins.conf
diff --git a/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReaderTest.java b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReaderTest.java
new file mode 100644
index 0000000..db33278
--- /dev/null
+++ b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/common/KeyValueFileReaderTest.java
@@ -0,0 +1,47 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.common;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests for the KeyValueFileReader class.
+ * 
+ * @author Raluca Martinescu
+ */
+public class KeyValueFileReaderTest extends AbstractDataTest {
+
+	/**
+	 * Tests that in case the file does not exist, the KeyValueFileReader
+	 * constructor throws an exception.
+	 * 
+	 * @throws FileNotFoundException
+	 *             when the test class encounters the non-existent file.
+	 */
+	@Test(expected = FileNotFoundException.class)
+	public final void testFileNotFound() throws FileNotFoundException {
+		File file = new File("non_existing_file.txt");
+		new KeyValueFileReader(file);
+	}
+
+
+	/**
+	 * Tests the reading of key-value pairs from the file.
+	 * 
+	 * @throws IOException
+	 *             if the allowed-key-values file cannot be found.
+	 */
+	@Test
+	public final void testLoadKeyValues() throws IOException {
+		File file = dataUtils.createDataFile("v0_6/allowed-key-values.txt");
+		String[] expected = {"box_type.lamp_box", "box_type.wall"};
+		String[] actual = new KeyValueFileReader(file).loadKeyValues();
+		Assert.assertArrayEquals(expected, actual);
+	}
+}
diff --git a/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterTest.java b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterTest.java
new file mode 100644
index 0000000..5fc1a65
--- /dev/null
+++ b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterTest.java
@@ -0,0 +1,69 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests for the NodeKeyValueFilter class.
+ * 
+ * @author Raluca Martinescu
+ */
+public class NodeKeyValueFilterTest extends AbstractDataTest {	
+
+	
+	/**
+	 * Tests the node key-value filter when allowed value pairs are read from
+	 * comma separated list of values.
+	 * 
+	 * @throws IOException
+	 *             if file manipulation fails.
+	 */
+	@Test
+	public final void testNodeKeyValueFilterFromList() throws IOException {
+		testNodeKeyValueFilter("keyValueList=box_type.lamp_box,box_type.wall");
+	}
+
+
+	/**
+	 * Tests the node key-value filter when allowed value pairs are read from
+	 * file.
+	 * 
+	 * @throws IOException
+	 *             if file manipulation fails.
+	 */
+	@Test
+	public final void testNodeKeyValueFilterFromFile() throws IOException {
+		File allowedPairs = dataUtils.createDataFile("v0_6/allowed-key-values.txt");
+		testNodeKeyValueFilter("keyValueListFile=" + allowedPairs.getPath());
+	}
+
+
+	private void testNodeKeyValueFilter(String keyValueListOption) throws IOException {
+
+		File inputFile = dataUtils.createDataFile("v0_6/node-key-value-filter-snapshot.osm");
+		File expectedResultFile = dataUtils.createDataFile("v0_6/node-key-value-filter-expected.osm");
+		File outputFile = dataUtils.newFile();
+		
+		// filter by key-value pairs
+		Osmosis.run(
+			new String [] {
+				"-q",					
+				"--read-xml-0.6",
+				inputFile.getPath(),
+				"--node-key-value",
+				keyValueListOption,
+				"--write-xml-0.6",
+				outputFile.getPath()
+			}
+		);
+					
+		// Validate that the output file matches the expected file
+		dataUtils.compareFiles(expectedResultFile, outputFile);
+	}
+}
diff --git a/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java
new file mode 100644
index 0000000..7ef5d93
--- /dev/null
+++ b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java
@@ -0,0 +1,168 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+/**
+ * Tests the TagFilter implementation.
+ * 
+ * @author Andrew Byrd
+ * Based on tests written by Karl Newman
+ */
+public class TagFilterTest {
+
+	private SinkEntityInspector entityInspector;
+	private TagFilter tagFilter;
+
+	private Node amenityNode;
+	private NodeContainer amenityNodeContainer;
+
+	private Node taglessNode;
+	private NodeContainer taglessNodeContainer;
+
+	private Way motorwayWay;
+	private WayContainer motorwayWayContainer;
+
+	private Way residentialWay;
+	private WayContainer residentialWayContainer;
+
+	private Relation testRelation;
+	private RelationContainer testRelationContainer;
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		OsmUser user;
+		List<Tag> tags;
+		
+		user = new OsmUser(12, "OsmosisTest");
+			
+		tags = Arrays.asList(new Tag("amenity", "bank"), new Tag("Akey", "Avalue"));
+		amenityNode = new Node(new CommonEntityData(1101, 0, new Date(), user, 0, tags), 1, 2);
+		amenityNodeContainer = new NodeContainer(amenityNode);
+
+		tags = new ArrayList<Tag>();
+		taglessNode = new Node(new CommonEntityData(1102, 0, new Date(), user, 0, tags), 3, 4);
+		taglessNodeContainer = new NodeContainer(taglessNode);
+
+		tags = Arrays.asList(new Tag("highway", "motorway"), new Tag("Bkey", "Bvalue"));
+		motorwayWay = new Way(new CommonEntityData(2201, 0, new Date(), user, 0, tags), new ArrayList<WayNode>());
+		motorwayWayContainer = new WayContainer(motorwayWay);
+
+		tags = Arrays.asList(new Tag("highway", "residential"), new Tag("Ckey", "Cvalue"));
+		residentialWay =
+			new Way(new CommonEntityData(2202, 0, new Date(), user, 0, tags), new ArrayList<WayNode>());
+		residentialWayContainer = new WayContainer(residentialWay);
+
+		tags = Arrays.asList(new Tag("Dkey", "Dvalue"));
+		testRelation =
+			new Relation(new CommonEntityData(3301, 0, new Date(), user, 0, tags), new ArrayList<RelationMember>());
+		testRelationContainer = new RelationContainer(testRelation);
+	}
+
+
+	/**
+	 * Performs post-test activities.
+	 */
+	@After
+	public void tearDown() {
+		// nothing to do here.
+	}
+
+
+	/**
+	 * Test passing a node which matches the filter.
+	 */
+	@Test
+	public final void testAcceptNode() {
+		Set<String> keys = new HashSet<String>(Arrays.asList("amenity"));
+		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
+		keyValues.put("key", new HashSet<String>(Arrays.asList("valone", "valtwo")));
+		tagFilter = new TagFilter("accept-nodes", keys, keyValues);
+		entityInspector = new SinkEntityInspector();
+		tagFilter.setSink(entityInspector);
+
+		tagFilter.process(amenityNodeContainer);
+		tagFilter.process(taglessNodeContainer);
+		tagFilter.process(residentialWayContainer);
+		tagFilter.complete();
+
+		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
+		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
+		tagFilter.release();
+	}
+
+
+	/**
+	 * Test rejecting a way which matches the filter.
+	 */
+	@Test
+	public final void testRejectWay() {
+		Set<String> keys = new HashSet<String>();
+		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
+		keyValues.put("highway", new HashSet<String>(Arrays.asList("motorway", "motorway_link")));
+		tagFilter = new TagFilter("reject-ways", keys, keyValues);
+		entityInspector = new SinkEntityInspector();
+		tagFilter.setSink(entityInspector);
+
+		tagFilter.process(amenityNodeContainer);
+		tagFilter.process(residentialWayContainer);
+		tagFilter.process(motorwayWayContainer);
+		tagFilter.complete();
+
+		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
+		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
+		tagFilter.release();
+	}
+
+
+	/**
+	 * Test rejecting a relation without tag-based filtering.
+	 */
+	@Test
+	public final void testRejectRelation() {
+		Set<String> keys = new HashSet<String>();
+		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
+		tagFilter = new TagFilter("reject-relations", keys, keyValues);
+		entityInspector = new SinkEntityInspector();
+		tagFilter.setSink(entityInspector);
+
+		tagFilter.process(amenityNodeContainer);
+		tagFilter.process(residentialWayContainer);
+		tagFilter.process(testRelationContainer);
+		tagFilter.complete();
+
+		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
+		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
+		tagFilter.release();
+	}
+
+}
diff --git a/tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverTest.java b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverTest.java
similarity index 100%
rename from tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverTest.java
rename to osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemoverTest.java
diff --git a/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterTest.java b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterTest.java
new file mode 100644
index 0000000..51e4160
--- /dev/null
+++ b/osmosis-tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterTest.java
@@ -0,0 +1,68 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagfilter.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests for the WayKeyValueFilter class.
+ * 
+ * @author Raluca Martinescu
+ */
+public class WayKeyValueFilterTest extends AbstractDataTest {
+
+	/**
+	 * Tests the way key-value filter when allowed value pairs are read from
+	 * comma separated list of values.
+	 * 
+	 * @throws IOException
+	 *             if file manipulation fails.
+	 */
+	@Test
+	public final void testWayKeyValueFilterFromList() throws IOException {
+		testWayKeyValueFilter("keyValueList=box_type.lamp_box,box_type.wall");
+	}
+
+
+	/**
+	 * Tests the way key-value filter when allowed value pairs are read from
+	 * file.
+	 * 
+	 * @throws IOException
+	 *             if file manipulation fails.
+	 */
+	@Test
+	public final void testWayKeyValueFilterFromFile() throws IOException {
+		File allowedPairs = dataUtils.createDataFile("v0_6/allowed-key-values.txt");
+		testWayKeyValueFilter("keyValueListFile=" + allowedPairs.getPath());
+	}
+
+
+	private void testWayKeyValueFilter(String keyValueListOption) throws IOException {
+
+		File inputFile = dataUtils.createDataFile("v0_6/way-key-value-filter-snapshot.osm");
+		File expectedResultFile = dataUtils.createDataFile("v0_6/way-key-value-filter-expected.osm");
+		File outputFile = dataUtils.newFile();
+		
+		// filter by key-value pairs
+		Osmosis.run(
+			new String [] {
+				"-q",					
+				"--read-xml-0.6",
+				inputFile.getPath(),
+				"--way-key-value",
+				keyValueListOption,
+				"--write-xml-0.6",
+				outputFile.getPath()
+			}
+		);
+					
+		// Validate that the output file matches the expected file
+		dataUtils.compareFiles(expectedResultFile, outputFile);
+	}
+}
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/allowed-key-values.txt b/osmosis-tagfilter/src/test/resources/data/template/v0_6/allowed-key-values.txt
new file mode 100644
index 0000000..dd2a72d
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/allowed-key-values.txt
@@ -0,0 +1,2 @@
+box_type.lamp_box
+box_type.wall
\ No newline at end of file
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-expected.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-expected.osm
new file mode 100644
index 0000000..d1ef095
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-expected.osm
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="19" version="2" timestamp="2010-07-07T07:51:18Z" uid="10" user="user1" lat="51.9458753" lon="-0.20698">
+    <tag k="amenity" v="post_box"/>
+    <tag k="box_type" v="lamp_box"/>
+    <tag k="last_collection_mf" v="1630"/>
+    <tag k="last_collection_sat" v="0945"/>
+    <tag k="ref" v="SG4 90"/>
+  </node>
+  <node id="33" version="2" timestamp="2010-12-27T18:09:50Z" uid="30" user="user1" lat="52.5336725" lon="0.8310367">
+    <tag k="amenity" v="post_box"/>
+    <tag k="box_type" v="wall"/>
+    <tag k="collection_times" v="Mo-Fr 17:30; Sa 11:30"/>
+    <tag k="note" v="in brick pillar being bus shelter"/>
+    <tag k="ref" v="IP24 3228"/>
+    <tag k="royal_cypher" v="EIIR"/>
+  </node>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-snapshot.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-snapshot.osm
new file mode 100644
index 0000000..7b90171
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/node-key-value-filter-snapshot.osm
@@ -0,0 +1,53 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="13" version="2" timestamp="2011-05-08T22:06:06Z" uid="134914" user="user1" lat="51.3731042" lon="9.5130058">
+    <tag k="highway" v="bus_stop"/>
+    <tag k="name" v="Bleichplatz"/>
+    <tag k="shelter" v="yes"/>
+    <tag k="uic_ref" v="713807"/>
+  </node>
+  <node id="19" version="2" timestamp="2010-07-07T07:51:18Z" uid="10" user="user1" lat="51.9458753" lon="-0.20698">
+    <tag k="amenity" v="post_box"/>
+    <tag k="box_type" v="lamp_box"/>
+    <tag k="last_collection_mf" v="1630"/>
+    <tag k="last_collection_sat" v="0945"/>
+    <tag k="ref" v="SG4 90"/>
+  </node>
+  <node id="22" version="4" timestamp="2010-07-31T21:53:23Z" uid="20" user="user1" lat="51.938183" lon="-0.268633">
+    <tag k="amenity" v="post_box"/>
+    <tag k="box_type" v="pillar_box_k"/>
+    <tag k="collection_times" v="Mo-Fr 17:30; Sa 12:00"/>
+    <tag k="cypher" v="EIIR"/>
+    <tag k="ref" v="SG4 24"/>
+  </node>  
+  <node id="33" version="2" timestamp="2010-12-27T18:09:50Z" uid="30" user="user1" lat="52.5336725" lon="0.8310367">
+    <tag k="amenity" v="post_box"/>
+    <tag k="box_type" v="wall"/>
+    <tag k="collection_times" v="Mo-Fr 17:30; Sa 11:30"/>
+    <tag k="note" v="in brick pillar being bus shelter"/>
+    <tag k="ref" v="IP24 3228"/>
+    <tag k="royal_cypher" v="EIIR"/>
+  </node>  
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user1">
+    <nd ref="13"/>
+    <nd ref="19"/>
+    <nd ref="22"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="19"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="other_tag_1c" v="other tag before"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="other_tag_1c" v="other tag after"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user1">
+    <member type="node" ref="33" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="other_tag_1d" v="other tag before"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm
new file mode 100644
index 0000000..7b7bea9
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm
@@ -0,0 +1,33 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="other_tag_1" v="other tag before"/>
+    <tag k="other_tag_2" v="other tag after"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" lat="-3" lon="-4"/>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" lat="-5" lon="-6">
+    <tag k="other_tag_1b" v="other tag before"/>
+    <tag k="other_tag_2b" v="other tag after"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" lat="-7" lon="-8"/>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="other_tag_1c" v="other tag before"/>
+    <tag k="other_tag_1c" v="other tag after"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="3" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="other_tag_1d" v="other tag before"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm
new file mode 100644
index 0000000..f423bf4
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
+    <tag k="other_tag_1" v="other tag before"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="other_tag_2" v="other tag after"/>
+  </node>
+  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" lat="-3" lon="-4">
+    <tag k="created_by" v="Me2"/>
+  </node>
+  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" lat="-5" lon="-6">
+    <tag k="other_tag_1b" v="other tag before"/>
+    <tag k="created_by" v="Me3"/>
+    <tag k="other_tag_2b" v="other tag after"/>
+  </node>
+  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" lat="-7" lon="-8">
+    <tag k="created_by" v="Me4"/>
+  </node>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <nd ref="1"/>
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20">
+    <nd ref="2"/>
+    <nd ref="3"/>
+    <nd ref="4"/>
+    <tag k="other_tag_1c" v="other tag before"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="other_tag_1c" v="other tag after"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
+    <member type="node" ref="3" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="other_tag_1d" v="other tag before"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-expected.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-expected.osm
new file mode 100644
index 0000000..c63dc6b
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-expected.osm
@@ -0,0 +1,29 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="13" version="2" timestamp="2011-05-08T22:06:06Z" uid="134914" user="user1" lat="51.3731042" lon="9.5130058"/>
+  <node id="19" version="2" timestamp="2010-07-07T07:51:18Z" uid="10" user="user1" lat="51.9458753" lon="-0.20698"/>
+  <node id="22" version="4" timestamp="2010-07-31T21:53:23Z" uid="20" user="user1" lat="51.938183" lon="-0.268633"/>
+  <node id="33" version="2" timestamp="2010-12-27T18:09:50Z" uid="30" user="user1" lat="52.5336725" lon="0.8310367"/>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="19"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="box_type" v="lamp_box"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="other_tag_1c" v="other tag after"/>
+  </way>
+  <way id="3" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="13"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="box_type" v="wall"/>
+  </way>
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user1">
+    <member type="node" ref="3" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="other_tag_1d" v="other tag before"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-snapshot.osm b/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-snapshot.osm
new file mode 100644
index 0000000..5c08579
--- /dev/null
+++ b/osmosis-tagfilter/src/test/resources/data/template/v0_6/way-key-value-filter-snapshot.osm
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <bounds minlon="-180.00000" minlat="-90.00000" maxlon="180.00000" maxlat="90.00000" origin="Osmosis %VERSION%"/>
+  <node id="13" version="2" timestamp="2011-05-08T22:06:06Z" uid="134914" user="user1" lat="51.3731042" lon="9.5130058"/>
+  <node id="19" version="2" timestamp="2010-07-07T07:51:18Z" uid="10" user="user1" lat="51.9458753" lon="-0.20698"/>
+  <node id="22" version="4" timestamp="2010-07-31T21:53:23Z" uid="20" user="user1" lat="51.938183" lon="-0.268633"/> 
+  <node id="33" version="2" timestamp="2010-12-27T18:09:50Z" uid="30" user="user1" lat="52.5336725" lon="0.8310367"/>
+  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user1">
+    <nd ref="13"/>
+    <nd ref="19"/>
+    <nd ref="22"/>
+    <tag k="created_by" v="Me1"/>
+  </way>
+  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="19"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="box_type" v="lamp_box"/>
+    <tag k="created_by" v="Me1"/>
+    <tag k="other_tag_1c" v="other tag after"/>
+  </way>
+  <way id="3" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="13"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="box_type" v="wall"/>
+  </way>
+  <way id="4" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user1">
+    <nd ref="13"/>
+    <nd ref="22"/>
+    <nd ref="33"/>
+    <tag k="box_type" v="pillar_box_k"/>
+  </way>   
+  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user1">
+    <member type="node" ref="3" role="noderole"/>
+    <member type="way" ref="1" role="wayrole1"/>
+    <member type="way" ref="2" role="wayrole2"/>
+    <tag k="other_tag_1d" v="other tag before"/>
+    <tag k="type" v="myrelation"/>
+  </relation>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagtransform/.checkstyle b/osmosis-tagtransform/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-tagtransform/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-tagtransform/.gitignore b/osmosis-tagtransform/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-tagtransform/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-tagtransform/build.gradle b/osmosis-tagtransform/build.gradle
new file mode 100644
index 0000000..f8843b8
--- /dev/null
+++ b/osmosis-tagtransform/build.gradle
@@ -0,0 +1,12 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile project(':osmosis-xml')
+    testCompile project(':osmosis-testutil')
+}
+
+/*
+ * Disable checkstyle.
+ * The tag transform plugin used to exist outside the main source tree and as a result
+ * doesn't comply with Osmosis coding standards.
+ */
+checkstyleMain.enabled = false
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Match.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Match.java
new file mode 100644
index 0000000..a66fb3b
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Match.java
@@ -0,0 +1,20 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+public interface Match {
+
+	String getMatchID();
+
+
+	String getKey(int group);
+
+
+	String getValue(int group);
+
+
+	int getKeyGroupCount();
+
+
+	int getValueGroupCount();
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Matcher.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Matcher.java
new file mode 100644
index 0000000..11ca1c5
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Matcher.java
@@ -0,0 +1,15 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import java.util.Collection;
+import java.util.Map;
+
+
+public interface Matcher {
+
+	Collection<Match> match(Map<String, String> tags, TTEntityType type, String uname, int uid);
+
+
+	void outputStats(StringBuilder output, String indent);
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Output.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Output.java
new file mode 100644
index 0000000..d2f73a3
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Output.java
@@ -0,0 +1,12 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import java.util.Collection;
+import java.util.Map;
+
+
+public interface Output {
+
+	void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches);
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/StatsSaveException.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/StatsSaveException.java
new file mode 100644
index 0000000..3144e51
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/StatsSaveException.java
@@ -0,0 +1,13 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import java.io.IOException;
+
+public class StatsSaveException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	public StatsSaveException(String message, IOException cause) {
+		super(message, cause);
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TTEntityType.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TTEntityType.java
new file mode 100644
index 0000000..43f3681
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TTEntityType.java
@@ -0,0 +1,26 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+
+
+public enum TTEntityType {
+
+	NODE, WAY, RELATION, BOUND;
+
+
+	public static TTEntityType fromEntityType06(EntityType entityType) {
+		switch (entityType) {
+		case Node:
+			return NODE;
+		case Way:
+			return WAY;
+		case Relation:
+			return RELATION;
+		case Bound:
+			return BOUND;
+		default:
+			return null;
+		}
+	}
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TransformPlugin.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TransformPlugin.java
new file mode 100644
index 0000000..3e1bd53
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/TransformPlugin.java
@@ -0,0 +1,31 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.plugin.PluginLoader;
+import org.openstreetmap.osmosis.tagtransform.v0_6.TransformChangeTaskFactory;
+import org.openstreetmap.osmosis.tagtransform.v0_6.TransformTaskFactory;
+
+
+public class TransformPlugin implements PluginLoader {
+
+	@Override
+	public Map<String, TaskManagerFactory> loadTaskFactories() {
+		TransformTaskFactory transformFactory = new org.openstreetmap.osmosis.tagtransform.v0_6.TransformTaskFactory();
+
+		TransformChangeTaskFactory changeTransformFactory =
+				new org.openstreetmap.osmosis.tagtransform.v0_6.TransformChangeTaskFactory();
+
+		Map<String, TaskManagerFactory> tasks = new HashMap<String, TaskManagerFactory>();
+		tasks.put("tag-transform-0.6", transformFactory);
+		tasks.put("tag-transform", transformFactory);
+		tasks.put("tt", transformFactory);
+		tasks.put("tag-transform-change-0.6", changeTransformFactory);
+		tasks.put("tag-transform-change", changeTransformFactory);
+		tasks.put("ttc", changeTransformFactory);
+		return tasks;
+	}
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Translation.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Translation.java
new file mode 100644
index 0000000..1bbd037
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/Translation.java
@@ -0,0 +1,21 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform;
+
+import java.util.Collection;
+import java.util.Map;
+
+
+public interface Translation {
+
+	Collection<Match> match(Map<String, String> tags, TTEntityType entityType, String uname, int uid);
+
+
+	boolean isDropOnMatch();
+
+
+	Collection<Output> getOutputs();
+
+
+	void outputStats(StringBuilder output, String indent);
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/AndMatcher.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/AndMatcher.java
new file mode 100644
index 0000000..166cfc2
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/AndMatcher.java
@@ -0,0 +1,67 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+
+
+public class AndMatcher implements Matcher {
+
+	private Collection<Matcher> matchers;
+	private long matchHits = 0;
+	private TTEntityType type;
+	private String uname;
+	private int uid;
+
+
+	public AndMatcher(Collection<Matcher> matchers, TTEntityType type, String uname, int uid) {
+		this.matchers = matchers;
+		this.type = type;
+		this.uname = uname;
+		this.uid = uid;
+	}
+
+
+	@Override
+	public Collection<Match> match(Map<String, String> tags, TTEntityType entityType, String entityUname,
+			int entityUid) {
+		if (this.type != null && this.type != entityType) {
+			return null;
+		}
+		if (this.uname != null && !this.uname.equals(entityUname)) {
+			return null;
+		}
+		if (this.uid != 0 && this.uid != entityUid) {
+			return null;
+		}
+
+		List<Match> allMatches = new ArrayList<Match>();
+		for (Matcher matcher : matchers) {
+			Collection<Match> matches = matcher.match(tags, entityType, entityUname, entityUid);
+			if (matches == null || matches.isEmpty()) {
+				return null;
+			}
+			allMatches.addAll(matches);
+		}
+		matchHits++;
+		return allMatches;
+	}
+
+
+	@Override
+	public void outputStats(StringBuilder output, String indent) {
+		output.append(indent);
+		output.append("And: ");
+		output.append(matchHits);
+		output.append('\n');
+		for (Matcher matcher : matchers) {
+			matcher.outputStats(output, indent + "    ");
+		}
+	}
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyAll.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyAll.java
new file mode 100644
index 0000000..eef4606
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyAll.java
@@ -0,0 +1,19 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Output;
+
+
+public class CopyAll implements Output {
+
+	@Override
+	public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
+		// copy all tags
+		tags.putAll(originalTags);
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyMatched.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyMatched.java
new file mode 100644
index 0000000..90123e4
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyMatched.java
@@ -0,0 +1,23 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Output;
+
+
+public class CopyMatched implements Output {
+
+	@Override
+	public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
+		// put any matches directly
+		for (Match match : matches) {
+			if (match.getKeyGroupCount() > 0) {
+				tags.put(match.getKey(0), match.getValue(0));
+			}
+		}
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyUnmatched.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyUnmatched.java
new file mode 100644
index 0000000..eab6963
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/CopyUnmatched.java
@@ -0,0 +1,27 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Output;
+
+
+public class CopyUnmatched implements Output {
+
+	@Override
+	public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
+		// copy the original, then remove the matches
+		Map<String, String> toCopy = new HashMap<String, String>(originalTags);
+		for (Match match : matches) {
+			if (match.getKeyGroupCount() > 0) {
+				toCopy.remove(match.getKey(0));
+			}
+		}
+		// apply the copy
+		tags.putAll(toCopy);
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/MatchResultMatch.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/MatchResultMatch.java
new file mode 100644
index 0000000..daf9b58
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/MatchResultMatch.java
@@ -0,0 +1,52 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.regex.MatchResult;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+
+
+public class MatchResultMatch implements Match {
+
+	private MatchResult keyRes;
+	private MatchResult valueRes;
+	private String matchID;
+
+
+	public MatchResultMatch(String matchID, MatchResult keyRes, MatchResult valueRes) {
+		this.matchID = matchID;
+		this.keyRes = keyRes;
+		this.valueRes = valueRes;
+	}
+
+
+	@Override
+	public String getKey(int group) {
+		return keyRes.group(group);
+	}
+
+
+	@Override
+	public int getKeyGroupCount() {
+		return keyRes.groupCount() + 1;
+	}
+
+
+	@Override
+	public String getMatchID() {
+		return matchID;
+	}
+
+
+	@Override
+	public String getValue(int group) {
+		return valueRes.group(group);
+	}
+
+
+	@Override
+	public int getValueGroupCount() {
+		return valueRes.groupCount() + 1;
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/NoTagMatcher.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/NoTagMatcher.java
new file mode 100644
index 0000000..ac510e4
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/NoTagMatcher.java
@@ -0,0 +1,86 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+
+
+public class NoTagMatcher implements Matcher {
+
+	private Pattern keyPattern;
+	private Pattern valuePattern;
+	private long matchHits;
+
+
+	public NoTagMatcher(String keyPattern, String valuePattern) {
+		this.keyPattern = Pattern.compile(keyPattern);
+		this.valuePattern = Pattern.compile(valuePattern);
+	}
+
+
+	@Override
+	public Collection<Match> match(Map<String, String> tags, TTEntityType type, String uname, int uid) {
+		// loop through the tags to find matches
+		for (Entry<String, String> tag : tags.entrySet()) {
+			java.util.regex.Matcher keyMatch = keyPattern.matcher(tag.getKey());
+			java.util.regex.Matcher valueMatch = valuePattern.matcher(tag.getValue());
+			if (keyMatch.matches() && valueMatch.matches()) {
+				return null;
+			}
+		}
+
+		matchHits += 1;
+		return Collections.singleton(NULL_MATCH);
+	}
+
+
+	@Override
+	public void outputStats(StringBuilder output, String indent) {
+		output.append(indent);
+		output.append("NoTag[");
+		output.append(keyPattern.pattern());
+		output.append(",");
+		output.append(valuePattern.pattern());
+		output.append("]: ");
+		output.append(matchHits);
+		output.append('\n');
+	}
+
+	private static final Match NULL_MATCH = new Match() {
+		@Override
+		public int getValueGroupCount() {
+			return 0;
+		}
+
+
+		@Override
+		public String getValue(int group) {
+			return null;
+		}
+
+
+		@Override
+		public String getMatchID() {
+			return null;
+		}
+
+
+		@Override
+		public int getKeyGroupCount() {
+			return 0;
+		}
+
+
+		@Override
+		public String getKey(int group) {
+			return null;
+		}
+	};
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/OrMatcher.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/OrMatcher.java
new file mode 100644
index 0000000..0e42abe
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/OrMatcher.java
@@ -0,0 +1,69 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+
+
+public class OrMatcher implements Matcher {
+
+	private Collection<Matcher> matchers;
+	private long matchHits = 0;
+	private TTEntityType type;
+	private String uname;
+	private int uid;
+
+
+	public OrMatcher(Collection<Matcher> matchers, TTEntityType type, String uname, int uid) {
+		this.matchers = matchers;
+		this.type = type;
+		this.uname = uname;
+		this.uid = uid;
+	}
+
+
+	@Override
+	public Collection<Match> match(Map<String, String> tags, TTEntityType entityType, String entityUname,
+			int entityUid) {
+		if (this.type != null && this.type != entityType) {
+			return null;
+		}
+		if (this.uname != null && !this.uname.equals(entityUname)) {
+			return null;
+		}
+		if (this.uid != 0 && this.uid != entityUid) {
+			return null;
+		}
+
+		List<Match> allMatches = new ArrayList<Match>();
+		for (Matcher matcher : matchers) {
+			Collection<Match> matches = matcher.match(tags, entityType, entityUname, entityUid);
+			if (matches != null) {
+				allMatches.addAll(matches);
+			}
+		}
+		if (!allMatches.isEmpty()) {
+			matchHits++;
+		}
+		return allMatches;
+	}
+
+
+	@Override
+	public void outputStats(StringBuilder output, String indent) {
+		output.append(indent);
+		output.append("Or: ");
+		output.append(matchHits);
+		output.append('\n');
+		for (Matcher matcher : matchers) {
+			matcher.outputStats(output, indent + "    ");
+		}
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagMatcher.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagMatcher.java
new file mode 100644
index 0000000..484e4ad
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagMatcher.java
@@ -0,0 +1,68 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.MatchResult;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+
+
+public class TagMatcher implements Matcher {
+
+	private String matchID;
+	private Pattern keyPattern;
+	private Pattern valuePattern;
+	private long matchHits = 0;
+
+
+	public TagMatcher(String matchID, String keyPattern, String valuePattern) {
+		this.matchID = matchID;
+		this.keyPattern = Pattern.compile(keyPattern);
+		this.valuePattern = Pattern.compile(valuePattern);
+	}
+
+
+	@Override
+	public Collection<Match> match(Map<String, String> tags, TTEntityType type, String uname, int uid) {
+		List<Match> matches = new ArrayList<Match>();
+
+		// loop through the tags to find matches
+		for (Entry<String, String> tag : tags.entrySet()) {
+			java.util.regex.Matcher keyMatch = keyPattern.matcher(tag.getKey());
+			java.util.regex.Matcher valueMatch = valuePattern.matcher(tag.getValue());
+			if (keyMatch.matches() && valueMatch.matches()) {
+				MatchResult keyRes = keyMatch.toMatchResult();
+				MatchResult valueRes = valueMatch.toMatchResult();
+				matches.add(new MatchResultMatch(matchID, keyRes, valueRes));
+			}
+		}
+
+		matchHits += matches.size();
+		return matches;
+	}
+
+
+	@Override
+	public void outputStats(StringBuilder output, String indent) {
+		output.append(indent);
+		output.append("Tag[");
+		if (matchID != null) {
+			output.append(matchID);
+			output.append(",");
+		}
+		output.append(keyPattern.pattern());
+		output.append(",");
+		output.append(valuePattern.pattern());
+		output.append("]: ");
+		output.append(matchHits);
+		output.append('\n');
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagOutput.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagOutput.java
new file mode 100644
index 0000000..faee7eb
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TagOutput.java
@@ -0,0 +1,68 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Output;
+
+
+public class TagOutput implements Output {
+
+	private MessageFormat keyFormat;
+	private MessageFormat valueFormat;
+	private String fromMatch;
+
+
+	public TagOutput(String key, String value, String fromMatch) {
+		keyFormat = new MessageFormat(santitise(key));
+		valueFormat = new MessageFormat(santitise(value));
+		if ((fromMatch != null && fromMatch.length() > 0)) {
+			this.fromMatch = fromMatch;
+		}
+	}
+
+
+	private String santitise(String str) {
+		if (str == null || str.length() == 0) {
+			return "{0}";
+		}
+		return str;
+	}
+
+
+	@Override
+	public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
+		// if we have a fromMatch field we apply our format to
+		// each and every matching match
+		if (fromMatch != null) {
+			for (Match match : matches) {
+				String matchID = match.getMatchID();
+				if (matchID != null && matchID.equals(fromMatch)) {
+					// process key args
+					String[] args = new String[match.getKeyGroupCount()];
+					for (int i = 0; i < args.length; i++) {
+						args[i] = match.getKey(i);
+					}
+					String key = keyFormat.format(args);
+
+					// process value args
+					args = new String[match.getValueGroupCount()];
+					for (int i = 0; i < args.length; i++) {
+						args[i] = match.getValue(i);
+					}
+					String value = valueFormat.format(args);
+
+					// put the tag
+					tags.put(key, value);
+				}
+			}
+		} else {
+			// simple case
+			tags.put(keyFormat.format(null), valueFormat.format(null));
+		}
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformHelper.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformHelper.java
new file mode 100644
index 0000000..8841587
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformHelper.java
@@ -0,0 +1,153 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampFormat;
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.common.Task;
+import org.openstreetmap.osmosis.core.task.v0_6.Initializable;
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Output;
+import org.openstreetmap.osmosis.tagtransform.StatsSaveException;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+import org.openstreetmap.osmosis.tagtransform.Translation;
+import org.openstreetmap.osmosis.xml.common.XmlTimestampFormat;
+
+
+/**
+ * Class is intended to provide utility place for tag transform functionality. <br/>
+ * See {@link org.openstreetmap.osmosis.tagtransform.v0_6.TransformTask
+ * TransformTask} for example implementation.
+ * 
+ * @author apopov
+ * 
+ * @param <T>
+ *            is a sink type.
+ */
+public abstract class TransformHelper<T extends Task & Initializable> implements Initializable {
+	protected Logger logger = Logger.getLogger(this.getClass().getName());
+
+	protected T sink;
+	protected String statsFile;
+	protected String configFile;
+	protected List<Translation> translations;
+	protected static TimestampFormat timestampFormat = new XmlTimestampFormat();
+
+
+	public TransformHelper(String configFile, String statsFile) {
+		logger.log(Level.FINE, "Transform configured with " + configFile + " and " + statsFile);
+		translations = new TransformLoader().load(configFile);
+		this.statsFile = statsFile;
+		this.configFile = configFile;
+	}
+
+
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		sink.initialize(metaData);
+	}
+
+
+	public void setSink(T sink) {
+		this.sink = sink;
+	}
+
+
+	@Override
+	public void complete() {
+		if (statsFile != null && !statsFile.isEmpty()) {
+			StringBuilder builder = new StringBuilder();
+			builder.append(configFile);
+			builder.append("\n\n");
+			for (Translation t : translations) {
+				t.outputStats(builder, "");
+			}
+
+			Writer writer = null;
+			try {
+				writer = new FileWriter(new File(statsFile));
+				writer.write(builder.toString());
+			} catch (IOException e) {
+				throw new StatsSaveException("Failed to save stats: " + e.getLocalizedMessage(), e);
+			} finally {
+				if (writer != null) {
+					try {
+						writer.close();
+					} catch (IOException e) {
+						logger.log(Level.WARNING, "Unable to close stats file " + statsFile + ".", e);
+					}
+				}
+			}
+		}
+		sink.complete();
+	}
+
+
+	@Override
+	public void release() {
+		sink.release();
+	}
+
+
+	/**
+	 * Transforms entity container according to configFile.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 * @return transformed (if needed) entityContainer
+	 */
+	protected EntityContainer processEntityContainer(EntityContainer entityContainer) {
+		// Entities may have been made read-only at some point in the pipeline.
+		// We want a writeable instance so that we can update the tags.
+		EntityContainer writeableEntityContainer = entityContainer.getWriteableInstance();
+		Entity entity = entityContainer.getEntity();
+		Collection<Tag> entityTags = entity.getTags();
+		EntityType entityType = entity.getType();
+
+		// Store the tags in a map keyed by tag key.
+		Map<String, String> tagMap = new HashMap<String, String>();
+		for (Tag tag : entity.getTags()) {
+			tagMap.put(tag.getKey(), tag.getValue());
+		}
+
+		// Apply tag transformations.
+		for (Translation translation : translations) {
+			Collection<Match> matches = translation.match(tagMap, TTEntityType.fromEntityType06(entityType), entity
+					.getUser().getName(), entity.getUser().getId());
+			if (matches == null || matches.isEmpty()) {
+				continue;
+			}
+			if (translation.isDropOnMatch()) {
+				return null;
+			}
+
+			Map<String, String> newTags = new HashMap<String, String>();
+			for (Output output : translation.getOutputs()) {
+				output.apply(tagMap, newTags, matches);
+			}
+			tagMap = newTags;
+		}
+
+		// Replace the entity tags with the transformed values.
+		entityTags.clear();
+		for (Entry<String, String> tag : tagMap.entrySet()) {
+			entityTags.add(new Tag(tag.getKey(), tag.getValue()));
+		}
+
+		return writeableEntityContainer;
+	}
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoadException.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoadException.java
new file mode 100644
index 0000000..c3e41f0
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoadException.java
@@ -0,0 +1,12 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+public class TransformLoadException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+
+	public TransformLoadException(String message, Exception cause) {
+		super(message, cause);
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoader.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoader.java
new file mode 100644
index 0000000..c1eace5
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TransformLoader.java
@@ -0,0 +1,181 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.Output;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+import org.openstreetmap.osmosis.tagtransform.Translation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+
+public class TransformLoader {
+	private static final Logger LOG = Logger.getLogger(TransformLoader.class.getName());
+
+
+	public List<Translation> load(String configFile) {
+		List<Translation> translations = new ArrayList<Translation>();
+		File file = new File(configFile);
+		try {
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			Document doc = builder.parse(file);
+
+			NodeList translationElements = doc.getDocumentElement().getElementsByTagName("translation");
+			for (int i = 0; i < translationElements.getLength(); i++) {
+				Translation t = parseTranslation((Element) translationElements.item(i));
+				if (t != null) {
+					translations.add(t);
+				}
+			}
+		} catch (Exception e) {
+			throw new TransformLoadException("Failed to load transform", e);
+		}
+		return translations;
+	}
+
+
+	private Translation parseTranslation(Element element) {
+		String name = "";
+		String description = "";
+		Matcher matcher = null;
+		Matcher finder = null;
+		List<Output> output = new ArrayList<Output>();
+
+		NodeList children = element.getChildNodes();
+		for (int i = 0; i < children.getLength(); i++) {
+			if (!(children.item(i) instanceof Element)) {
+				continue;
+			}
+			Element child = (Element) children.item(i);
+			String nodeName = child.getNodeName();
+			if (nodeName.equals("name")) {
+				name = child.getTextContent();
+			} else if (nodeName.equals("description")) {
+				description = child.getTextContent();
+			} else if (nodeName.equals("match")) {
+				matcher = parseMatcher(child);
+			} else if (nodeName.equals("find")) {
+				finder = parseMatcher(child);
+			} else if (nodeName.equals("output")) {
+				NodeList outputs = child.getChildNodes();
+				for (int j = 0; j < outputs.getLength(); j++) {
+					if (!(outputs.item(j) instanceof Element)) {
+						continue;
+					}
+					Output o = parseOutput((Element) outputs.item(j));
+					if (o != null) {
+						output.add(o);
+					}
+				}
+			}
+		}
+
+		if (matcher != null) {
+			LOG.info("New translation: " + name);
+			return new TranslationImpl(name, description, matcher, finder, output);
+		} else {
+			return null;
+		}
+	}
+
+
+	private Output parseOutput(Element child) {
+		String name = child.getNodeName();
+		if (name.equals("copy-all")) {
+			return new CopyAll();
+		} else if (name.equals("copy-unmatched")) {
+			return new CopyUnmatched();
+		} else if (name.equals("copy-matched")) {
+			return new CopyMatched();
+		} else if (name.equals("tag")) {
+			String k = child.getAttribute("k");
+			String v = child.getAttribute("v");
+			String m = child.getAttribute("from_match");
+			return new TagOutput(k, v, m);
+		}
+		return null;
+	}
+
+
+	private Matcher parseMatcher(Element matcher) {
+		String name = matcher.getNodeName();
+		if (name.equals("match") || name.equals("find")) {
+			NodeList children = matcher.getChildNodes();
+			List<Matcher> matchers = new ArrayList<Matcher>();
+			String uname = null;
+			int uid = 0;
+
+			for (int i = 0; i < children.getLength(); i++) {
+				if (!(children.item(i) instanceof Element)) {
+					continue;
+				}
+				Element child = (Element) children.item(i);
+				Matcher m = parseMatcher(child);
+				if (m != null) {
+					matchers.add(m);
+				}
+			}
+
+			TTEntityType type = getType(matcher.getAttribute("type"));
+			if (matcher.getAttribute("user") != "") {
+				uname = matcher.getAttribute("user");
+			}
+			if (matcher.getAttribute("uid") != "") {
+				uid = Integer.parseInt(matcher.getAttribute("uid"));
+			}
+			String mode;
+			if (name.equals("find")) {
+				mode = "or";
+			} else {
+				mode = matcher.getAttribute("mode");
+			}
+			if (mode == null || mode.equals("") || mode.equals("and")) {
+				return new AndMatcher(matchers, type, uname, uid);
+			} else if (mode.equals("or")) {
+				return new OrMatcher(matchers, type, uname, uid);
+			}
+
+		} else if (name.equals("tag")) {
+			String k = matcher.getAttribute("k");
+			String v = matcher.getAttribute("v");
+			String id = matcher.getAttribute("match_id");
+			return new TagMatcher(id, k, v);
+		} else if (name.equals("notag")) {
+			String k = matcher.getAttribute("k");
+			String v = matcher.getAttribute("v");
+			return new NoTagMatcher(k, v);
+		}
+		return null;
+	}
+
+
+	private TTEntityType getType(String type) {
+		if (type == null || type.isEmpty() || type.equals("all")) {
+			return null;
+		}
+		if (type.equals("node")) {
+			return TTEntityType.NODE;
+		}
+		if (type.equals("way")) {
+			return TTEntityType.WAY;
+		}
+		if (type.equals("relation")) {
+			return TTEntityType.RELATION;
+		}
+		if (type.equals("bound")) {
+			return TTEntityType.BOUND;
+		}
+		return null;
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TranslationImpl.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TranslationImpl.java
new file mode 100644
index 0000000..9ab6d0f
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/impl/TranslationImpl.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.tagtransform.Match;
+import org.openstreetmap.osmosis.tagtransform.Matcher;
+import org.openstreetmap.osmosis.tagtransform.Output;
+import org.openstreetmap.osmosis.tagtransform.TTEntityType;
+import org.openstreetmap.osmosis.tagtransform.Translation;
+
+
+public class TranslationImpl implements Translation {
+
+	private String name;
+	private String description;
+	private Matcher matcher;
+	private List<Output> output;
+	private Matcher finder;
+
+
+	public TranslationImpl(String name, String description, Matcher matcher, Matcher finder, List<Output> output) {
+		this.name = name;
+		this.description = description;
+		this.matcher = matcher;
+		this.finder = finder;
+		this.output = output;
+	}
+
+
+	@Override
+	public Collection<Output> getOutputs() {
+		return output;
+	}
+
+
+	@Override
+	public boolean isDropOnMatch() {
+		return output.isEmpty();
+	}
+
+
+	@Override
+	public Collection<Match> match(Map<String, String> tags, TTEntityType type, String uname, int uid) {
+		Collection<Match> matches = matcher.match(tags, type, uname, uid);
+		if (matches != null && !matches.isEmpty()) {
+			Collection<Match> finds;
+			if (finder == null) {
+				finds = null;
+			} else {
+				finds = finder.match(tags, type, uname, uid);
+			}
+			if (finds != null && !finds.isEmpty()) {
+				if (matches instanceof ArrayList) {
+					matches.addAll(finds);
+				} else {
+					List<Match> allMatches = new ArrayList<Match>();
+					allMatches.addAll(matches);
+					allMatches.addAll(finds);
+					return allMatches;
+				}
+			}
+
+			return matches;
+		}
+
+		return null;
+	}
+
+
+	@Override
+	public void outputStats(StringBuilder statsOutput, String indent) {
+		statsOutput.append(indent);
+		statsOutput.append(name);
+		statsOutput.append(":");
+		statsOutput.append('\n');
+		if (description != null && !description.isEmpty()) {
+			statsOutput.append(description);
+			statsOutput.append('\n');
+		}
+		matcher.outputStats(statsOutput, indent + "    ");
+		if (finder != null) {
+			finder.outputStats(statsOutput, "  + ");
+		}
+		statsOutput.append('\n');
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTask.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTask.java
new file mode 100644
index 0000000..2f00e61
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTask.java
@@ -0,0 +1,38 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.v0_6;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
+import org.openstreetmap.osmosis.tagtransform.impl.TransformHelper;
+
+
+public class TransformChangeTask extends TransformHelper<ChangeSink> implements ChangeSinkChangeSource {
+
+	public TransformChangeTask(String configFile, String statsFile) {
+		super(configFile, statsFile);
+	}
+
+
+	@Override
+	public void process(ChangeContainer changeContainer) {
+		if (!ChangeAction.Delete.equals(changeContainer.getAction())) {
+			EntityContainer output = super.processEntityContainer(changeContainer.getEntityContainer());
+
+			if (output != null) {
+				sink.process(new ChangeContainer(output, changeContainer.getAction()));
+			}
+		} else {
+			sink.process(changeContainer);
+		}
+	}
+
+
+	@Override
+	public void setChangeSink(ChangeSink changeSink) {
+		this.setSink(changeSink);
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTaskFactory.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTaskFactory.java
new file mode 100644
index 0000000..b28b72b
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformChangeTaskFactory.java
@@ -0,0 +1,21 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkChangeSourceManager;
+
+
+public class TransformChangeTaskFactory extends TaskManagerFactory {
+
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String configFile =
+				getStringArgument(taskConfig, "file", getDefaultStringArgument(taskConfig, "transform.xml"));
+		String statsFile = getStringArgument(taskConfig, "stats", null);
+		return new ChangeSinkChangeSourceManager(taskConfig.getId(), new TransformChangeTask(configFile, statsFile),
+				taskConfig.getPipeArgs());
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTask.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTask.java
new file mode 100644
index 0000000..51007d0
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTask.java
@@ -0,0 +1,25 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.v0_6;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
+import org.openstreetmap.osmosis.tagtransform.impl.TransformHelper;
+
+
+public class TransformTask extends TransformHelper<Sink> implements SinkSource {
+
+	public TransformTask(String configFile, String statsFile) {
+		super(configFile, statsFile);
+	}
+
+
+	@Override
+	public void process(EntityContainer entityContainer) {
+		EntityContainer output = processEntityContainer(entityContainer);
+		if (output != null) {
+			sink.process(output);
+		}
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTaskFactory.java b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTaskFactory.java
new file mode 100644
index 0000000..d01b272
--- /dev/null
+++ b/osmosis-tagtransform/src/main/java/org/openstreetmap/osmosis/tagtransform/v0_6/TransformTaskFactory.java
@@ -0,0 +1,22 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.v0_6;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
+
+
+public class TransformTaskFactory extends TaskManagerFactory {
+
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String configFile =
+				getStringArgument(taskConfig, "file", getDefaultStringArgument(taskConfig, "transform.xml"));
+		String statsFile =
+				getStringArgument(taskConfig, "stats", null);
+		return new SinkSourceManager(taskConfig.getId(), new TransformTask(configFile, statsFile),
+				taskConfig.getPipeArgs());
+	}
+
+}
diff --git a/osmosis-tagtransform/src/main/resources/osmosis-plugins.conf b/osmosis-tagtransform/src/main/resources/osmosis-plugins.conf
new file mode 100644
index 0000000..a925920
--- /dev/null
+++ b/osmosis-tagtransform/src/main/resources/osmosis-plugins.conf
@@ -0,0 +1 @@
+org.openstreetmap.osmosis.tagtransform.TransformPlugin
\ No newline at end of file
diff --git a/osmosis-tagtransform/src/test/java/org/openstreetmap/osmosis/tagtransform/v0_6/TagTransformTest.java b/osmosis-tagtransform/src/test/java/org/openstreetmap/osmosis/tagtransform/v0_6/TagTransformTest.java
new file mode 100644
index 0000000..f522956
--- /dev/null
+++ b/osmosis-tagtransform/src/test/java/org/openstreetmap/osmosis/tagtransform/v0_6/TagTransformTest.java
@@ -0,0 +1,55 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.tagtransform.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.Osmosis;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+
+
+/**
+ * Tests the tag transform functionality.
+ * 
+ * @author Brett Henderson
+ */
+public class TagTransformTest extends AbstractDataTest {
+
+	/**
+	 * Tests transforming all tags in a single OSM file.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testTransform() throws IOException {
+		File sourceFile;
+		File translationFile;
+		File expectedOutputFile;
+		File actualOutputFile;
+
+		// Generate files.
+		sourceFile = dataUtils.createDataFile("v0_6/test-in.osm");
+		translationFile = dataUtils.createDataFile("v0_6/translation.xml");
+		expectedOutputFile = dataUtils.createDataFile("v0_6/test-out.osm");
+		actualOutputFile = dataUtils.newFile();
+
+		// Append the two source files into the destination file.
+		Osmosis.run(
+			new String[] {
+				"-q",
+				"--read-xml-0.6",
+				sourceFile.getPath(),
+				"--tag-transform-0.6",
+				"file=" + translationFile,
+				"--tag-sort-0.6",
+				"--write-xml-0.6",
+				actualOutputFile.getPath()
+			}
+		);
+
+		// Validate that the output file matches the expected result.
+		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
+	}
+}
diff --git a/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-in.osm b/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-in.osm
new file mode 100644
index 0000000..376baa8
--- /dev/null
+++ b/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-in.osm
@@ -0,0 +1,56 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51"/>
+  <node id="2" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="bicycle" v="yes"/>
+    <tag k="crossing" v="traffic_signals"/>
+    <tag k="foot" v="designated"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+    <tag k="oneway" v="true"/>
+    <tag k="tiger:tlid" v="18784790909"/>
+  </node>
+  <node id="3" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="bicycle" v="no"/>
+    <tag k="crossing" v="traffic_signals"/>
+    <tag k="foot" v="yes"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+  </node>
+  <node id="2" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="crossing" v="toucan"/>
+    <tag k="highway" v="crossing"/>
+  </node>
+  <way id="4" version="1" timestamp="2007-01-29T08:48:14Z"/>
+  <way id="5" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="highway" v="motorway"/>
+  </way>
+  <way id="6" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="ncn_ref" v="4"/>
+  </way>
+  <way id="10" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="route" v="lcn;rcn;ncn"/>
+  </way>
+  <way id="11" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="ncn" v="proposed"/>
+    <tag k="route" v="lcn;ncn;rcn"/>
+  </way>
+  <way id="12" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="highway" v="residential"/>
+    <tag k="route" v="ncn"/>
+  </way>
+  <way id="13" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="piste:lift" v="something"/>
+    <tag k="piste:lift:duration" v="16"/>
+    <tag k="piste:lift:occupancy" v="156"/>
+  </way>
+  <way id="22" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="bicycle" v="yes"/>
+    <tag k="crossing" v="traffic_signals"/>
+    <tag k="foot" v="designated"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+    <tag k="oneway" v="true"/>
+    <tag k="tiger:tlid" v="18784790909"/>
+  </way>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-out.osm b/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-out.osm
new file mode 100644
index 0000000..3824bc3
--- /dev/null
+++ b/osmosis-tagtransform/src/test/resources/data/template/v0_6/test-out.osm
@@ -0,0 +1,61 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="Osmosis %VERSION%">
+  <node id="1" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51"/>
+  <node id="2" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="bicycle" v="yes"/>
+    <tag k="crossing" v="toucan"/>
+    <tag k="foot" v="yes"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+    <tag k="oneway" v="yes"/>
+  </node>
+  <node id="3" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="bicycle" v="no"/>
+    <tag k="crossing" v="traffic_signals"/>
+    <tag k="foot" v="yes"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+  </node>
+  <node id="2" version="1" timestamp="2007-01-29T08:48:14Z" lat="1.1" lon="51">
+    <tag k="crossing" v="toucan"/>
+    <tag k="highway" v="crossing"/>
+  </node>
+  <way id="4" version="1" timestamp="2007-01-29T08:48:14Z"/>
+  <way id="5" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="highway" v="motorway"/>
+  </way>
+  <way id="6" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="ncn" v="yes"/>
+    <tag k="ncn_ref" v="4"/>
+  </way>
+  <way id="10" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="lcn" v="yes"/>
+    <tag k="ncn" v="yes"/>
+    <tag k="rcn" v="yes"/>
+    <tag k="route" v=";;"/>
+  </way>
+  <way id="11" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="lcn" v="yes"/>
+    <tag k="ncn" v="proposed"/>
+    <tag k="rcn" v="yes"/>
+    <tag k="route" v=";ncn;"/>
+  </way>
+  <way id="12" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="highway" v="residential"/>
+    <tag k="ncn" v="yes"/>
+    <tag k="route" v=""/>
+  </way>
+  <way id="13" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="duration" v="16"/>
+    <tag k="occupancy" v="156"/>
+    <tag k="piste_lift" v="something"/>
+  </way>
+  <way id="22" version="1" timestamp="2007-01-29T08:48:14Z">
+    <tag k="bicycle" v="yes"/>
+    <tag k="crossing" v="traffic_signals"/>
+    <tag k="foot" v="yes"/>
+    <tag k="highway" v="crossing"/>
+    <tag k="horse" v="no"/>
+    <tag k="oneway" v="yes"/>
+  </way>
+</osm>
\ No newline at end of file
diff --git a/osmosis-tagtransform/src/test/resources/data/template/v0_6/translation.xml b/osmosis-tagtransform/src/test/resources/data/template/v0_6/translation.xml
new file mode 100644
index 0000000..016d670
--- /dev/null
+++ b/osmosis-tagtransform/src/test/resources/data/template/v0_6/translation.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0"?>
+
+<translations>
+
+  <translation>
+    <name>Strip unused</name>
+    <match mode="or">
+      <tag k="tiger:.*" v=".*"/>
+      <tag k="created_by" v=".*"/>
+      <tag k="source" v=".*"/>
+      <tag k="attribution" v=".*"/>
+    </match>
+    <output>
+      <copy-unmatched/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>Simplify True</name>
+    <description>Simplifies the different variations of true/false, yes/no.</description>
+    <match mode="or">
+      <tag k="oneway|bridge|tunnel|ncn|lcn|rcn|bicycle|foot|motorcar|horse" match_id="yes" v="1|true"/>
+      <tag k="oneway|bridge|tunnel|ncn|lcn|rcn|bicycle|foot|motorcar|horse" match_id="no" v="0|false"/>
+      <tag k="oneway" match_id="reverse" v="reverse|backwards"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag from_match="yes" v="yes"/>
+      <tag from_match="no" v="no"/>
+      <tag from_match="reverse" v="-1"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>Simplify Access</name>
+    <description>Simplifies the access restrictions to yes/no.</description>
+    <match mode="or">
+      <tag k=".*" match_id="yes" v="designated|public|permissive"/>
+      <tag k=".*" match_id="no" v="private|privat"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag from_match="yes" v="yes"/>
+      <tag from_match="no" v="no"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>Bike->Toucan</name>
+    <description>Convert bike crossings</description>
+    <match mode="or" type="node">
+      <match>
+        <match mode="or">
+          <tag k="highway" v="traffic_signals"/>
+          <tag k="highway" v="crossing"/>
+          <tag k="crossing" v="traffic_signals"/>
+        </match>
+        <tag k="bicycle" v="yes"/>
+      </match>
+      <tag k="crossing_ref" v="toucan"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag k="crossing" v="toucan"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>Paths->track</name>
+    <match type="way">
+      <tag k="highway" v="path"/>
+      <tag k="motorcar" v="yes"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag highway="track"/>
+    </output>
+  </translation>
+  
+  <translation>
+    <name>Paths/footways->cycleway</name>
+    <description>Convert paths to cycleways -- these have precedence in this result</description>
+    <match type="way">
+      <match mode="or">
+        <tag k="highway" v="path"/>
+        <tag k="highway" v="footway"/>
+      </match>
+      <tag k="bicycle" v="yes"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag highway="cycleway"/>
+    </output>
+  </translation>
+  
+  <translation>
+    <name>Paths->footway</name>
+    <match type="way">
+      <tag k="highway" v="path"/>
+      <tag k="foot" v="yes"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag highway="footway"/>
+    </output>
+  </translation>
+  
+  <translation>
+    <name>NCN</name>
+    <description>Find all the way ncn variations and tag consistently</description>
+    <match>
+      <match mode="or">
+        <tag k="route" v="(.*;|^)ncn(;.*|$)" match_id="route"/>
+        <tag k="ncn_ref" v=".*"/>
+      </match>
+      <notag k="ncn" v=".*"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag k="ncn" v="yes"/>
+      <tag k="route" from_match="route" v="{1}{2}"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>RCN</name>
+    <description>Find all the way rcn variations and tag consistently</description>
+    <match>
+      <match mode="or">
+        <tag k="route" v="(.*;|^)rcn(;.*|$)" match_id="route"/>
+        <tag k="rcn_ref" v=".*"/>
+      </match>
+      <notag k="rcn" v=".*"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag k="rcn" v="yes"/>
+      <tag k="route" from_match="route" v="{1}{2}"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>LCN</name>
+    <description>Find all the way lcn variations and tag consistently</description>
+    <match>
+      <match mode="or">
+        <tag k="route" v="(.*;|^)lcn(;.*|$)" match_id="route"/>
+        <tag k="lcn_ref" v=".*"/>
+      </match>
+      <notag k="lcn" v=".*"/>
+    </match>
+    <output>
+      <copy-all/>
+      <tag k="lcn" v="yes"/>
+      <tag k="route" from_match="route" v="{1}{2}"/>
+    </output>
+  </translation>
+
+  <translation>
+    <name>Arbitrary Piste Remapping</name>
+    <description>Just test some of the advanced features by remapping the piste:* style tags</description>
+    <match type="way" mode="or">
+      <tag k="piste:lift" v=".*" match_id="type"/>
+    </match>
+    <find>
+      <tag k="piste:lift:(.*)" v=".*" match_id="piste_attr"/>
+    </find>
+    <output>
+      <copy-unmatched/>
+      <tag k="piste_lift" from_match="type"/>
+      <tag from_match="piste_attr" k="{1}" v="{0}"/>
+    </output>
+  </translation>
+ <!-- And to do the reverse mapping would be:
+  <translation>
+    <name>Arbitrary Piste Remapping</name>
+    <description>Just test some of the advanced features by remapping to the piste:* style tags</description>
+    <match type="way">
+      <tag k="piste_lift" v=".*" match_id="type"/>
+      <findtag k="(occupancy|capacity|duration)" v=".*" match_id="piste_attr"/>
+    </match>
+    <output>
+      <copy-unmatched/>
+      <tag k="piste:lift" from_match="type"/>
+      <tag from_match="piste_attr" k="piste:lift:\1" v="\0"/>
+    </output>
+  </translation>
+ -->
+
+</translations>
diff --git a/osmosis-testutil/.checkstyle b/osmosis-testutil/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-testutil/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-testutil/.gitignore b/osmosis-testutil/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-testutil/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-testutil/build.gradle b/osmosis-testutil/build.gradle
new file mode 100644
index 0000000..c6c7cb4
--- /dev/null
+++ b/osmosis-testutil/build.gradle
@@ -0,0 +1,4 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile group: 'junit', name: 'junit', version: dependencyVersionJunit
+}
diff --git a/testutil/src/main/java/org/openstreetmap/osmosis/testutil/AbstractDataTest.java b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/AbstractDataTest.java
similarity index 100%
rename from testutil/src/main/java/org/openstreetmap/osmosis/testutil/AbstractDataTest.java
rename to osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/AbstractDataTest.java
diff --git a/testutil/src/main/java/org/openstreetmap/osmosis/testutil/TestDataUtilities.java b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/TestDataUtilities.java
similarity index 100%
rename from testutil/src/main/java/org/openstreetmap/osmosis/testutil/TestDataUtilities.java
rename to osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/TestDataUtilities.java
diff --git a/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/RunTaskUtilities.java b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/RunTaskUtilities.java
new file mode 100644
index 0000000..b1119c2
--- /dev/null
+++ b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/RunTaskUtilities.java
@@ -0,0 +1,118 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.testutil.v0_6;
+
+import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableChangeSource;
+import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+
+/**
+ * Utility methods for running complicated tasks like MultiSinks.
+ * 
+ * @author Igor Podolskiy
+ */
+public final class RunTaskUtilities {
+
+	private RunTaskUtilities() {
+	}
+	
+	/**
+	 * Helper method to execute a two-sink entity source.
+	 * 
+	 * @param multiSink the multi-sink source to run.
+	 * @param source1 the first source to feed to the sink.
+	 * @param source2 the second source to feed to the sink.
+	 * @return the sink entity inspector containing the result.
+	 * @throws Exception if something goes wrong.
+	 */
+	public static SinkEntityInspector run(MultiSinkRunnableSource multiSink, 
+			RunnableSource source1, RunnableSource source2) throws Exception {
+		return run(multiSink, source1, source2, null);
+	}
+	
+	/**
+	 * Helper method to execute a two-sink entity source.
+	 * 
+	 * @param multiSink the multi-sink source to run.
+	 * @param source1 the first source to feed to the sink.
+	 * @param source2 the second source to feed to the sink.
+	 * @param exceptionHandler the exception handler to attach to threads.
+	 * @return the sink entity inspector containing the result.
+	 * @throws Exception if something goes wrong.
+	 */
+	public static SinkEntityInspector run(MultiSinkRunnableSource multiSink, 
+			RunnableSource source1, RunnableSource source2,
+			Thread.UncaughtExceptionHandler exceptionHandler) throws Exception {
+		
+		SinkEntityInspector inspector = new SinkEntityInspector();
+		source1.setSink(multiSink.getSink(0));
+		source2.setSink(multiSink.getSink(1));
+		multiSink.setSink(inspector);
+		
+		runCore(multiSink, source1, source2, exceptionHandler);
+		
+		return inspector;
+	}
+
+	/**
+	 * Helper method to execute a two-sink change source.
+	 * 
+	 * @param multiSink the twp-sink change source to run.
+	 * @param source1 the first source to feed to the sink.
+	 * @param source2 the second source to feed to the sink.
+	 * @return the sink change inspector containing the result.
+	 * @throws Exception if something goes wrong.
+	 */
+	public static SinkChangeInspector run(MultiSinkRunnableChangeSource multiSink, 
+			RunnableSource source1, RunnableSource source2) throws Exception {
+		return run(multiSink, source1, source2, null);
+	}
+
+	
+	/**
+	 * Helper method to execute a two-sink change source.
+	 * 
+	 * @param multiSink the twp-sink change source to run.
+	 * @param source1 the first source to feed to the sink.
+	 * @param source2 the second source to feed to the sink.
+	 * @param exceptionHandler the exception handler to attach to threads.
+	 * @return the sink change inspector containing the result.
+	 * @throws Exception if something goes wrong.
+	 */
+	public static SinkChangeInspector run(MultiSinkRunnableChangeSource multiSink, 
+			RunnableSource source1, RunnableSource source2,
+			Thread.UncaughtExceptionHandler exceptionHandler) throws Exception {
+		SinkChangeInspector inspector = new SinkChangeInspector();
+		
+		source1.setSink(multiSink.getSink(0));
+		source2.setSink(multiSink.getSink(1));
+		multiSink.setChangeSink(inspector);
+		
+		runCore(multiSink, source1, source2, exceptionHandler);
+		
+		return inspector;
+	}
+	
+	private static void runCore(Runnable multiSink,
+			Runnable source1, Runnable source2,
+			Thread.UncaughtExceptionHandler exceptionHandler)
+			throws InterruptedException {
+
+		Thread sourceThread1 = new Thread(source1);
+		Thread sourceThread2 = new Thread(source2);
+		Thread sinkThread = new Thread(multiSink);
+		
+		if (exceptionHandler != null) {
+			sourceThread1.setUncaughtExceptionHandler(exceptionHandler);
+			sourceThread2.setUncaughtExceptionHandler(exceptionHandler);
+			sinkThread.setUncaughtExceptionHandler(exceptionHandler);
+		}
+
+		sinkThread.start();
+		sourceThread1.start();
+		sourceThread2.start();
+		
+		sinkThread.join();
+		sourceThread1.join();
+		sourceThread2.join();
+	}
+}
diff --git a/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkChangeInspector.java b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkChangeInspector.java
new file mode 100644
index 0000000..a67bee3
--- /dev/null
+++ b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkChangeInspector.java
@@ -0,0 +1,70 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.testutil.v0_6;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+
+/**
+ * Mock object for inspecting the resulting changes after passing through a pipeline task.
+ * 
+ * @author Igor Podolskiy
+ */
+public class SinkChangeInspector implements ChangeSink {
+	
+	private List<ChangeContainer> receivedChanges;
+
+	/**
+	 * Creates a new instance.
+	 */
+	public SinkChangeInspector() {
+		receivedChanges = new ArrayList<ChangeContainer>();
+	}
+	
+	@Override
+	public void initialize(Map<String, Object> metaData) {
+		// Nothing to do here
+	}
+
+	@Override
+	public void complete() {
+		// Nothing to do here
+	}
+
+	@Override
+	public void release() {
+		// Nothing to do here
+	}
+
+	@Override
+	public void process(ChangeContainer change) {
+		receivedChanges.add(change);
+	}
+	
+	/**
+	 * Returns the list of the processed changes.
+	 * 
+	 * @return the list of the processed changes, never null.
+	 */
+	public List<ChangeContainer> getProcessedChanges() {
+		return receivedChanges;
+	}
+
+	/**
+	 * Returns the last processed change container, or null if no changes have
+	 * been processed.
+	 * 
+	 * @return the last processed change container, or null if no changes have
+	 *         been processed.
+	 */
+	public ChangeContainer getLastChangeContainer() {
+		if (receivedChanges.isEmpty()) {
+			return null;
+		}
+		return receivedChanges.get(receivedChanges.size() - 1);
+	}
+
+}
diff --git a/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java
new file mode 100644
index 0000000..616ebc1
--- /dev/null
+++ b/osmosis-testutil/src/main/java/org/openstreetmap/osmosis/testutil/v0_6/SinkEntityInspector.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.testutil.v0_6;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+
+/**
+ * Mock object for inspecting the resulting entities after passing through a pipeline task.
+ * 
+ * @author Karl Newman
+ */
+public class SinkEntityInspector implements Sink {
+
+	private List<EntityContainer> processedEntities;
+	
+	
+	/**
+	 * Creates a new instance.
+	 */
+	public SinkEntityInspector() {
+		processedEntities = new LinkedList<EntityContainer>();
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void complete() {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * Catch all passed entities and save them for later inspection.
+	 * 
+	 * @param entityContainer
+	 *            The entity to be processed.
+	 */
+	@Override
+	public void process(EntityContainer entityContainer) {
+		processedEntities.add(entityContainer);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void release() {
+		// Nothing to do here
+	}
+
+
+	/**
+	 * Shortcut method if you only care about the most recent EntityContainer.
+	 * 
+	 * @return the lastEntityContainer
+	 */
+	public EntityContainer getLastEntityContainer() {
+		if (processedEntities.isEmpty()) {
+			return null;
+		} else {
+			return processedEntities.get(processedEntities.size() - 1);
+		}
+	}
+
+
+	/**
+	 * Retrieve an Iterable of all the processed EntityContainers.
+	 * 
+	 * @return the processedEntities
+	 */
+	public Iterable<EntityContainer> getProcessedEntities() {
+		return Collections.unmodifiableList(processedEntities);
+	}
+
+}
diff --git a/osmosis-xml/.checkstyle b/osmosis-xml/.checkstyle
new file mode 100644
index 0000000..b945ac0
--- /dev/null
+++ b/osmosis-xml/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Osmosis Checks" location="/build-support/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/osmosis-xml/.gitignore b/osmosis-xml/.gitignore
new file mode 100644
index 0000000..c2bc33a
--- /dev/null
+++ b/osmosis-xml/.gitignore
@@ -0,0 +1,6 @@
+.classpath
+.project
+.settings
+/bin
+/build
+
diff --git a/osmosis-xml/build.gradle b/osmosis-xml/build.gradle
new file mode 100644
index 0000000..1c599a3
--- /dev/null
+++ b/osmosis-xml/build.gradle
@@ -0,0 +1,5 @@
+dependencies {
+    compile project(':osmosis-core')
+    compile group: 'commons-codec', name: 'commons-codec', version: dependencyVersionCommonsCodec
+    testCompile project(':osmosis-testutil')
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/XmlPluginLoader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/XmlPluginLoader.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/XmlPluginLoader.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/XmlPluginLoader.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseElementProcessor.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java
new file mode 100644
index 0000000..960900f
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java
@@ -0,0 +1,234 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.common;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * An OSM data sink for storing all data to an xml file.
+ * 
+ * @author Brett Henderson
+ */
+public abstract class BaseXmlWriter {
+	
+	private static Logger log = Logger.getLogger(BaseXmlWriter.class.getName());
+	
+	
+	private boolean closeRequired;
+	private boolean writerProvided;
+	private File file;
+	private boolean initialized;
+	private BufferedWriter writer;
+	private CompressionMethod compressionMethod;
+	
+	
+	/**
+	 * Creates a new instance to write to the provided writer.
+	 * 
+	 * @param writer The writer to receive data.  This writer will not be closed on completion.
+	 */
+	public BaseXmlWriter(BufferedWriter writer) {
+		this.writer = writer;
+		
+		writerProvided = true;
+		closeRequired = false;
+	}
+	
+	
+	/**
+	 * Creates a new instance to write to the specified file.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 */
+	public BaseXmlWriter(File file, CompressionMethod compressionMethod) {
+		this.file = file;
+		this.compressionMethod = compressionMethod;
+		
+		writerProvided = false;
+		closeRequired = true;
+	}
+	
+	
+	/**
+	 * Sets the writer on the element writer used for this implementation.
+	 * 
+	 * @param resultWriter
+	 *            The writer receiving xml data.
+	 */
+	protected abstract void setWriterOnElementWriter(BufferedWriter resultWriter);
+	
+	
+	/**
+	 * Calls the begin method of the element writer used for this implementation.
+	 */
+	protected abstract void beginElementWriter();
+	
+	
+	/**
+	 * Calls the end method of the element writer used for this implementation.
+	 */
+	protected abstract void endElementWriter();
+	
+	
+	/**
+	 * Writes data to the output file.
+	 * 
+	 * @param data
+	 *            The data to be written.
+	 */
+	private void write(String data) {
+		try {
+			writer.write(data);
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write data.", e);
+		}
+	}
+	
+	
+	/**
+	 * Writes a new line in the output file.
+	 */
+	private void writeNewLine() {
+		try {
+			writer.newLine();
+			
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to write data.", e);
+		}
+	}
+    
+    
+	/**
+	 * Initialize the object.
+	 * 
+	 * @param metaData
+	 *            Meta data applicable to this pipeline invocation.
+	 */
+	public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+	
+	
+	/**
+	 * Initialises the output file for writing. This must be called by
+	 * sub-classes before any writing is performed. This method may be called
+	 * multiple times without adverse affect allowing sub-classes to invoke it
+	 * every time they perform processing.
+	 */
+	protected void initialize() {
+		if (!initialized) {
+			if (!writerProvided) {
+				OutputStream outStream = null;
+				
+				try {
+					OutputStreamWriter outStreamWriter;
+					
+					// make "-" an alias for /dev/stdout
+					if (file.getName().equals("-")) {
+						outStream = System.out;
+						
+						// We don't want to close stdout because we'll need to
+						// re-use it if we receive multiple streams.
+						closeRequired = false;
+					} else {
+						outStream = new FileOutputStream(file);
+					}
+					
+					outStream =
+						new CompressionActivator(compressionMethod).createCompressionOutputStream(outStream);
+					
+					outStreamWriter = new OutputStreamWriter(outStream, "UTF-8");
+					
+					writer = new BufferedWriter(outStreamWriter);
+					
+					outStream = null;
+					
+				} catch (IOException e) {
+					throw new OsmosisRuntimeException("Unable to open file for writing.", e);
+				} finally {
+					if (outStream != null) {
+						try {
+							outStream.close();
+						} catch (Exception e) {
+							log.log(Level.SEVERE, "Unable to close output stream.", e);
+						}
+						outStream = null;
+					}
+				}
+			}
+			
+			setWriterOnElementWriter(writer);
+			
+			initialized = true;
+			
+			write("<?xml version='1.0' encoding='UTF-8'?>");
+			writeNewLine();
+			
+			beginElementWriter();
+		}
+	}
+	
+	
+	/**
+	 * Flushes all changes to file.
+	 */
+	public void complete() {
+		// We need to call this here so that we create empty files if no records
+		// are available.
+		initialize();
+
+		endElementWriter();
+
+		try {
+			if (closeRequired) {
+				writer.close();
+				writer = null;
+			} else if (!writerProvided) {
+				writer.flush();
+			}
+
+			initialized = false;
+
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to complete writing to the xml stream.", e);
+		}
+	}
+	
+	
+	/**
+	 * Cleans up any open file handles.
+	 */
+	public void release() {
+		try {
+			if (closeRequired) {
+				try {
+					try {
+						if (writer != null) {
+							writer.close();
+						}
+					} catch (IOException e) {
+						log.log(Level.SEVERE, "Unable to close writer.", e);
+					}
+				} finally {
+					writer = null;
+				}
+			}
+		} finally {
+			initialized = false;
+		}
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionActivator.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionActivator.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionActivator.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionActivator.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethod.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethod.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethod.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethod.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethodDeriver.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethodDeriver.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethodDeriver.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/CompressionMethodDeriver.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/DummyElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/DummyElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/DummyElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/DummyElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementWriter.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementWriter.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/ElementWriter.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTaskManagerFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTaskManagerFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTaskManagerFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTaskManagerFactory.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTimestampFormat.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTimestampFormat.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTimestampFormat.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/common/XmlTimestampFormat.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java
new file mode 100644
index 0000000..d32aadb
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java
@@ -0,0 +1,116 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.CompressionActivator;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.impl.FastXmlParser;
+
+
+/**
+ * An OSM data source reading from an xml file. The entire contents of the file
+ * are read.
+ * 
+ * @author Jiri Clement
+ * @author Brett Henderson
+ */
+public class FastXmlReader implements RunnableSource {
+		
+		private static Logger log = Logger.getLogger(FastXmlReader.class.getName());
+		
+		private Sink sink;
+		private final File file;
+		private final boolean enableDateParsing;
+		private final CompressionMethod compressionMethod;
+		
+		
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param file
+		 *            The file to read.
+		 * @param enableDateParsing
+		 *            If true, dates will be parsed from xml data, else the current
+		 *            date will be used thus saving parsing time.
+		 * @param compressionMethod
+		 *            Specifies the compression method to employ.
+		 */
+		public FastXmlReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
+			this.file = file;
+			this.enableDateParsing = enableDateParsing;
+			this.compressionMethod = compressionMethod;
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void setSink(Sink sink) {
+			this.sink = sink;
+		}
+		
+				
+		
+		/**
+		 * Reads all data from the file and send it to the sink.
+		 */
+		public void run() {
+			InputStream inputStream = null;
+			FastXmlParser parser = null;
+			
+			try {
+				sink.initialize(Collections.<String, Object>emptyMap());
+				
+				// make "-" an alias for /dev/stdin
+				if (file.getName().equals("-")) {
+					inputStream = System.in;
+				} else {
+					inputStream = new FileInputStream(file);
+				}
+				
+				
+				inputStream =
+					new CompressionActivator(compressionMethod).
+						createCompressionInputStream(inputStream);
+				
+		        XMLInputFactory factory = XMLInputFactory.newInstance();
+		        factory.setProperty(XMLInputFactory.IS_COALESCING, false);
+		        factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+		        factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
+		        XMLStreamReader xpp = factory.createXMLStreamReader(inputStream);
+				
+				parser = new FastXmlParser(sink, xpp, enableDateParsing);
+				
+				parser.readOsm();
+				
+				sink.complete();
+				
+			} catch (Exception e) {
+				throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
+			} finally {
+				sink.release();
+				
+				if (inputStream != null) {
+					try {
+						inputStream.close();
+					} catch (IOException e) {
+						log.log(Level.SEVERE, "Unable to close input stream.", e);
+					}
+					inputStream = null;
+				}
+			}
+		}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReaderFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReaderFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReaderFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReaderFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java
new file mode 100644
index 0000000..d64e6be
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java
@@ -0,0 +1,138 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
+import org.openstreetmap.osmosis.xml.common.CompressionActivator;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+/**
+ * A change source reading from an xml file. The entire contents of the file
+ * are read.
+ * 
+ * @author Brett Henderson
+ */
+public class XmlChangeReader implements RunnableChangeSource {
+	
+	private static Logger log = Logger.getLogger(XmlReader.class.getName());
+	
+	private ChangeSink changeSink;
+	private File file;
+	private boolean enableDateParsing;
+	private CompressionMethod compressionMethod;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to read.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 */
+	public XmlChangeReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
+		this.file = file;
+		this.enableDateParsing = enableDateParsing;
+		this.compressionMethod = compressionMethod;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setChangeSink(ChangeSink changeSink) {
+		this.changeSink = changeSink;
+	}
+	
+	
+	/**
+	 * Creates a new SAX parser.
+	 * 
+	 * @return The newly created SAX parser.
+	 */
+	private SAXParser createParser() {
+		try {
+			return SAXParserFactory.newInstance().newSAXParser();
+			
+		} catch (ParserConfigurationException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		}
+	}
+	
+	
+	/**
+	 * Reads all data from the file and send it to the sink.
+	 */
+	public void run() {
+		InputStream inputStream = null;
+		
+		try {
+			SAXParser parser;
+			
+			changeSink.initialize(Collections.<String, Object>emptyMap());
+			
+			// make "-" an alias for /dev/stdin
+			if (file.getName().equals("-")) {
+				inputStream = System.in;
+			} else {
+				inputStream = new FileInputStream(file);
+			}
+			
+			inputStream =
+				new CompressionActivator(compressionMethod).
+					createCompressionInputStream(inputStream);
+			
+			parser = createParser();
+			
+			parser.parse(inputStream, new OsmChangeHandler(changeSink, enableDateParsing));
+			
+			changeSink.complete();
+			
+		} catch (SAXParseException e) {
+			throw new OsmosisRuntimeException(
+				"Unable to parse xml file " + file
+				+ ".  publicId=(" + e.getPublicId()
+				+ "), systemId=(" + e.getSystemId()
+				+ "), lineNumber=" + e.getLineNumber()
+				+ ", columnNumber=" + e.getColumnNumber() + ".",
+				e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Unable to parse XML.", e);
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
+		} finally {
+			changeSink.release();
+			
+			if (inputStream != null) {
+				try {
+					inputStream.close();
+				} catch (IOException e) {
+					log.log(Level.SEVERE, "Unable to close input stream.", e);
+				}
+				inputStream = null;
+			}
+		}
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java
new file mode 100644
index 0000000..f832910
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java
@@ -0,0 +1,317 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeWriter;
+
+
+/**
+ * An OSM change sink for uploading all data to an OpenStreetMap server.
+ *
+ * @author Marcus Wolschon Marcus at Wolscon.biz
+ */
+public class XmlChangeUploader implements ChangeSink {
+
+    /**
+     * Our logger for debug and error -output.
+     */
+    private static final Logger LOG = Logger.getLogger(
+            XmlChangeUploader.class.getName());
+
+    /**
+     * Default-value for {@link DownloadingDataSet#APIBASEURSETTING}.
+     */
+    private static final String DEFAULTAPIBASEURL =
+             "http://api.openstreetmap.org/api/0.6";
+
+    /**
+     * The baseURL defaults to the value of DEFAULTAPIBASEURL.
+     */
+    private String myBaseURL;
+
+    /**
+     * the user-name to use.
+     */
+    private String myUserName;
+
+    /**
+     * the password to use.
+     */
+    private String myPassword;
+
+    /**
+     * Comment to add to the Changeset.
+     */
+    private String myComment;
+
+    /**
+     * Used to generate the XML-content of an osc-file.
+     */
+    private OsmChangeWriter myChangeWriter;
+
+    /**
+     * The ID of the changeset we opened on the server.
+     */
+    private int myChangesetNumber = -1;
+
+    /**
+     * We cache the changeset here to replace the
+     * changeset-id later.
+     */
+    private StringWriter myChangesetBuffer = new StringWriter();
+
+    /**
+     * Creates a new instance.
+     * The baseURL defaults to the production API.
+     * @param aBaseURL may be null
+     * @param aUserName the user-name to use
+     * @param aPassword the password to use
+     * @param aComment the comment to set in the changeset
+     */
+    public XmlChangeUploader(final String aBaseURL,
+                             final String aUserName,
+                             final String aPassword,
+                             final String aComment) {
+
+        if (aBaseURL == null) {
+            this.myBaseURL = DEFAULTAPIBASEURL;
+        } else {
+            this.myBaseURL = aBaseURL;
+        }
+        if (aUserName == null) {
+            throw new IllegalArgumentException("null username given");
+        }
+        this.myUserName = aUserName;
+        if (aPassword == null) {
+            throw new IllegalArgumentException("null password given");
+        }
+        this.myPassword = aPassword;
+        if (aComment == null) {
+            this.myComment = "";
+        } else {
+            this.myComment = aComment;
+        }
+        this.myPassword = aPassword;
+
+        this.myChangeWriter = new OsmChangeWriter("osmChange", 0);
+    }
+
+    /**
+     * Open the changeset if it is not yet open.
+     * @throws IOException if we cannot contact the server.
+     */
+    protected final void initialize() throws IOException {
+        if (myChangesetNumber == -1) {
+            URL url = new URL(this.myBaseURL + "/changeset/create");
+            System.err.println("DEBUG: URL= " + url.toString());
+            HttpURLConnection httpCon = (HttpURLConnection)
+                                      url.openConnection();
+
+            // we do not use Authenticator.setDefault()
+            // here to stay thread-safe.
+            httpCon.setRequestProperty("Authorization", "Basic "
+                    + Base64.encodeBase64String(
+                            (this.myUserName + ":"
+                           + this.myPassword).getBytes("UTF8")));
+
+            httpCon.setDoOutput(true);
+            httpCon.setRequestMethod("PUT");
+            OutputStreamWriter out = new OutputStreamWriter(
+                httpCon.getOutputStream());
+            out.write("<osm version=\"0.6\" generator=\"Osmosis "
+                    + OsmosisConstants.VERSION + "\">\n"
+                    + "\t<changeset>\n");
+            out.write("\t\t<tag k=\"created_by\" v=\"Osmosis\"/>\n");
+            out.write("\t\t<tag k=\"comment\" v=\""
+                           + this.myComment + "\"/>\n");
+            out.write("\t</changeset>\n</osm>");
+            out.close();
+
+            int responseCode = httpCon.getResponseCode();
+            if (responseCode != HttpURLConnection.HTTP_OK) {
+                InputStreamReader reader = new InputStreamReader(
+                        httpCon.getInputStream());
+                LOG.severe(readAll(reader).toString());
+                throw new IllegalStateException("Http-Status-code is not"
+                        + " 200 OK but " + responseCode
+                        + " \"" + httpCon.getResponseMessage()
+                        + "\" Error=" + httpCon.getHeaderField("Error"));
+            }
+            Reader in = new InputStreamReader(httpCon.getInputStream());
+            char[] buffer = new char[Byte.MAX_VALUE];
+            int len = in.read(buffer);
+            int changeset = Integer.parseInt(new String(buffer, 0, len));
+
+            LOG.info("opened changeset with ID: " + changeset);
+            this.myChangesetNumber = changeset;
+
+            this.myChangeWriter.setWriter(this.myChangesetBuffer);
+            this.myChangeWriter.begin();
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Map<String, Object> metaData) {
+		// Do nothing.
+	}
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    public final void process(final ChangeContainer changeContainer) {
+        try {
+            initialize();
+
+            myChangeWriter.process(changeContainer);
+        } catch (IOException e) {
+            throw new OsmosisRuntimeException(
+                    "Cannot open changeset on server", e);
+        }
+    }
+
+    /**
+     * close the changeset on the server.
+     */
+    public final void complete() {
+        try {
+            myChangeWriter.end();
+            LOG.fine("complete() called");
+            uploadChangeBuffer();
+            closeChangeset();
+        } catch (Exception e) {
+            throw new OsmosisRuntimeException(
+                    "cannot upload or close changeset.", e);
+        }
+    }
+
+    /**
+     * Upload the buffered changes to the server,
+     * replacing the changeset-number.
+     * @throws IOException if we cannot contact the server
+     */
+    private void uploadChangeBuffer() throws IOException {
+        URL url = new URL(this.myBaseURL + "/changeset/"
+                        + this.myChangesetNumber + "/upload");
+        HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
+        httpCon.setDoOutput(true);
+
+        // we do not use Authenticator.setDefault() here to stay thread-safe.
+        httpCon.setRequestProperty("Authorization", "Basic "
+                + Base64.encodeBase64String(
+                        (this.myUserName + ":"
+                       + this.myPassword).getBytes("UTF8")));
+
+        OutputStream out = httpCon.getOutputStream();
+        OutputStreamWriter writer = new OutputStreamWriter(out, "UTF8");
+        writer.flush();
+        String changeSet = this.myChangesetBuffer.getBuffer().toString();
+        System.out.println("changeset we got uploading:\n" + changeSet);
+        String modified = changeSet.replaceAll("changeset=\"[0-9]*\"",
+                                               "changeset=\""
+                                             + this.myChangesetNumber + "\"");
+        System.out.println("changeset we are uploading:\n" + modified);
+        writer.write(modified);
+        writer.close();
+        int responseCode = httpCon.getResponseCode();
+        LOG.fine("response-code to changeset: "
+                + responseCode);
+        if (responseCode != HttpURLConnection.HTTP_OK) {
+//            InputStreamReader reader = new InputStreamReader(
+//                    httpCon.getInputStream());
+//            LOG.severe("response:\n" + readAll(reader).toString());
+            throw new IllegalStateException("Http-Status-code is not"
+                    + " 200 OK but " + responseCode
+                    + " \"" + httpCon.getResponseMessage()
+                    + "\" Error=" + httpCon.getHeaderField("Error"));
+        }
+    }
+
+    /**
+     * Close the changeset on the server,
+     * commiting the change.
+     * @throws IOException if we cannot contact the server
+     */
+    private void closeChangeset() throws IOException {
+        URL url = new URL(this.myBaseURL + "/changeset/"
+                + this.myChangesetNumber + "/close");
+        System.err.println("DEBUG: URL= " + url.toString());
+        HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
+        httpCon.setDoOutput(true);
+        httpCon.setRequestMethod("PUT");
+
+        // we do not use Authenticator.setDefault() here to stay thread-safe.
+        httpCon.setRequestProperty("Authorization", "Basic "
+                + Base64.encodeBase64String(
+                        (this.myUserName + ":"
+                       + this.myPassword).getBytes("UTF8")));
+
+        httpCon.setRequestProperty(
+                "Content-Type", "application/x-www-form-urlencoded");
+        httpCon.connect();
+        int responseCode = httpCon.getResponseCode();
+        LOG.info("response-code to closing of changeset: "
+                + responseCode);
+        this.myChangesetNumber = -1;
+        if (responseCode != HttpURLConnection.HTTP_OK) {
+//            InputStreamReader reader = new InputStreamReader(
+//                       httpCon.getInputStream());
+//            LOG.severe(readAll(reader).toString());
+            throw new IllegalStateException("Http-Status-code is not"
+                    + " 200 OK but " + responseCode
+                    + " \"" + httpCon.getResponseMessage()
+                    + "\" Error=" + httpCon.getHeaderField("Error"));
+        }
+    }
+
+    /**
+     * Read this reader into a String.
+     * @param aReader where to read from
+     * @return the content
+     * @throws IOException if we cannot read
+     */
+    private StringBuilder readAll(final Reader aReader) throws IOException {
+        char[] buffer = new char[Byte.MAX_VALUE];
+        int reat = -1;
+        StringBuilder sb = new StringBuilder();
+        while ((reat = aReader.read(buffer)) >= 0) {
+            sb.append(buffer, 0, reat);
+        }
+        aReader.close();
+        return sb;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final void release() {
+        if (this.myChangesetNumber != -1) {
+            try {
+                LOG.fine("release() called");
+                closeChangeset();
+            } catch (Exception e) {
+                LOG.log(Level.SEVERE, "Cannot close changeset.", e);
+            }
+        }
+    }
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploaderFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploaderFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploaderFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploaderFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java
new file mode 100644
index 0000000..66d4be3
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java
@@ -0,0 +1,84 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.BufferedWriter;
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.xml.common.BaseXmlWriter;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeWriter;
+
+
+/**
+ * An OSM change sink for storing all data to an xml file.
+ *
+ * @author Brett Henderson
+ */
+public class XmlChangeWriter extends BaseXmlWriter implements ChangeSink {
+
+	private OsmChangeWriter osmChangeWriter;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param writer
+	 *            The writer to send all data to.
+	 */
+	public XmlChangeWriter(BufferedWriter writer) {
+		super(writer);
+
+        osmChangeWriter = new OsmChangeWriter("osmChange", 0);
+	}
+	
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 */
+	public XmlChangeWriter(File file, CompressionMethod compressionMethod) {
+    	super(file, compressionMethod);
+
+        osmChangeWriter = new OsmChangeWriter("osmChange", 0);
+    }
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(ChangeContainer changeContainer) {
+		initialize();
+		
+		osmChangeWriter.process(changeContainer);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void beginElementWriter() {
+		osmChangeWriter.begin();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void endElementWriter() {
+		osmChangeWriter.end();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void setWriterOnElementWriter(BufferedWriter writer) {
+		osmChangeWriter.setWriter(writer);
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriterFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriterFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriterFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriterFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java
new file mode 100644
index 0000000..f1f7dd2
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java
@@ -0,0 +1,273 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.core.util.MultiMemberGZIPInputStream;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmHandler;
+import org.openstreetmap.osmosis.xml.v0_6.impl.XmlConstants;
+
+
+/**
+ * An OSM data source reading from an osm-xml file from the
+ * OpenStreetMap-server.
+ *
+ * @author <a href="mailto:Marcus at Wolschon.biz">Marcus Wolschon</a>
+ */
+public class XmlDownloader implements RunnableSource {
+
+    /**
+     * The http-response-code for OK.
+     */
+    private static final int RESPONSECODE_OK = 200;
+
+
+    /**
+     * My logger for debug- and error-output.
+     */
+    private static Logger log = Logger.getLogger(XmlDownloader.class.getName());
+
+
+    /**
+     * The timeout we use for the  HttpURLConnection.
+     */
+    private static final int TIMEOUT = 15000;
+
+
+    /**
+     * Where to deliver the loaded data.
+     */
+    private Sink mySink;
+
+    /**
+     * Left longitude of the bounding box.
+     */
+    private double myLeft;
+
+    /**
+     * Right longitude of the bounding box.
+     */
+    private double myRight;
+
+    /**
+     * Top latitude of the bounding box.
+     */
+    private double myTop;
+
+    /**
+     * Bottom latitude of the bounding box.
+     */
+    private double myBottom;
+
+    /**
+     * The base url of the server.
+     * Defaults to. "http://www.openstreetmap.org/api/0.5".
+     */
+    private String myBaseUrl = XmlConstants.DEFAULT_URL;
+
+    /**
+     * The http connection used to retrieve data.
+     */
+    private HttpURLConnection myActiveConnection;
+
+    /**
+     * The stream providing response data.
+     */
+    private InputStream responseStream;
+
+    /**
+     * Creates a new instance with the specified geographical coordinates.
+     *
+     * @param left
+     *            The longitude marking the left edge of the bounding box.
+     * @param right
+     *            The longitude marking the right edge of the bounding box.
+     * @param top
+     *            The latitude marking the top edge of the bounding box.
+     * @param bottom
+     *            The latitude marking the bottom edge of the bounding box.
+     * @param baseUrl
+     *            (optional) The base url of the server (eg.
+     *            http://www.openstreetmap.org/api/0.5).
+     */
+    public XmlDownloader(final double left,
+                         final double right,
+                         final double top,
+                         final double bottom,
+                         final String baseUrl) {
+        this.myLeft = Math.min(left, right);
+        this.myRight = Math.max(left, right);
+        this.myTop = Math.max(top, bottom);
+        this.myBottom = Math.min(top, bottom);
+        if (baseUrl != null) {
+            this.myBaseUrl = baseUrl;
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setSink(final Sink aSink) {
+        this.mySink = aSink;
+    }
+
+    /**
+     * Cleans up any resources remaining after completion.
+     */
+    private void cleanup() {
+        if (myActiveConnection != null) {
+            try {
+                myActiveConnection.disconnect();
+            } catch (Exception e) {
+                log.log(Level.SEVERE, "Unable to disconnect.", e);
+            }
+            myActiveConnection = null;
+        }
+
+        if (responseStream != null) {
+            try {
+
+                responseStream.close();
+            } catch (IOException e) {
+                log.log(Level.SEVERE, "Unable to close response stream.", e);
+            }
+            responseStream = null;
+        }
+    }
+
+
+    /**
+     * Creates a new SAX parser.
+     *
+     * @return The newly created SAX parser.
+     */
+    private SAXParser createParser() {
+        try {
+            return SAXParserFactory.newInstance().newSAXParser();
+
+        } catch (ParserConfigurationException e) {
+            throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+        } catch (SAXException e) {
+            throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+        }
+    }
+
+
+    /**
+     * Reads all data from the server and send it to the {@link Sink}.
+     */
+    public void run() {
+        try {
+        	mySink.initialize(Collections.<String, Object>emptyMap());
+        	
+            SAXParser parser = createParser();
+            InputStream inputStream =
+            	getInputStream(myBaseUrl + "/map?bbox=" + myLeft + "," + myBottom + "," + myRight + "," + myTop);
+
+            // First send the Bound down the pipeline
+            mySink.process(new BoundContainer(new Bound(myRight, myLeft, myTop, myBottom, myBaseUrl)));
+
+            try {
+                parser.parse(inputStream, new OsmHandler(mySink, true));
+            } finally {
+                inputStream.close();
+                inputStream = null;
+            }
+
+            mySink.complete();
+
+        } catch (SAXParseException e) {
+            throw new OsmosisRuntimeException(
+                    "Unable to parse xml"
+                    + ".  publicId=(" + e.getPublicId()
+                    + "), systemId=(" + e.getSystemId()
+                    + "), lineNumber=" + e.getLineNumber()
+                    + ", columnNumber=" + e.getColumnNumber() + ".",
+                    e);
+        } catch (SAXException e) {
+            throw new OsmosisRuntimeException("Unable to parse XML.", e);
+        } catch (IOException e) {
+            throw new OsmosisRuntimeException("Unable to read XML.", e);
+        } finally {
+            mySink.release();
+
+            cleanup();
+        }
+    }
+
+    /**
+     * Open a connection to the given url and return a reader on the input
+     * stream from that connection.
+     *
+     * @param pUrlStr
+     *            The exact url to connect to.
+     * @return An reader reading the input stream (servers answer) or
+     *         <code>null</code>.
+     * @throws IOException
+     *             on io-errors
+     */
+    private InputStream getInputStream(final String pUrlStr) throws IOException {
+        URL url;
+        int responseCode;
+        String encoding;
+
+        url = new URL(pUrlStr);
+        myActiveConnection = (HttpURLConnection) url.openConnection();
+
+        myActiveConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
+
+        responseCode = myActiveConnection.getResponseCode();
+
+        if (responseCode != RESPONSECODE_OK) {
+            String message;
+            String apiErrorMessage;
+
+            apiErrorMessage = myActiveConnection.getHeaderField("Error");
+
+            if (apiErrorMessage != null) {
+                message = "Received API HTTP response code " + responseCode
+                + " with message \"" + apiErrorMessage
+                + "\" for URL \"" + pUrlStr + "\".";
+            } else {
+                message = "Received API HTTP response code " + responseCode
+                + " for URL \"" + pUrlStr + "\".";
+            }
+
+            throw new OsmosisRuntimeException(message);
+        }
+
+        myActiveConnection.setConnectTimeout(TIMEOUT);
+
+        encoding = myActiveConnection.getContentEncoding();
+
+        responseStream = myActiveConnection.getInputStream();
+        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
+            responseStream = new MultiMemberGZIPInputStream(responseStream);
+        } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
+            responseStream = new InflaterInputStream(responseStream, new Inflater(true));
+        }
+
+        return responseStream;
+    }
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloaderFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloaderFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloaderFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloaderFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java
new file mode 100644
index 0000000..6e24c54
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java
@@ -0,0 +1,140 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.CompressionActivator;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmHandler;
+
+
+/**
+ * An OSM data source reading from an xml file. The entire contents of the file
+ * are read.
+ * 
+ * @author Brett Henderson
+ */
+public class XmlReader implements RunnableSource {
+	
+	private static Logger log = Logger.getLogger(XmlReader.class.getName());
+	
+	private Sink sink;
+	private File file;
+	private boolean enableDateParsing;
+	private CompressionMethod compressionMethod;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to read.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 */
+	public XmlReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
+		this.file = file;
+		this.enableDateParsing = enableDateParsing;
+		this.compressionMethod = compressionMethod;
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setSink(Sink sink) {
+		this.sink = sink;
+	}
+	
+	
+	/**
+	 * Creates a new SAX parser.
+	 * 
+	 * @return The newly created SAX parser.
+	 */
+	private SAXParser createParser() {
+		try {
+			return SAXParserFactory.newInstance().newSAXParser();
+			
+		} catch (ParserConfigurationException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		}
+	}
+	
+	
+	/**
+	 * Reads all data from the file and send it to the sink.
+	 */
+	public void run() {
+		InputStream inputStream = null;
+		
+		try {
+			SAXParser parser;
+			
+			sink.initialize(Collections.<String, Object>emptyMap());
+			
+			// make "-" an alias for /dev/stdin
+			if (file.getName().equals("-")) {
+				inputStream = System.in;
+			} else {
+				inputStream = new FileInputStream(file);
+			}
+			
+			
+			inputStream =
+				new CompressionActivator(compressionMethod).
+					createCompressionInputStream(inputStream);
+			
+			parser = createParser();
+			
+			parser.parse(inputStream, new OsmHandler(sink, enableDateParsing));
+			
+			sink.complete();
+			
+		} catch (SAXParseException e) {
+			throw new OsmosisRuntimeException(
+				"Unable to parse xml file " + file
+				+ ".  publicId=(" + e.getPublicId()
+				+ "), systemId=(" + e.getSystemId()
+				+ "), lineNumber=" + e.getLineNumber()
+				+ ", columnNumber=" + e.getColumnNumber() + ".",
+				e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Unable to parse XML.", e);
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
+		} finally {
+			sink.release();
+			
+			if (inputStream != null) {
+				try {
+					inputStream.close();
+				} catch (IOException e) {
+					log.log(Level.SEVERE, "Unable to close input stream.", e);
+				}
+				inputStream = null;
+			}
+		}
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderFactory.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderFactory.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderFactory.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java
new file mode 100644
index 0000000..25a5550
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java
@@ -0,0 +1,104 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.BufferedWriter;
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseXmlWriter;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.v0_6.impl.OsmWriter;
+
+
+/**
+ * An OSM data sink for storing all data to an xml file.
+ * 
+ * @author Brett Henderson
+ */
+public class XmlWriter extends BaseXmlWriter implements Sink {
+	
+	private OsmWriter osmWriter;
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param writer
+	 *            The writer to send all data to.
+	 */
+	public XmlWriter(BufferedWriter writer) {
+		super(writer);
+		
+		osmWriter = new OsmWriter("osm", 0, true, false);
+	}
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 */
+	public XmlWriter(File file, CompressionMethod compressionMethod) {
+		super(file, compressionMethod);
+		
+		osmWriter = new OsmWriter("osm", 0, true, false);
+	}
+	
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param file
+	 *            The file to write.
+	 * @param compressionMethod
+	 *            Specifies the compression method to employ.
+	 * @param legacyBound
+	 *            If true, write the legacy <bound> element instead of the
+	 *            correct <bounds> one.
+	 */
+	public XmlWriter(File file, CompressionMethod compressionMethod, boolean legacyBound) {
+		super(file, compressionMethod);
+		
+		osmWriter = new OsmWriter("osm", 0, true, legacyBound);
+	}
+
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void process(EntityContainer entityContainer) {
+		initialize();
+		
+		osmWriter.process(entityContainer);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void beginElementWriter() {
+		osmWriter.begin();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void endElementWriter() {
+		osmWriter.end();
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void setWriterOnElementWriter(BufferedWriter writer) {
+		osmWriter.setWriter(writer);
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java
new file mode 100644
index 0000000..c1a2165
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java
@@ -0,0 +1,53 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.File;
+
+import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
+import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
+import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+import org.openstreetmap.osmosis.xml.common.XmlTaskManagerFactory;
+
+
+/**
+ * The task manager factory for an xml writer.
+ * 
+ * @author Brett Henderson
+ */
+public class XmlWriterFactory extends XmlTaskManagerFactory {
+	private static final String ARG_FILE_NAME = "file";
+	private static final String DEFAULT_FILE_NAME = "dump.osm";
+
+	private static final String ARG_LEGACY_BOUND = "useLegacyBound";
+	private static final boolean DEFAULT_LEGACY_BOUND = false;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
+		String fileName;
+		File file;
+		XmlWriter task;
+		CompressionMethod compressionMethod;
+		
+		// Get the task arguments.
+		fileName = getStringArgument(
+			taskConfig,
+			ARG_FILE_NAME,
+			getDefaultStringArgument(taskConfig, DEFAULT_FILE_NAME)
+		);
+		compressionMethod = getCompressionMethodArgument(taskConfig, fileName);
+		
+		// Create a file object from the file name provided.
+		file = new File(fileName);
+		
+		boolean legacyBound = getBooleanArgument(taskConfig, ARG_LEGACY_BOUND, DEFAULT_LEGACY_BOUND);
+		
+		// Build the task object.
+		task = new XmlWriter(file, compressionMethod, legacyBound);
+		
+		return new SinkManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java
new file mode 100644
index 0000000..4bce565
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java
@@ -0,0 +1,92 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.util.Locale;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.xml.common.ElementWriter;
+
+/**
+ * @author KNewman
+ * @author Igor Podolskiy
+ * 
+ */
+public class BoundWriter extends ElementWriter {
+
+	private boolean legacyBound;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 * @param legacyBound
+	 *            If true, write the legacy <bound> element instead of the
+	 *            correct <bounds> one.
+	 */
+	public BoundWriter(String elementName, int indentLevel, boolean legacyBound) {
+		super(elementName, indentLevel);
+		this.legacyBound = legacyBound;
+	}
+
+
+	/**
+	 * Writes the bound.
+	 * 
+	 * @param bound
+	 *            The bound to be processed.
+	 */
+	public void process(Bound bound) {
+		if (legacyBound) {
+			processLegacy(bound);
+		} else {
+			processRegular(bound);
+		}
+	}
+
+
+	private void processRegular(Bound bound) {
+		String format = "%.5f";
+
+		beginOpenElement();
+		
+		addAttribute(XmlConstants.ATTRIBUTE_NAME_MINLON,
+				String.format(Locale.US, format, bound.getLeft()));
+		addAttribute(XmlConstants.ATTRIBUTE_NAME_MINLAT,
+				String.format(Locale.US, format, bound.getBottom()));
+		addAttribute(XmlConstants.ATTRIBUTE_NAME_MAXLON,
+				String.format(Locale.US, format, bound.getRight()));
+		addAttribute(XmlConstants.ATTRIBUTE_NAME_MAXLAT,
+				String.format(Locale.US, format, bound.getTop()));
+		
+		if (bound.getOrigin() != null) {
+			addAttribute("origin", bound.getOrigin());
+		}
+		
+		endOpenElement(true);
+	}
+
+
+	private void processLegacy(Bound bound) {
+		// Only add the Bound if the origin string isn't empty
+		if (!"".equals(bound.getOrigin())) {
+			beginOpenElement();
+			// Write with the US locale (to force . instead of , as the decimal
+			// separator)
+			// Use only 5 decimal places (~1.2 meter resolution should be
+			// sufficient for Bound)
+			addAttribute("box", String.format(
+					Locale.US,
+					"%.5f,%.5f,%.5f,%.5f", 
+					bound.getBottom(), 
+					bound.getLeft(),
+					bound.getTop(), 
+					bound.getRight()));
+			addAttribute("origin", bound.getOrigin());
+			endOpenElement(true);
+		}
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundsElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundsElementProcessor.java
new file mode 100644
index 0000000..819e178
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundsElementProcessor.java
@@ -0,0 +1,88 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import org.xml.sax.Attributes;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
+
+/**
+ * Provides an element processor implementation for a node.
+ * 
+ * @author Karl Newman
+ * @author Igor Podolskiy
+ */
+public class BoundsElementProcessor extends SourceElementProcessor {
+
+	private Bound bound;
+	private String defaultOrigin;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current date will be used
+	 *            thus saving parsing time.
+	 * @param defaultOrigin
+	 * 			  The origin to set on the bound entity if there is no explicit origin defined.
+	 */
+	public BoundsElementProcessor(BaseElementProcessor parentProcessor,
+	        Sink sink,
+	        boolean enableDateParsing,
+	        String defaultOrigin) {
+		super(parentProcessor, sink, enableDateParsing);
+		this.defaultOrigin = defaultOrigin;
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void begin(Attributes attributes) {
+		double bottom = getRequiredDoubleValue(attributes, XmlConstants.ATTRIBUTE_NAME_MINLAT);
+		double left = getRequiredDoubleValue(attributes, XmlConstants.ATTRIBUTE_NAME_MINLON);
+		double top = getRequiredDoubleValue(attributes, XmlConstants.ATTRIBUTE_NAME_MAXLAT);
+		double right = getRequiredDoubleValue(attributes, XmlConstants.ATTRIBUTE_NAME_MAXLON);
+
+		String origin = attributes.getValue(XmlConstants.ATTRIBUTE_NAME_ORIGIN);
+		if (origin == null) {
+			origin = defaultOrigin;
+		}
+		bound = new Bound(right, left, top, bottom, origin);
+	}
+
+	private double getRequiredDoubleValue(Attributes attributes, String attributeName) {
+		String valueString = attributes.getValue(attributeName);
+
+		if (valueString == null) {
+			throw new OsmosisRuntimeException(String.format(
+					"Required attribute %s of the bounds element is missing", attributeName));
+		}
+		try {
+			return Double.parseDouble(valueString);
+		} catch (NumberFormatException e) {
+			throw new OsmosisRuntimeException(
+					String.format("Cannot parse the %s attribute of the bounds element", attributeName), 
+					e);
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void end() {
+		getSink().process(new BoundContainer(bound));
+		bound = null;
+	}
+
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java
new file mode 100644
index 0000000..6aeda0a
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java
@@ -0,0 +1,163 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.util.Map;
+
+import org.xml.sax.Attributes;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
+import org.openstreetmap.osmosis.xml.common.ElementProcessor;
+
+
+/**
+ * Provides an element processor implementation for an osm change element.
+ * 
+ * @author Brett Henderson
+ */
+public class ChangeSourceElementProcessor extends BaseElementProcessor {
+	private static final String ELEMENT_NAME_CREATE = "create";
+	private static final String ELEMENT_NAME_MODIFY = "modify";
+	private static final String ELEMENT_NAME_DELETE = "delete";
+	private static final String ATTRIBUTE_NAME_VERSION = "version";
+	
+	
+	private OsmElementProcessor createElementProcessor;
+	private OsmElementProcessor modifyElementProcessor;
+	private OsmElementProcessor deleteElementProcessor;
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param changeSink
+	 *            The changeSink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 */
+	public ChangeSourceElementProcessor(
+			BaseElementProcessor parentProcessor, ChangeSink changeSink, boolean enableDateParsing) {
+		super(parentProcessor, enableDateParsing);
+		
+		createElementProcessor =
+			new OsmElementProcessor(
+					this, new ChangeSinkAdapter(changeSink, ChangeAction.Create), enableDateParsing, false, true);
+		modifyElementProcessor =
+			new OsmElementProcessor(
+					this, new ChangeSinkAdapter(changeSink, ChangeAction.Modify), enableDateParsing, false, true);
+		deleteElementProcessor =
+			new OsmElementProcessor(
+					this, new ChangeSinkAdapter(changeSink, ChangeAction.Delete), enableDateParsing, false, false);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void begin(Attributes attributes) {
+		String fileVersion;
+		
+		fileVersion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
+		
+		if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
+			System.err.println(
+				"Warning, expected version " + XmlConstants.OSM_VERSION
+				+ " but received " + fileVersion + "."
+			);
+		}
+	}
+	
+	
+	/**
+	 * Retrieves the appropriate child element processor for the newly
+	 * encountered nested element.
+	 * 
+	 * @param uri
+	 *            The element uri.
+	 * @param localName
+	 *            The element localName.
+	 * @param qName
+	 *            The element qName.
+	 * @return The appropriate element processor for the nested element.
+	 */
+	@Override
+	public ElementProcessor getChild(String uri, String localName, String qName) {
+		if (ELEMENT_NAME_CREATE.equals(qName)) {
+			return createElementProcessor;
+		} else if (ELEMENT_NAME_MODIFY.equals(qName)) {
+			return modifyElementProcessor;
+		} else if (ELEMENT_NAME_DELETE.equals(qName)) {
+			return deleteElementProcessor;
+		}
+		
+		return super.getChild(uri, localName, qName);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void end() {
+		// This class produces no data and therefore doesn't need to do anything
+		// when the end of the element is reached.
+	}
+	
+	
+	private static class ChangeSinkAdapter implements Sink {
+		private ChangeSink changeSink;
+		private ChangeAction action;
+		
+		
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param changeSink
+		 *            The changeSink for receiving processed data.
+		 * @param action
+		 *            The action to apply to all data received.
+		 */
+		public ChangeSinkAdapter(ChangeSink changeSink, ChangeAction action) {
+			this.changeSink = changeSink;
+			this.action = action;
+		}
+	    
+	    
+	    /**
+	     * {@inheritDoc}
+	     */
+	    public void initialize(Map<String, Object> metaData) {
+			changeSink.initialize(metaData);
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(EntityContainer entityContainer) {
+			changeSink.process(new ChangeContainer(entityContainer, action));
+		}
+
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void complete() {
+			changeSink.complete();
+		}
+
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void release() {
+			changeSink.release();
+		}
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityElementProcessor.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityWriter.java
new file mode 100644
index 0000000..df20fdf
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/EntityWriter.java
@@ -0,0 +1,65 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.util.Map.Entry;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.xml.common.ElementWriter;
+
+
+/**
+ * Provides common functionality for all classes writing OSM entities to xml.
+ * 
+ * @author Brett Henderson
+ */
+public class EntityWriter extends ElementWriter {
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentionLevel
+	 *            The indent level of the element.
+	 */
+	protected EntityWriter(String elementName, int indentionLevel) {
+		super(elementName, indentionLevel);
+	}
+
+
+	/**
+	 * Add common entity attributes.
+	 * 
+	 * @param entity
+	 *            The entity being written.
+	 */
+	protected void addCommonAttributes(Entity entity) {
+		addAttribute("id", Long.toString(entity.getId()));
+		addAttribute("version", Integer.toString(entity.getVersion()));
+		addAttribute("timestamp", entity.getFormattedTimestamp(getTimestampFormat()));
+
+		OsmUser user = entity.getUser();
+		if (!user.equals(OsmUser.NONE)) {
+			addAttribute("uid", Integer.toString(user.getId()));
+			addAttribute("user", user.getName());
+		}
+
+		if (entity.getChangesetId() != 0) {
+			addAttribute("changeset", Long.toString(entity.getChangesetId()));
+		}
+	}
+
+
+	/**
+	 * Add metatag attributes.
+	 * 
+	 * @param entity
+	 *            The entity being written.
+	 */
+	protected void addMetatags(Entity entity) {
+		for (Entry<String, Object> metaTag : entity.getMetaTags().entrySet()) {
+			addAttribute(metaTag.getKey(), metaTag.getValue().toString());
+		}
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java
new file mode 100644
index 0000000..9a48c04
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java
@@ -0,0 +1,439 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.util.Calendar;
+import java.util.logging.Logger;
+
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampFormat;
+import org.openstreetmap.osmosis.core.domain.common.UnparsedTimestampContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.XmlTimestampFormat;
+
+
+/**
+ * Reads the contents of an osm file using a Stax parser.
+ * 
+ * @author Jiri Klement
+ * @author Brett Henderson
+ * @author Igor Podolskiy
+ */
+public class FastXmlParser {
+	
+	private static final String ELEMENT_NAME_BOUND = "bound";
+	private static final String ELEMENT_NAME_NODE = "node";
+	private static final String ELEMENT_NAME_WAY = "way";
+	private static final String ELEMENT_NAME_RELATION = "relation";
+	private static final String ELEMENT_NAME_TAG = "tag";
+	private static final String ELEMENT_NAME_NODE_REFERENCE = "nd";
+	private static final String ELEMENT_NAME_MEMBER = "member";
+	private static final String ATTRIBUTE_NAME_ID = "id";
+	private static final String ATTRIBUTE_NAME_VERSION = "version";
+	private static final String ATTRIBUTE_NAME_GENERATOR = "generator";
+	private static final String ATTRIBUTE_NAME_TIMESTAMP = "timestamp";
+	private static final String ATTRIBUTE_NAME_USER_ID = "uid";
+	private static final String ATTRIBUTE_NAME_USER = "user";
+	private static final String ATTRIBUTE_NAME_CHANGESET_ID = "changeset";
+	private static final String ATTRIBUTE_NAME_LATITUDE = "lat";
+	private static final String ATTRIBUTE_NAME_LONGITUDE = "lon";
+	private static final String ATTRIBUTE_NAME_KEY = "k";
+	private static final String ATTRIBUTE_NAME_VALUE = "v";
+	private static final String ATTRIBUTE_NAME_REF = "ref";
+	private static final String ATTRIBUTE_NAME_TYPE = "type";
+	private static final String ATTRIBUTE_NAME_ROLE = "role";
+	private static final String ATTRIBUTE_NAME_BOX = "box";
+	private static final String ATTRIBUTE_NAME_ORIGIN = "origin";
+	
+	private static final Logger LOG = Logger.getLogger(FastXmlParser.class.getName());
+	private static final Object ELEMENT_NAME_BOUNDS = "bounds";
+	
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param sink
+	 *            The sink receiving all output data.
+	 * @param reader
+	 *            The input xml reader.
+	 * @param enableDateParsing
+	 *            If true, parsing of dates in the xml will be enabled,
+	 *            otherwise the current system time will be used.
+	 */
+	public FastXmlParser(Sink sink, XMLStreamReader reader, boolean enableDateParsing) {
+		this.sink = sink;
+		this.enableDateParsing = enableDateParsing;
+		this.reader = reader;
+		
+		if (enableDateParsing) {
+			timestampFormat = new XmlTimestampFormat();
+		} else {
+			Calendar calendar;
+			
+			calendar = Calendar.getInstance();
+			calendar.set(Calendar.MILLISECOND, 0);
+			dummyTimestampContainer = new SimpleTimestampContainer(calendar.getTime());
+		}
+		
+		memberTypeParser = new MemberTypeParser();
+	}
+	
+	private final XMLStreamReader reader;
+	private final Sink sink;
+	private final boolean enableDateParsing;
+	private final MemberTypeParser memberTypeParser;
+	private TimestampFormat timestampFormat;
+	private TimestampContainer dummyTimestampContainer;
+
+	
+	private TimestampContainer parseTimestamp(String data) {
+		if (enableDateParsing) {
+			return new UnparsedTimestampContainer(timestampFormat, data);
+		} else {
+			return dummyTimestampContainer;
+		}
+	}
+	
+	private void readUnknownElement() throws XMLStreamException {
+		int level = 0;
+		
+		do {
+			if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
+				level++;
+			} else if (reader.getEventType() == XMLStreamConstants.END_ELEMENT) {
+				level--;
+			}
+			reader.nextTag();
+		} while (level > 0);
+	}
+
+
+	/**
+	 * Creates a user instance based on the current entity attributes. This includes identifying the
+	 * case where no user is available.
+	 * 
+	 * @return The appropriate user instance.
+	 */
+	private OsmUser readUser() {
+		String rawUserId;
+		String rawUserName;
+		
+		rawUserId = reader.getAttributeValue(null, ATTRIBUTE_NAME_USER_ID);
+		rawUserName = reader.getAttributeValue(null, ATTRIBUTE_NAME_USER);
+		
+		if (rawUserId != null) {
+			int userId;
+			String userName;
+			
+			userId = Integer.parseInt(rawUserId);
+			if (rawUserName == null) {
+				userName = "";
+			} else {
+				userName = rawUserName;
+			}
+			
+			return new OsmUser(userId, userName);
+			
+		} else {
+			return OsmUser.NONE;
+		}
+	}
+	
+	
+	/**
+	 * Parses a changeset id from the current entity.
+	 * 
+	 * @return The changeset id as a long. 0 is returned if no attribute is available.
+	 */
+	private long readChangesetId() {
+		String changesetIdAttribute;
+		
+		changesetIdAttribute = reader.getAttributeValue(null, ATTRIBUTE_NAME_CHANGESET_ID);
+		if (changesetIdAttribute != null) {
+			return Long.parseLong(changesetIdAttribute);
+		} else {
+			return 0;
+		}
+	}
+	
+	
+	private Bound readBound() throws Exception {
+		String boxString;
+		String origin;
+		String[] boundStrings;
+		Double right;
+		Double left;
+		Double top;
+		Double bottom;
+		
+		boxString = reader.getAttributeValue(null, ATTRIBUTE_NAME_BOX);
+		
+		if (boxString == null) {
+			throw new OsmosisRuntimeException("Missing required box attribute of bound element");
+		}
+		boundStrings = boxString.split(",");
+		if (boundStrings.length != 4) {
+			throw new OsmosisRuntimeException("Badly formed box attribute of bound element");
+		}
+		try {
+			bottom = Double.parseDouble(boundStrings[0]);
+			left = Double.parseDouble(boundStrings[1]);
+			top = Double.parseDouble(boundStrings[2]);
+			right = Double.parseDouble(boundStrings[3]);
+		} catch (NumberFormatException e) {
+			throw new OsmosisRuntimeException("Can't parse box attribute of bound element", e);
+		}
+		origin = reader.getAttributeValue(null, ATTRIBUTE_NAME_ORIGIN);
+		if (origin == null || origin.equals("")) {
+			throw new OsmosisRuntimeException("Origin attribute of bound element is empty or missing.");
+		}
+		Bound bound = new Bound(right, left, top, bottom, origin);
+		
+		reader.nextTag();
+		reader.nextTag();
+		
+		return bound;
+	}
+	
+	private Bound readBounds(String defaultOrigin) throws Exception {
+		double bottom = getRequiredDoubleValue(XmlConstants.ATTRIBUTE_NAME_MINLAT);
+		double left = getRequiredDoubleValue(XmlConstants.ATTRIBUTE_NAME_MINLON);
+		double top = getRequiredDoubleValue(XmlConstants.ATTRIBUTE_NAME_MAXLAT);
+		double right = getRequiredDoubleValue(XmlConstants.ATTRIBUTE_NAME_MAXLON);
+
+		String origin = reader.getAttributeValue(null, ATTRIBUTE_NAME_ORIGIN);
+		if (origin == null) {
+			origin = defaultOrigin;
+		}
+		
+		reader.nextTag();
+		reader.nextTag();
+
+		return new Bound(right, left, top, bottom, origin);
+	}
+	
+	private double getRequiredDoubleValue(String attributeName) {
+		String valueString = reader.getAttributeValue(null, attributeName);
+
+		if (valueString == null) {
+			throw new OsmosisRuntimeException(String.format(
+					"Required attribute %s of the bounds element is missing", attributeName));
+		}
+		try {
+			return Double.parseDouble(valueString);
+		} catch (NumberFormatException e) {
+			throw new OsmosisRuntimeException(
+					String.format("Cannot parse the %s attribute of the bounds element", attributeName), 
+					e);
+		}
+	}
+	
+	private Tag readTag() throws Exception {
+		Tag tag = new Tag(reader.getAttributeValue(null, ATTRIBUTE_NAME_KEY),
+				reader.getAttributeValue(null, ATTRIBUTE_NAME_VALUE));
+		reader.nextTag();
+		reader.nextTag();
+		return tag;
+	}
+	
+	private Node readNode() throws Exception {
+		long id;
+		int version;
+		TimestampContainer timestamp;
+		OsmUser user;
+		long changesetId;
+		double latitude;
+		double longitude;
+		Node node;
+		
+		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
+		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
+		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
+		changesetId = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_CHANGESET_ID));
+		user = readUser();
+		changesetId = readChangesetId();
+		latitude = Double.parseDouble(reader.getAttributeValue(null, ATTRIBUTE_NAME_LATITUDE));
+		longitude = Double.parseDouble(reader.getAttributeValue(null, ATTRIBUTE_NAME_LONGITUDE));
+		
+		node = new Node(new CommonEntityData(id, version, timestamp, user, changesetId), latitude, longitude);
+		
+		reader.nextTag();
+		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
+			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
+				node.getTags().add(readTag());
+			} else {
+				readUnknownElement();
+			}
+		}
+		
+		reader.nextTag();
+		
+		return node;
+	}
+	
+	private WayNode readWayNode() throws Exception {
+		WayNode node = new WayNode(
+				Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_REF)));
+		reader.nextTag();
+		reader.nextTag();
+		return node;
+	}
+	
+	private Way readWay() throws Exception {
+		long id;
+		int version;
+		TimestampContainer timestamp;
+		OsmUser user;
+		long changesetId;
+		Way way;
+		
+		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
+		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
+		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
+		user = readUser();
+		changesetId = readChangesetId();
+		
+		way = new Way(new CommonEntityData(id, version, timestamp, user, changesetId));
+		
+		reader.nextTag();
+		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
+			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
+				way.getTags().add(readTag());
+			} else if (reader.getLocalName().equals(ELEMENT_NAME_NODE_REFERENCE)) {
+				way.getWayNodes().add(readWayNode());
+			} else {
+				readUnknownElement();
+			}
+		}
+		reader.nextTag();
+
+		return way;
+	}
+	
+	private RelationMember readRelationMember() throws Exception {
+		long id;
+		EntityType type;
+		String role;
+		
+		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_REF));
+		type = memberTypeParser.parse(reader.getAttributeValue(null, ATTRIBUTE_NAME_TYPE));
+		role = reader.getAttributeValue(null, ATTRIBUTE_NAME_ROLE);
+		
+		RelationMember relationMember = new RelationMember(id, type, role);
+		
+		reader.nextTag();
+		reader.nextTag();
+		
+		return relationMember;
+	}
+	
+	private Relation readRelation() throws Exception {
+		long id;
+		int version;
+		TimestampContainer timestamp;
+		OsmUser user;
+		long changesetId;
+		Relation relation;
+		
+		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
+		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
+		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
+		user = readUser();
+		changesetId = readChangesetId();
+		
+		relation = new Relation(new CommonEntityData(id, version, timestamp, user, changesetId));
+		
+		reader.nextTag();
+		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
+			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
+				relation.getTags().add(readTag());
+			} else if (reader.getLocalName().equals(ELEMENT_NAME_MEMBER)) {
+				relation.getMembers().add(readRelationMember());
+			} else {
+				readUnknownElement();
+			}
+		}
+		reader.nextTag();
+		
+		return relation;
+	}
+
+	
+	/**
+	 * Parses the xml and sends all data to the sink.
+	 */
+	public void readOsm() {
+		
+		try {
+		
+			String generator = null;
+			
+			if (reader.nextTag() == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("osm")) {
+
+				String fileVersion;
+
+				fileVersion = reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION);
+
+				if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
+					LOG.warning(
+							"Expected version " + XmlConstants.OSM_VERSION
+							+ " but received " + fileVersion + "."
+					);
+				}
+				
+				generator = reader.getAttributeValue(null, ATTRIBUTE_NAME_GENERATOR);
+
+				reader.nextTag();
+				
+
+				if (reader.getEventType() == XMLStreamConstants.START_ELEMENT
+						&& reader.getLocalName().equals(ELEMENT_NAME_BOUND)) {
+					LOG.fine("Legacy <bound> element encountered.");
+					sink.process(new BoundContainer(readBound()));
+				}
+				
+				if (reader.getEventType() == XMLStreamConstants.START_ELEMENT
+						&& reader.getLocalName().equals(ELEMENT_NAME_BOUNDS)) {
+					sink.process(new BoundContainer(readBounds(generator)));
+				}
+
+				while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {			
+					// Node, way, relation
+					if (reader.getLocalName().equals(ELEMENT_NAME_NODE)) {
+						sink.process(new NodeContainer(readNode()));
+					} else if (reader.getLocalName().equals(ELEMENT_NAME_WAY)) {
+						sink.process(new WayContainer(readWay()));
+					} else if (reader.getLocalName().equals(ELEMENT_NAME_RELATION)) {
+						sink.process(new RelationContainer(readRelation()));
+					} else {
+						readUnknownElement();
+					}
+				}
+
+			} else {
+				throw new XMLStreamException();
+			}
+		} catch (Exception e) {
+			throw new OsmosisRuntimeException(e);
+		}
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/LegacyBoundElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/LegacyBoundElementProcessor.java
new file mode 100644
index 0000000..c31d874
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/LegacyBoundElementProcessor.java
@@ -0,0 +1,89 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import org.xml.sax.Attributes;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
+
+/**
+ * Provides an element processor implementation for a node.
+ * 
+ * @author Karl Newman
+ */
+public class LegacyBoundElementProcessor extends SourceElementProcessor {
+	private static final String ATTRIBUTE_NAME_BOX = "box";
+	private static final String ATTRIBUTE_NAME_ORIGIN = "origin";
+
+	private Bound bound;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current date will be used
+	 *            thus saving parsing time.
+	 */
+	public LegacyBoundElementProcessor(BaseElementProcessor parentProcessor,
+	        Sink sink,
+	        boolean enableDateParsing) {
+		super(parentProcessor, sink, enableDateParsing);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void begin(Attributes attributes) {
+		String boxString;
+		String origin;
+		String[] boundStrings;
+		Double right;
+		Double left;
+		Double top;
+		Double bottom;
+		
+		boxString = attributes.getValue(ATTRIBUTE_NAME_BOX);
+		
+		if (boxString == null) {
+			throw new OsmosisRuntimeException("Missing required box attribute of bound element");
+		}
+		boundStrings = boxString.split(",");
+		if (boundStrings.length != 4) {
+			throw new OsmosisRuntimeException("Badly formed box attribute of bound element");
+		}
+		try {
+			bottom = Double.parseDouble(boundStrings[0]);
+			left = Double.parseDouble(boundStrings[1]);
+			top = Double.parseDouble(boundStrings[2]);
+			right = Double.parseDouble(boundStrings[3]);
+		} catch (NumberFormatException e) {
+			throw new OsmosisRuntimeException("Can't parse box attribute of bound element", e);
+		}
+		origin = attributes.getValue(ATTRIBUTE_NAME_ORIGIN);
+		if (origin == null || origin.equals("")) {
+			throw new OsmosisRuntimeException("Origin attribute of bound element is empty or missing.");
+		}
+		bound = new Bound(right, left, top, bottom, origin);
+	}
+
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void end() {
+		getSink().process(new BoundContainer(bound));
+		bound = null;
+	}
+
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeParser.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeParser.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeParser.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeParser.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeRenderer.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeRenderer.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeRenderer.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/MemberTypeRenderer.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java
new file mode 100644
index 0000000..a56b07d
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java
@@ -0,0 +1,174 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import org.xml.sax.Attributes;
+
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
+import org.openstreetmap.osmosis.xml.common.ElementProcessor;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+
+
+/**
+ * Provides an element processor implementation for a node.
+ * 
+ * @author Brett Henderson
+ */
+public class NodeElementProcessor extends EntityElementProcessor implements TagListener {
+	private static final String ELEMENT_NAME_TAG = "tag";
+	private static final String ATTRIBUTE_NAME_ID = "id";
+	private static final String ATTRIBUTE_NAME_TIMESTAMP = "timestamp";
+	private static final String ATTRIBUTE_NAME_USER = "user";
+	private static final String ATTRIBUTE_NAME_USERID = "uid";
+	private static final String ATTRIBUTE_NAME_CHANGESET_ID = "changeset";
+	private static final String ATTRIBUTE_NAME_VERSION = "version";
+	private static final String ATTRIBUTE_NAME_LATITUDE = "lat";
+	private static final String ATTRIBUTE_NAME_LONGITUDE = "lon";
+	
+	private TagElementProcessor tagElementProcessor;
+	private Node node;
+	private boolean coordinatesRequired;
+	
+	
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 */
+	public NodeElementProcessor(BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing) {
+		this(parentProcessor, sink, enableDateParsing, true);
+	}
+	
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 * @param coordinatesRequired
+	 * 		      If true, nodes without lat and lon attributes set will cause an exception.
+	 */
+	public NodeElementProcessor(BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing, 
+			boolean coordinatesRequired) {
+		super(parentProcessor, sink, enableDateParsing);
+
+		this.coordinatesRequired = coordinatesRequired;
+		tagElementProcessor = new TagElementProcessor(this, this);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void begin(Attributes attributes) {
+		long id;
+		String sversion;
+		int version;
+		TimestampContainer timestampContainer;
+		String rawUserId;
+		String rawUserName;
+		OsmUser user;
+		long changesetId;
+		double latitude;
+		double longitude;
+		
+		id = Long.parseLong(attributes.getValue(ATTRIBUTE_NAME_ID));
+		sversion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
+		if (sversion == null) {
+			throw new OsmosisRuntimeException("Node " + id
+					+ " does not have a version attribute as OSM 0.6 are required to have.  Is this a 0.5 file?");
+		} else {
+			version = Integer.parseInt(sversion);
+		}
+		timestampContainer = createTimestampContainer(attributes.getValue(ATTRIBUTE_NAME_TIMESTAMP));
+		rawUserId = attributes.getValue(ATTRIBUTE_NAME_USERID);
+		rawUserName = attributes.getValue(ATTRIBUTE_NAME_USER);
+		changesetId = buildChangesetId(attributes.getValue(ATTRIBUTE_NAME_CHANGESET_ID));
+		
+		latitude = getLatLonDouble(attributes, ATTRIBUTE_NAME_LATITUDE, id);
+		longitude = getLatLonDouble(attributes, ATTRIBUTE_NAME_LONGITUDE, id);
+		
+		user = buildUser(rawUserId, rawUserName);
+		
+		node = new Node(new CommonEntityData(id, version, timestampContainer, user, changesetId), latitude, longitude);
+	}
+	
+	/**
+	 * Retrieves the appropriate child element processor for the newly
+	 * encountered nested element.
+	 * 
+	 * @param uri
+	 *            The element uri.
+	 * @param localName
+	 *            The element localName.
+	 * @param qName
+	 *            The element qName.
+	 * @return The appropriate element processor for the nested element.
+	 */
+	@Override
+	public ElementProcessor getChild(String uri, String localName, String qName) {
+		if (ELEMENT_NAME_TAG.equals(qName)) {
+			return tagElementProcessor;
+		}
+		
+		return super.getChild(uri, localName, qName);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void end() {
+		getSink().process(new NodeContainer(node));
+	}
+	
+	
+	/**
+	 * This is called by child element processors when a tag object is
+	 * encountered.
+	 * 
+	 * @param tag
+	 *            The tag to be processed.
+	 */
+	public void processTag(Tag tag) {
+		node.getTags().add(tag);
+	}
+	
+	private double getLatLonDouble(Attributes attributes, String attributeName, long id) {
+		String value = attributes.getValue(attributeName);
+		if (value == null) {
+			if (coordinatesRequired) {
+				throw new OsmosisRuntimeException(String.format(
+						"Node %s does not have its %s attribute set; this attribute is required in current context.",
+						id, attributeName));
+			} else {
+				return Double.NaN;
+			}
+		}
+		
+		try {
+			return Double.parseDouble(value);
+		} catch (NumberFormatException ex) {
+			throw new OsmosisRuntimeException(String.format(
+					"Node %s: cannot parse the %s attribute as a numeric value",
+					id, attributeName));
+		}
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java
new file mode 100644
index 0000000..1f04f81
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java
@@ -0,0 +1,97 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.io.Writer;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Collection;
+import java.util.Locale;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+
+
+/**
+ * Renders a node as xml.
+ *
+ * @author Brett Henderson
+ */
+public class NodeWriter extends EntityWriter {
+    /**
+     * Write the tags of a node.
+     */
+   private TagWriter tagWriter;
+   private NumberFormat numberFormat;
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 */
+	public NodeWriter(String elementName, int indentLevel) {
+		super(elementName, indentLevel);
+		
+		tagWriter = new TagWriter("tag", indentLevel + 1);
+		
+		// Only write the first 7 decimal places.
+		// Write in US locale so that a '.' is used as the decimal separator.
+		numberFormat = new DecimalFormat(
+			"0.#######;-0.#######",
+			new DecimalFormatSymbols(Locale.US)
+		);
+	}
+	
+	
+	/**
+	 * Writes the node.
+	 * 
+	 * @param node
+	 *            The node to be processed.
+	 */
+	public void process(Node node) {
+		Collection<Tag> tags;
+		
+		beginOpenElement();
+		addCommonAttributes(node);
+		
+		if (!Double.isNaN(node.getLatitude())) {
+			addAttribute("lat", numberFormat.format(node.getLatitude()));
+		}
+		
+		if (!Double.isNaN(node.getLongitude())) {
+			addAttribute("lon", numberFormat.format(node.getLongitude()));
+		}
+
+		addMetatags(node);
+		
+		tags = node.getTags();
+		
+		if (tags.size() > 0) {
+			endOpenElement(false);
+			
+			for (Tag tag : tags) {
+				tagWriter.process(tag);
+			}
+			
+			closeElement();
+			
+		} else {
+			endOpenElement(true);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setWriter(final Writer writer) {
+		super.setWriter(writer);
+		
+		tagWriter.setWriter(writer);
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeHandler.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeHandler.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeHandler.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeHandler.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java
new file mode 100644
index 0000000..9c492fe
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java
@@ -0,0 +1,153 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.io.Writer;
+
+import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.common.ChangeAction;
+import org.openstreetmap.osmosis.xml.common.ElementWriter;
+
+
+/**
+ * Renders OSM changes as xml.
+ *
+ * @author Brett Henderson
+ */
+public class OsmChangeWriter extends ElementWriter {
+
+    /**
+     * The OsmWriter to use for created elements.
+     */
+    private OsmWriter osmCreateWriter;
+
+    /**
+     * The OsmWriter to use for modified elements.
+     */
+    private OsmWriter osmModifyWriter;
+
+    /**
+     * The OsmWriter to use for deleted elements.
+     */
+    private OsmWriter osmDeleteWriter;
+    /**
+     * @see #updateActiveOsmWriter(ChangeAction)
+     */
+    private OsmWriter activeOsmWriter;
+    /**
+     * The last action (add, modify, delete)
+     * that we processed.
+     */
+    private ChangeAction lastAction;
+
+    /**
+     * Creates a new instance that
+     * starts with an <osmChange> -element
+     * at indent-level 0.
+     */
+    public OsmChangeWriter() {
+       this("osmChange", 0);
+    }
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 */
+	public OsmChangeWriter(final String elementName, final int indentLevel) {
+		super(elementName, indentLevel);
+		
+		osmCreateWriter = new OsmWriter("create", indentLevel + 1, false, false);
+		osmModifyWriter = new OsmWriter("modify", indentLevel + 1, false, false);
+		osmDeleteWriter = new OsmWriter("delete", indentLevel + 1, false, false);
+		activeOsmWriter = null;
+		lastAction = null;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setWriter(final Writer aWriter) {
+		super.setWriter(aWriter);
+
+		this.osmCreateWriter.setWriter(aWriter);
+		this.osmModifyWriter.setWriter(aWriter);
+		this.osmDeleteWriter.setWriter(aWriter);
+	}
+
+	/**
+	 * Begins an <osmchange>-element.
+	 */
+	public void begin() {
+		beginOpenElement();
+		addAttribute("version", XmlConstants.OSM_VERSION);
+		addAttribute("generator", "Osmosis " + OsmosisConstants.VERSION);
+		endOpenElement(false);
+	}
+
+	/**
+	 * Ends an <osmchange>-element.
+	 */
+	public void end() {
+		if (activeOsmWriter != null) {
+			activeOsmWriter.end();
+			activeOsmWriter = null;
+		}
+
+		lastAction = null;
+		closeElement();
+	}
+
+	/**
+	 * Returns the appropriate osm writer for the particular change type.
+	 * 
+	 * @param action
+	 *            The change action to be performed.
+	 * @return The osm writer for the change type.
+	 */
+	private OsmWriter getWriterForAction(final ChangeAction action) {
+		if (action.equals(ChangeAction.Create)) {
+			return osmCreateWriter;
+		} else if (action.equals(ChangeAction.Modify)) {
+			return osmModifyWriter;
+		} else if (action.equals(ChangeAction.Delete)) {
+			return osmDeleteWriter;
+		} else {
+			throw new OsmosisRuntimeException("The change action " + action + " is not recognised.");
+		}
+	}
+
+	/**
+	 * Switch to another type of change.
+	 * @param action the action to apply to the next elements.
+	 */
+	private void updateActiveOsmWriter(final ChangeAction action) {
+		if (action != lastAction) {
+			if (activeOsmWriter != null) {
+				activeOsmWriter.end();
+			}
+
+			activeOsmWriter = getWriterForAction(action);
+
+			activeOsmWriter.begin();
+
+			lastAction = action;
+		}
+	}
+
+	/**
+	 * Writes the change in the container.
+	 * 
+	 * @param changeContainer
+	 *            The container holding the change.
+	 */
+	public void process(final ChangeContainer changeContainer) {
+		updateActiveOsmWriter(changeContainer.getAction());
+		activeOsmWriter.process(changeContainer.getEntityContainer());
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java
new file mode 100644
index 0000000..aa009dd
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java
@@ -0,0 +1,156 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.util.logging.Logger;
+
+import org.xml.sax.Attributes;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.task.v0_6.Sink;
+import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
+import org.openstreetmap.osmosis.xml.common.ElementProcessor;
+
+
+/**
+ * Provides an element processor implementation for an osm element.
+ * 
+ * @author Brett Henderson
+ */
+public class OsmElementProcessor extends SourceElementProcessor {
+	
+	private static final Logger LOG = Logger.getLogger(OsmElementProcessor.class.getName());
+	
+	private static final String ELEMENT_NAME_BOUND_LEGACY = "bound";
+	private static final String ELEMENT_NAME_BOUNDS = "bounds";
+	private static final String ELEMENT_NAME_NODE = "node";
+	private static final String ELEMENT_NAME_WAY = "way";
+	private static final String ELEMENT_NAME_RELATION = "relation";
+	private static final String ATTRIBUTE_NAME_VERSION = "version";
+	private static final String ATTRIBUTE_NAME_GENERATOR = "generator";
+	
+	private NodeElementProcessor nodeElementProcessor;
+	private WayElementProcessor wayElementProcessor;
+	private RelationElementProcessor relationElementProcessor;
+	
+	private boolean foundBound = false;
+	private boolean foundEntities = false;
+	private boolean validateVersion;
+
+	private String generator;
+	
+
+	/**
+	 * Creates a new instance (coordinates are required).
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 * @param validateVersion If true, a version attribute will be checked and validated.
+	 */
+	public OsmElementProcessor(
+			BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing, boolean validateVersion) {
+		this(parentProcessor, sink, enableDateParsing, validateVersion, true);
+	}
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param parentProcessor
+	 *            The parent of this element processor.
+	 * @param sink
+	 *            The sink for receiving processed data.
+	 * @param enableDateParsing
+	 *            If true, dates will be parsed from xml data, else the current
+	 *            date will be used thus saving parsing time.
+	 * @param validateVersion If true, a version attribute will be checked and validated.
+	 * @param coordinatesRequired
+	 *            If true, nodes without lat and lon attributes set will cause an exception.
+	 */
+	public OsmElementProcessor(
+			BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing, boolean validateVersion,
+			boolean coordinatesRequired) {
+		super(parentProcessor, sink, enableDateParsing);
+		
+		this.validateVersion = validateVersion;
+
+		nodeElementProcessor = new NodeElementProcessor(this, getSink(), enableDateParsing, coordinatesRequired);
+		wayElementProcessor = new WayElementProcessor(this, getSink(), enableDateParsing);
+		relationElementProcessor = new RelationElementProcessor(this, getSink(), enableDateParsing);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void begin(Attributes attributes) {
+		if (validateVersion) {
+			String fileVersion;
+			
+			fileVersion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
+			
+			if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
+				LOG.warning(
+					"Expected version " + XmlConstants.OSM_VERSION
+					+ " but received " + fileVersion + "."
+				);
+			}
+		}
+		
+		generator = attributes.getValue(ATTRIBUTE_NAME_GENERATOR);
+	}
+	
+	
+	/**
+	 * Retrieves the appropriate child element processor for the newly
+	 * encountered nested element.
+	 * 
+	 * @param uri
+	 *            The element uri.
+	 * @param localName
+	 *            The element localName.
+	 * @param qName
+	 *            The element qName.
+	 * @return The appropriate element processor for the nested element.
+	 */
+	@Override
+	public ElementProcessor getChild(String uri, String localName, String qName) {
+		if (ELEMENT_NAME_BOUNDS.equals(qName) || ELEMENT_NAME_BOUND_LEGACY.equals(qName)) {
+			if (foundEntities) {
+				throw new OsmosisRuntimeException("Bound element must come before any entities.");
+			}
+			if (foundBound) {
+				throw new OsmosisRuntimeException("Only one bound element allowed.");
+			}
+			foundBound = true;
+			if (ELEMENT_NAME_BOUND_LEGACY.equals(qName)) {
+				LOG.fine("Legacy <bound> element encountered.");
+				return new LegacyBoundElementProcessor(this, getSink(), true);
+			} else {
+				return new BoundsElementProcessor(this, getSink(), true, generator);
+			}
+		} else if (ELEMENT_NAME_NODE.equals(qName)) {
+			foundEntities = true;
+			return nodeElementProcessor;
+		} else if (ELEMENT_NAME_WAY.equals(qName)) {
+			foundEntities = true;
+			return wayElementProcessor;
+		} else if (ELEMENT_NAME_RELATION.equals(qName)) {
+			foundEntities = true;
+			return relationElementProcessor;
+		}
+		
+		return super.getChild(uri, localName, qName);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void end() {
+		// This class produces no data and therefore doesn't need to do anything
+		// when the end of the element is reached.
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandler.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandler.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandler.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandler.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java
new file mode 100644
index 0000000..8d72683
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java
@@ -0,0 +1,192 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.io.Writer;
+
+import org.openstreetmap.osmosis.core.OsmosisConstants;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.xml.common.ElementWriter;
+
+
+/**
+ * Renders OSM data types as xml.
+ *
+ * @author Brett Henderson
+ */
+public class OsmWriter extends ElementWriter {
+
+	private SubElementWriter subElementWriter;
+	private boolean renderAttributes;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 * @param renderAttributes
+	 *            Specifies whether attributes of the top level element should
+	 *            be rendered. This would typically be set to false if this
+	 *            element is embedded within a higher level element (eg.
+	 *            changesets)
+	 * @param legacyBound
+	 *            If true, write the legacy <bound> element instead of the
+	 *            correct <bounds> one.
+	 *        
+	 */
+	public OsmWriter(String elementName, int indentLevel, boolean renderAttributes, boolean legacyBound) {
+		super(elementName, indentLevel);
+		
+		this.renderAttributes = renderAttributes;
+		
+		// Create the sub-element writer which calls the appropriate element
+		// writer based on data type.
+		subElementWriter = new SubElementWriter(indentLevel + 1, legacyBound);
+	}
+	
+	
+	/**
+	 * Begins an element.
+	 */
+	public void begin() {
+		beginOpenElement();
+		
+		if (renderAttributes) {
+			addAttribute("version", XmlConstants.OSM_VERSION);
+			addAttribute("generator", "Osmosis " + OsmosisConstants.VERSION);
+		}
+		
+		endOpenElement(false);
+	}
+	
+	
+	/**
+	 * Ends an element.
+	 */
+	public void end() {
+		closeElement();
+	}
+	
+	
+	/**
+	 * Writes the element in the container.
+	 * 
+	 * @param entityContainer
+	 *            The container holding the entity.
+	 */
+	public void process(EntityContainer entityContainer) {
+		entityContainer.process(subElementWriter);
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setWriter(final Writer writer) {
+		super.setWriter(writer);
+		
+		// Tell the sub element writer that a new writer is available. This will
+		// cause the underlying entity writing classes to be updated.
+		subElementWriter.updateWriter(writer);
+	}
+	
+	
+	/**
+	 * Directs data to the appropriate underlying element writer.
+	 * 
+	 * @author Brett Henderson
+	 */
+	private static class SubElementWriter implements EntityProcessor {
+		private NodeWriter nodeWriter;
+		private WayWriter wayWriter;
+		private RelationWriter relationWriter;
+		private BoundWriter boundWriter;
+		private boolean boundWritten = false; // can't write a Bound twice
+		private boolean entitiesWritten = false; // can't write a Bound after any Entities
+		
+		/**
+		 * Creates a new instance.
+		 * 
+		 * @param indentLevel
+		 *            The indent level of the sub-elements.
+		 */
+		public SubElementWriter(int indentLevel, boolean legacyBound) {
+			nodeWriter = new NodeWriter("node", indentLevel);
+			wayWriter = new WayWriter("way", indentLevel);
+			relationWriter = new RelationWriter("relation", indentLevel);
+			if (legacyBound) {
+				boundWriter = new BoundWriter("bound", indentLevel, legacyBound);
+			} else {
+				boundWriter = new BoundWriter("bounds", indentLevel, legacyBound);
+			}
+		}
+		
+		
+		/**
+		 * Updates the underlying writer.
+		 * 
+		 * @param writer
+		 *            The writer to be used for all output xml.
+		 */
+		public void updateWriter(final Writer writer) {
+			nodeWriter.setWriter(writer);
+			wayWriter.setWriter(writer);
+			relationWriter.setWriter(writer);
+			boundWriter.setWriter(writer);
+			// reset the flags indicating which data has been written
+			boundWritten = false;
+			entitiesWritten = false;
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(NodeContainer node) {
+			nodeWriter.process(node.getEntity());
+			entitiesWritten = true;
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(WayContainer way) {
+			wayWriter.process(way.getEntity());
+			entitiesWritten = true;
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+		public void process(RelationContainer relation) {
+			relationWriter.process(relation.getEntity());
+			entitiesWritten = true;
+		}
+		
+		
+		/**
+		 * {@inheritDoc}
+		 */
+        public void process(BoundContainer bound) {
+    		if (boundWritten) {
+    			throw new OsmosisRuntimeException("Bound element already written and only one allowed.");
+    		}
+    		if (entitiesWritten) {
+    			throw new OsmosisRuntimeException("Can't write bound element after other entities.");    			
+    		}
+        	boundWriter.process(bound.getEntity());
+    		boundWritten = true;
+        }
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberListener.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberListener.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberListener.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberListener.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberWriter.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberWriter.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationMemberWriter.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java
new file mode 100644
index 0000000..111a5c8
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+
+
+/**
+ * Renders a relation as xml.
+ *
+ * @author Brett Henderson
+ */
+public class RelationWriter extends EntityWriter {
+    /**
+     * Write the ordered list of members of a relation.
+     */
+    private RelationMemberWriter relationMemberWriter;
+    /**
+     * Write the tags of a relation.
+     */
+    private TagWriter tagWriter;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 */
+	public RelationWriter(String elementName, int indentLevel) {
+		super(elementName, indentLevel);
+		
+		tagWriter = new TagWriter("tag", indentLevel + 1);
+		relationMemberWriter = new RelationMemberWriter("member", indentLevel + 1);
+	}
+	
+	
+	/**
+	 * Writes the relation.
+	 * 
+	 * @param relation
+	 *            The relation to be processed.
+	 */
+	public void process(Relation relation) {
+		List<RelationMember> relationMembers;
+		Collection<Tag> tags;
+		
+		beginOpenElement();
+		addCommonAttributes(relation);
+		addMetatags(relation);
+		
+		relationMembers = relation.getMembers();
+		tags = relation.getTags();
+		
+		if (relationMembers.size() > 0 || tags.size() > 0) {
+			endOpenElement(false);
+
+			for (RelationMember relationMember : relationMembers) {
+				relationMemberWriter.processRelationMember(relationMember);
+			}
+			
+			for (Tag tag : tags) {
+				tagWriter.process(tag);
+			}
+			
+			closeElement();
+			
+		} else {
+			endOpenElement(true);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setWriter(final Writer writer) {
+		super.setWriter(writer);
+		
+		relationMemberWriter.setWriter(writer);
+		tagWriter.setWriter(writer);
+	}
+}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/SourceElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/SourceElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/SourceElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/SourceElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagListener.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagListener.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagListener.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagListener.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagWriter.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagWriter.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/TagWriter.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeElementProcessor.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeElementProcessor.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeElementProcessor.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeElementProcessor.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeListener.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeListener.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeListener.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeListener.java
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeWriter.java
similarity index 100%
rename from xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeWriter.java
rename to osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayNodeWriter.java
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java
new file mode 100644
index 0000000..4c206a1
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+
+
+/**
+ * Renders a way as xml.
+ *
+ * @author Brett Henderson
+ */
+public class WayWriter extends EntityWriter {
+    /**
+     * Write the ordered list of node-references of a way.
+     */
+    private WayNodeWriter wayNodeWriter;
+    /**
+     * Write the tags of a way.
+     */
+    private TagWriter tagWriter;
+
+
+	/**
+	 * Creates a new instance.
+	 * 
+	 * @param elementName
+	 *            The name of the element to be written.
+	 * @param indentLevel
+	 *            The indent level of the element.
+	 */
+	public WayWriter(String elementName, int indentLevel) {
+		super(elementName, indentLevel);
+		
+		tagWriter = new TagWriter("tag", indentLevel + 1);
+		wayNodeWriter = new WayNodeWriter("nd", indentLevel + 1);
+	}
+	
+	
+	/**
+	 * Writes the way.
+	 * 
+	 * @param way
+	 *            The way to be processed.
+	 */
+	public void process(Way way) {
+		List<WayNode> wayNodes;
+		Collection<Tag> tags;
+		
+		beginOpenElement();
+		addCommonAttributes(way);
+		addMetatags(way);
+		
+		wayNodes = way.getWayNodes();
+		tags = way.getTags();
+		
+		if (wayNodes.size() > 0 || tags.size() > 0) {
+			endOpenElement(false);
+
+			for (WayNode wayNode : wayNodes) {
+				wayNodeWriter.processWayNode(wayNode);
+			}
+			
+			for (Tag tag : tags) {
+				tagWriter.process(tag);
+			}
+			
+			closeElement();
+			
+		} else {
+			endOpenElement(true);
+		}
+	}
+	
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void setWriter(final Writer writer) {
+		super.setWriter(writer);
+		
+		wayNodeWriter.setWriter(writer);
+		tagWriter.setWriter(writer);
+	}
+}
diff --git a/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java
new file mode 100644
index 0000000..1de1e51
--- /dev/null
+++ b/osmosis-xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java
@@ -0,0 +1,56 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+
+/**
+ * Defines some common constants shared between various xml processing classes.
+ * 
+ * @author Brett Henderson
+ */
+public final class XmlConstants {
+	
+	/**
+	 * The origin attribute.
+	 */
+	public static final String ATTRIBUTE_NAME_ORIGIN = "origin";
+
+	/**
+	 * The minlat attribute.
+	 */
+	public static final String ATTRIBUTE_NAME_MINLAT = "minlat";
+
+	/**
+	 * The maxlat attribute.
+	 */
+	public static final String ATTRIBUTE_NAME_MAXLAT = "maxlat";
+
+	/**
+	 * The minlon attribute.
+	 */
+	public static final String ATTRIBUTE_NAME_MINLON = "minlon";
+
+	/**
+	 * The maxlon attribute.
+	 */
+	public static final String ATTRIBUTE_NAME_MAXLON = "maxlon";
+
+
+	/**
+	 * This class cannot be instantiated.
+	 */
+	private XmlConstants() {
+	}
+	
+	
+	/**
+	 * Defines the version number to be stored in osm xml files. This number
+	 * will also be applied to osmChange files.
+	 */
+	public static final String OSM_VERSION = "0.6";
+	
+	
+	/**
+	 * The default URL for the production API.
+	 */
+	public static final String DEFAULT_URL = "http://www.openstreetmap.org/api/0.6";
+}
diff --git a/xml/src/main/resources/osmosis-plugins.conf b/osmosis-xml/src/main/resources/osmosis-plugins.conf
similarity index 100%
rename from xml/src/main/resources/osmosis-plugins.conf
rename to osmosis-xml/src/main/resources/osmosis-plugins.conf
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/common/ElementWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/common/ElementWriterTest.java
similarity index 100%
rename from xml/src/test/java/org/openstreetmap/osmosis/xml/common/ElementWriterTest.java
rename to osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/common/ElementWriterTest.java
diff --git a/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java
new file mode 100644
index 0000000..db8dcb8
--- /dev/null
+++ b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java
@@ -0,0 +1,91 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.misc.v0_6.NullChangeWriter;
+import org.openstreetmap.osmosis.testutil.AbstractDataTest;
+import org.openstreetmap.osmosis.xml.common.CompressionMethod;
+
+
+/**
+ * A simple test verifying the operation of the xml change reader and change
+ * writer tasks.
+ * 
+ * @author Brett Henderson
+ */
+public class XmlChangeReaderWriterTest extends AbstractDataTest {
+	
+	/**
+	 * A basic test reading and writing an osm file testing both reader and
+	 * writer tasks.
+	 * 
+	 * @throws IOException
+	 *             if any file operations fail.
+	 */
+	@Test
+	public void testSimple() throws IOException {
+		XmlChangeReader xmlReader;
+		XmlChangeWriter xmlWriter;
+		File inputFile;
+		File outputFile;
+		
+		inputFile = dataUtils.createDataFile("v0_6/xml-task-tests-v0_6.osc");
+		outputFile = dataUtils.newFile();
+		
+		// Create and connect the xml tasks.
+		xmlReader = new XmlChangeReader(inputFile, true, CompressionMethod.None);
+		xmlWriter = new XmlChangeWriter(outputFile, CompressionMethod.None);
+		xmlReader.setChangeSink(xmlWriter);
+		
+		// Process the xml.
+		xmlReader.run();
+		
+		// Validate that the output file matches the input file.
+		dataUtils.compareFiles(inputFile, outputFile);
+	}
+	
+	/**
+	 * Tests acceptance of nodes in a delete change with lat/lon attribute not set.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test
+	public void testDeleteLatLonNotSet() throws Exception {
+		XmlChangeReader xmlReader;
+		XmlChangeWriter xmlWriter;
+		File inputFile;
+		File outputFile;
+		
+		inputFile = dataUtils.createDataFile("v0_6/xml-delete-no-coordinates.osc");
+		outputFile = dataUtils.newFile();
+		
+		// Create and connect the xml tasks.
+		xmlReader = new XmlChangeReader(inputFile, true, CompressionMethod.None);
+		xmlWriter = new XmlChangeWriter(outputFile, CompressionMethod.None);
+		xmlReader.setChangeSink(xmlWriter);
+		
+		// Process the xml.
+		xmlReader.run();
+		
+		// Validate that the output file matches the input file.
+		dataUtils.compareFiles(inputFile, outputFile);
+	}
+	
+	/**
+	 * Tests non-acceptance of nodes in a non-delete change with lat/lon attribute not set.
+	 * 
+	 * @throws Exception if something goes wrong.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public void testNonDeleteLatLonNotSet() throws Exception {
+		File inputFile = dataUtils.createDataFile("v0_6/xml-create-no-coordinates.osc");
+		XmlChangeReader reader = new XmlChangeReader(inputFile, false, CompressionMethod.None);
+		reader.setChangeSink(new NullChangeWriter());
+		reader.run();
+	}
+	
+}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderWriterTest.java
similarity index 100%
rename from xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderWriterTest.java
rename to osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlReaderWriterTest.java
diff --git a/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java
new file mode 100644
index 0000000..182c6a9
--- /dev/null
+++ b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java
@@ -0,0 +1,88 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+
+/**
+ * Tests the Bound class implementation.
+ */
+public class BoundWriterTest {
+
+	private StringWriter testWriter;
+	private BufferedWriter testBufferedWriter;
+
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		testWriter = new StringWriter();
+		testBufferedWriter = new BufferedWriter(testWriter);
+	}
+
+
+	/**
+	 * Performs post-test activities.
+	 * 
+	 * @throws IOException
+	 *             if stream cleanup fails.
+	 */
+	@After
+	public void tearDown() throws IOException {
+		testBufferedWriter.close();
+		testWriter.close();
+	}
+
+
+	/**
+	 * Test writing out a normal Bound element. 
+	 */
+	@Test
+	public final void testProcess1() {
+		BoundWriter bw = new BoundWriter("bound", 2, true);
+		bw.setWriter(testBufferedWriter);
+		bw.process(new Bound(20.123456, -21.987654, 22.555555, -23.234567, "originstring"));
+		try {
+			testBufferedWriter.flush();
+		} catch (IOException e) {
+			e.printStackTrace();
+			fail("IOException");
+		}
+		// If this test fails, it could be because the regex has broken. There are a number of
+		// variations which are valid XML which this regex won't catch. It might need any number of
+		// \\s* to account for variable whitespace.
+		String regexMatch = "^\\s*<bound\\s*"
+		        + "box=['\"]-23.23457,-21.98765,22.55556,20.12346['\"]\\s*"
+		        + "origin=['\"]originstring['\"]/>\\s*$";
+		assertTrue(testWriter.toString().matches(regexMatch));
+	}
+
+	
+	/**
+	 * Test non-writing of a Bound element with an empty origin string. 
+	 */
+	@Test
+	public final void testProcess2() {
+		BoundWriter bw = new BoundWriter("bound", 2, true);
+		bw.setWriter(testBufferedWriter);
+		bw.process(new Bound(20.123456, -21.987654, 22.555555, -23.234567, ""));
+		try {
+			testBufferedWriter.flush();
+		} catch (IOException e) {
+			e.printStackTrace();
+			fail("IOException");
+		}
+		assertTrue(testWriter.toString().equals("")); // not written; empty string
+	}
+}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriterTest.java
similarity index 100%
rename from xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriterTest.java
rename to osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriterTest.java
diff --git a/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java
new file mode 100644
index 0000000..157a2d1
--- /dev/null
+++ b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java
@@ -0,0 +1,281 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.testutil.v0_6.SinkEntityInspector;
+
+
+/**
+ * Not sure how to go about unit testing this. The individual parser classes seem to require a lot
+ * of infrastructure, so this test will just set up the full parser to parse an XML string and check
+ * the produced entities.
+ * 
+ * @author Karl Newman
+ * 
+ */
+public class OsmHandlerTest {
+
+	private SAXParser parser;
+	private SinkEntityInspector entityInspector;
+	private static final String OSM_PREFIX = "<osm version=\"0.6\">\n";
+	private static final String OSM_SUFFIX = "</osm>";
+
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		entityInspector = new SinkEntityInspector();
+		try {
+			parser = SAXParserFactory.newInstance().newSAXParser();
+		} catch (ParserConfigurationException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
+		}
+	}
+
+
+	private void parseString(String input) {
+		InputStream inputStream = null;
+		try {
+			inputStream = new ByteArrayInputStream(input.getBytes("UTF-8"));
+			parser.parse(inputStream, new OsmHandler(entityInspector, true));
+		} catch (UnsupportedEncodingException e) {
+			throw new OsmosisRuntimeException("String encoding exception", e);
+		} catch (SAXException e) {
+			throw new OsmosisRuntimeException("Parse exception", e);
+		} catch (IOException e) {
+			throw new OsmosisRuntimeException("IOException", e);
+		} finally {
+			try {
+				if (inputStream != null) {
+					inputStream.close();
+				}
+			} catch (IOException e) {
+				throw new OsmosisRuntimeException("IOException", e);
+			} finally {
+				inputStream = null;
+			}
+		}
+	}
+
+
+	/**
+	 * Tests that an empty xml document can be parsed successfully.
+	 */
+	@Test
+	public final void testEmptyDocument() {
+		parseString(OSM_PREFIX + OSM_SUFFIX);
+		assertNull(entityInspector.getLastEntityContainer());
+	}
+
+
+	/**
+	 * Test a normal, well-formed bound element.
+	 */
+	@Test
+	public final void testBoundElement1() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		Bound b = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertTrue(Double.compare(b.getRight(), 45.67891) == 0
+		        && Double.compare(b.getLeft(), -23.45678) == 0
+		        && Double.compare(b.getTop(), 34.56789) == 0
+		        && Double.compare(b.getBottom(), -12.34567) == 0);
+		assertTrue(b.getOrigin().equals("someorigin"));
+	}
+
+
+	/**
+	 * Test a malformed box attribute for a bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement2() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12.34567,-23.45678,34.56789\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a missing box attribute of a bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement3() {
+		parseString(OSM_PREFIX + "<bound origin=\"someorigin\"/>" + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a number parse error for a box attribute of a bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement4() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12..34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a missing origin attribute of a bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement5() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test an empty origin attribute of a bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement6() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a repeated bound element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement7() {
+		parseString(OSM_PREFIX
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someotherorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a bound element occurring after a node element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement8() {
+		parseString(OSM_PREFIX
+		        + "<node id=\"12345\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
+		        + "timestamp=\"2008-01-01T15:32:01\" lat=\"-12.34567\" lon=\"-23.45678\"/>"
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a bound element occurring after a way element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement9() {
+		parseString(OSM_PREFIX
+		        + "<way id=\"12346\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
+		        + "timestamp=\"2008-01-01T15:32:01\">"
+		        + "<nd ref=\"12345\"/>"
+		        + "<nd ref=\"12347\"/>"
+		        + "</way>"
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+
+
+	/**
+	 * Test a bound element occurring after a relation element.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testBoundElement10() {
+		parseString(OSM_PREFIX
+		        + "<relation id=\"12348\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
+		        + "timestamp=\"2008-01-01T15:32:01\">"
+		        + "<member ref=\"12345\" type=\"node\" role=\"node1\"/>"
+		        + "<member ref=\"12346\" type=\"way\" role=\"way1\"/>"
+		        + "</relation>"
+		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
+		        + " origin=\"someorigin\"/>"
+		        + OSM_SUFFIX);
+		fail("Expected to throw an exception");
+	}
+	
+	/**
+	 * Test the parsing of a bound element as returned by the OSM API
+	 * without an origin.
+	 */
+	@Test
+	public void testBoundsNoOrigin() {
+		parseString(OSM_PREFIX
+				+ "<bounds minlat=\"-1.234\" minlon=\"-1.234\" maxlat=\"1.234\" maxlon=\"1.234\"/>"
+				+ OSM_SUFFIX);
+		Bound b = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertEquals(-1.234, b.getLeft(), 1E-6);
+		assertEquals(-1.234, b.getBottom(), 1E-6);
+		assertEquals(1.234, b.getRight(), 1E-6);
+		assertEquals(1.234, b.getTop(), 1E-6);
+		assertNull(b.getOrigin());
+	}
+	
+	/**
+	 * Test the parsing of a bound element as returned by the OSM API
+	 * with an origin.
+	 */
+	@Test
+	public void testBoundsWithOrigin() {
+		parseString(OSM_PREFIX
+				+ "<bounds minlat=\"-1\" minlon=\"-1\" maxlat=\"1\" maxlon=\"1\" " 
+				+ " origin=\"someorigin\"/>"
+				+ OSM_SUFFIX);
+		Bound b = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertEquals("someorigin", b.getOrigin());
+	}
+	
+	/**
+	 * Test the inheritance of generator to the origin.
+	 */
+	@Test
+	public void testBoundsOriginInheritance() {
+		parseString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+					+ "<osm version=\"0.6\" generator=\"somegenerator\">"
+					+ "<bounds minlat=\"-1.234\" minlon=\"-1.234\" maxlat=\"1.234\" maxlon=\"1.234\"/>"
+					+ "</osm>");
+		Bound b = (Bound) entityInspector.getLastEntityContainer().getEntity();
+		assertEquals("somegenerator", b.getOrigin());
+	}
+}
diff --git a/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java
new file mode 100644
index 0000000..3b7dacc
--- /dev/null
+++ b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java
@@ -0,0 +1,183 @@
+// This software is released into the Public Domain.  See copying.txt for details.
+package org.openstreetmap.osmosis.xml.v0_6.impl;
+
+import static org.junit.Assert.fail;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
+import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
+import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
+import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
+import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
+import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
+import org.openstreetmap.osmosis.core.domain.v0_6.Node;
+import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
+import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
+import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
+import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
+import org.openstreetmap.osmosis.core.domain.v0_6.Way;
+import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
+
+
+/**
+ * Tests the XML osm element writer implementation.
+ */
+public class OsmWriterTest {
+
+	private StringWriter testWriter;
+	private BufferedWriter testBufferedWriter;
+	private OsmWriter testOsmWriter;
+
+
+	/**
+	 * Performs pre-test activities.
+	 */
+	@Before
+	public void setUp() {
+		testWriter = new StringWriter();
+		testBufferedWriter = new BufferedWriter(testWriter);
+		testOsmWriter = new OsmWriter("osm", 0, true, true);
+		testOsmWriter.setWriter(testBufferedWriter);
+	}
+
+
+	/**
+	 * Performs post-test activities.
+	 * 
+	 * @throws IOException
+	 *             if IO stream cleanup fails.
+	 */
+	@After
+	public void tearDown() throws IOException {
+		testBufferedWriter.close();
+		testWriter.close();
+		testOsmWriter = null;
+	}
+
+
+	/**
+	 * Test processing a single Bound entity.
+	 */
+	@Test
+	public final void testProcess1() {
+		testOsmWriter.process(new BoundContainer(new Bound("source")));
+		// Nothing to assert; just expect no exception
+	}
+
+
+	/**
+	 * Test processing a repeated Bound entity.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testProcess2() {
+		testOsmWriter.process(new BoundContainer(new Bound("source")));
+		testOsmWriter.process(new BoundContainer(new Bound("source2")));
+		fail("Expected to throw an exception.");
+	}
+
+
+	/**
+	 * Test processing a Node entity.
+	 */
+	@Test
+	public final void testProcess3() {
+		testOsmWriter.process(
+				new NodeContainer(
+					new Node(
+						new CommonEntityData(
+								1234, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0, new ArrayList<Tag>()),
+						20, 20)));
+		// Nothing to assert; just expect no exception
+	}
+
+
+	/**
+	 * Test processing a Bound after a Node.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testProcess4() {
+		testOsmWriter.process(new NodeContainer(
+				new Node(
+						new CommonEntityData(1234, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0,
+								new ArrayList<Tag>()),
+						20, 20)));
+		testOsmWriter.process(new BoundContainer(new Bound("source")));
+		fail("Expected to throw an exception.");
+	}
+
+
+	/**
+	 * Test processing a Way.
+	 */
+	@Test
+	public final void testProcess6() {
+		Way testWay;
+		
+		testWay = new Way(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
+		testWay.getWayNodes().add(new WayNode(1234));
+		testWay.getWayNodes().add(new WayNode(1235));
+		testWay.getTags().add(new Tag("test_key1", "test_value1"));
+		
+		testOsmWriter.process(new WayContainer(testWay));
+		// Nothing to assert; just expect no exception
+	}
+
+
+	/**
+	 * Test processing a Bound after a Way.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testProcess7() {
+		Way testWay;
+		
+		testWay = new Way(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
+		testWay.getWayNodes().add(new WayNode(1234));
+		testWay.getWayNodes().add(new WayNode(1235));
+		testWay.getTags().add(new Tag("test_key1", "test_value1"));
+		
+		testOsmWriter.process(new WayContainer(testWay));
+		testOsmWriter.process(new BoundContainer(new Bound("source")));
+	}
+
+
+	/**
+	 * Test processing a Relation.
+	 */
+	@Test
+	public final void testProcess8() {
+		Relation testRelation;
+		
+		testRelation = new Relation(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
+		testRelation.getMembers().add(new RelationMember(1234, EntityType.Node, "role1"));
+		testRelation.getTags().add(new Tag("test_key1", "test_value1"));
+		
+		testOsmWriter.process(new RelationContainer(testRelation));
+		// Nothing to assert; just expect no exception
+	}
+
+	
+	/**
+	 * Test processing a Bound after a Relation.
+	 */
+	@Test(expected = OsmosisRuntimeException.class)
+	public final void testProcess9() {
+		Relation testRelation;
+		
+		testRelation = new Relation(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
+		testRelation.getMembers().add(new RelationMember(1234, EntityType.Node, "role1"));
+		testRelation.getTags().add(new Tag("test_key1", "test_value1"));
+		
+		testOsmWriter.process(new RelationContainer(testRelation));
+		testOsmWriter.process(new BoundContainer(new Bound("source")));
+	}
+}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriterTest.java
similarity index 100%
rename from xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriterTest.java
rename to osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriterTest.java
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriterTest.java b/osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriterTest.java
similarity index 100%
rename from xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriterTest.java
rename to osmosis-xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriterTest.java
diff --git a/osmosis-xml/src/test/resources/data/template/v0_6/xml-create-no-coordinates.osc b/osmosis-xml/src/test/resources/data/template/v0_6/xml-create-no-coordinates.osc
new file mode 100644
index 0000000..b708263
--- /dev/null
+++ b/osmosis-xml/src/test/resources/data/template/v0_6/xml-create-no-coordinates.osc
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <create>
+    <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10/>
+  </create>
+</osmChange>
diff --git a/osmosis-xml/src/test/resources/data/template/v0_6/xml-delete-no-coordinates.osc b/osmosis-xml/src/test/resources/data/template/v0_6/xml-delete-no-coordinates.osc
new file mode 100644
index 0000000..e63e435
--- /dev/null
+++ b/osmosis-xml/src/test/resources/data/template/v0_6/xml-delete-no-coordinates.osc
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osmChange version="0.6" generator="Osmosis %VERSION%">
+  <delete>
+    <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10"/>
+  </delete>
+</osmChange>
diff --git a/xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osc b/osmosis-xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osc
similarity index 100%
rename from xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osc
rename to osmosis-xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osc
diff --git a/xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osm b/osmosis-xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osm
similarity index 100%
rename from xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osm
rename to osmosis-xml/src/test/resources/data/template/v0_6/xml-task-tests-v0_6.osm
diff --git a/package/.gitignore b/package/.gitignore
index 6006e52..f90761b 100644
--- a/package/.gitignore
+++ b/package/.gitignore
@@ -1,4 +1,5 @@
+.project
+.settings
 /build
-/distrib
 /lib
-/report
+
diff --git a/package/.project b/package/.project
deleted file mode 100644
index 4767799..0000000
--- a/package/.project
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Package</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-	</buildSpec>
-	<natures>
-	</natures>
-</projectDescription>
diff --git a/package/bin/osmosis b/package/bin/osmosis
index 2ef3ef2..22bf268 100755
--- a/package/bin/osmosis
+++ b/package/bin/osmosis
@@ -73,7 +73,7 @@ osmosis --read-xml file="planetin.osm" --write-xml file="planetout.osm"
 
 osmosis --read-xml file="planetin.osm" outPipe.0="mypipe" --write-xml file="planetout.osm" inPipe.0="mypipe"
 
-Full usage details are available at: http://wiki.openstreetmap.org/wiki/Osmosis/DetailedUsage 
+Full usage details are available at: http://wiki.openstreetmap.org/wiki/Osmosis/Detailed_Usage
 
 EOF
 exit 1
diff --git a/package/build.gradle b/package/build.gradle
new file mode 100644
index 0000000..76c74fa
--- /dev/null
+++ b/package/build.gradle
@@ -0,0 +1,73 @@
+configurations {
+    runtime
+    archives
+}
+
+dependencies {
+    runtime project(':osmosis-apidb')
+    runtime project(':osmosis-areafilter')
+    runtime project(':osmosis-core')
+    runtime project(':osmosis-dataset')
+    runtime project(':osmosis-extract')
+    runtime project(':osmosis-pbf')
+    runtime project(':osmosis-pbf2')
+    runtime project(':osmosis-pgsimple')
+    runtime project(':osmosis-pgsnapshot')
+    runtime project(':osmosis-replication')
+    runtime project(':osmosis-replication-http')
+    runtime project(':osmosis-set')
+    runtime project(':osmosis-tagfilter')
+    runtime project(':osmosis-tagtransform')
+    runtime project(':osmosis-xml')
+    runtime group: 'org.codehaus.plexus', name: 'plexus-classworlds', version: dependencyVersionClassworlds
+}
+
+task syncLibs(type: Sync) {
+    into "lib/default"
+    from configurations.runtime
+}
+
+def artefactPrefix = 'osmosis-' + version
+
+task distZip(type: Zip) {
+    description = 'Builds a zip file containing a self-contained distribution of the application.'
+    archiveName = artefactPrefix + '.zip'
+    destinationDir = new File(projectDir, 'build/distribution')
+    from('.') {
+    	exclude 'build*'
+    	exclude 'ivy.xml'
+    	exclude '.*'
+    }
+}
+distZip.dependsOn syncLibs
+
+task distTgz(type: Tar) {
+    description = 'Builds a tgz file containing a self-contained distribution of the application.'
+    archiveName = artefactPrefix + '.tgz'
+    destinationDir = new File(projectDir, 'build/distribution')
+    compression = Compression.GZIP
+    from('.') {
+    	exclude 'build*'
+    	exclude 'ivy.xml'
+    	exclude '.*'
+    }
+}
+distTgz.dependsOn syncLibs
+
+task assemble {
+    description = 'Generates a working application directory structure.'
+}
+assemble.dependsOn distZip, distTgz
+
+task build {
+	description = 'Assembles and tests this project.'
+}
+build.dependsOn assemble
+
+task clean(type: Delete) {
+    delete 'build'
+}
+
+artifacts {
+    archives distZip, distTgz
+}
diff --git a/package/build.xml b/package/build.xml
deleted file mode 100644
index 1dd3ed5..0000000
--- a/package/build.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Package" default="all" basedir=".">
-	
-	<!-- Include common build components. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-ivy.xml"/>
-	
-	<target name="build" depends="resolve" description="Builds the distribution package.">
-		<!-- Create the distribution archives. -->
-		<mkdir dir="distrib/zips"/>
-		<zip destfile="distrib/zips/osmosis-${project.version}.zip">
-			<zipfileset prefix="osmosis-${project.version}" dir=".">
-				<exclude name="distrib/"/>
-				<exclude name="lib/compile/"/>
-				<exclude name="lib/runtime/"/>
-				<exclude name="lib/test/"/>
-				<exclude name="report/"/>
-				<exclude name="build*.xml"/>
-				<exclude name="ivy.xml"/>
-				<exclude name=".*"/>
-			</zipfileset>
-		</zip>
-		<mkdir dir="distrib/tgzs"/>
-		<tar destfile="distrib/tgzs/osmosis-${project.version}.tgz" compression="gzip" longfile="gnu">
-			<tarfileset prefix="osmosis-${project.version}" dir=".">
-				<exclude name="distrib/"/>
-				<exclude name="lib/compile/"/>
-				<exclude name="lib/runtime/"/>
-				<exclude name="lib/test/"/>
-				<exclude name="report/"/>
-				<exclude name="build*.xml"/>
-				<exclude name="ivy.xml"/>
-				<exclude name=".*"/>
-				<exclude name="bin/*"/>
-			</tarfileset>
-			<tarfileset prefix="osmosis-${project.version}" dir="." mode="755">
-				<include name="bin/*"/>
-			</tarfileset>
-		</tar>
-	</target>
-	
-	<target name="dist" depends="build"/>
-	
-	<!-- Executes all major build targets. -->
-	<target name="all" depends="publish"/>
-	
-	<target name="clean" description="Clean up the project tree.">
-		<!-- Delete generated directory trees. -->
-		<delete dir="build"/>
-		<delete dir="distrib"/>
-		<delete dir="lib"/>
-		<delete dir="report"/>
-	</target>
-</project>
diff --git a/package/changes.txt b/package/changes.txt
index 02d33ba..bd4329b 100644
--- a/package/changes.txt
+++ b/package/changes.txt
@@ -1,3 +1,62 @@
+0.43.1
+Fix the version number displayed on startup to be up to date.
+Remove RELEASE suffix from version number of release builds.
+
+0.43
+Update command line help to point to the correct wiki location.
+Remove idTrackerType arguments to filtering tasks, only the Dynamic option is now available.
+Fix the --write-apidb task to use 64-bit ids.
+Upgrade to version 1.4 of the Gradle build tool.
+Enhance the build scripts to support publishing to Sonatype OSS Repository and Maven Central.
+Rename all projects to use an "osmosis-" prefix.
+Included a copy of the PBF OSM Binary project in the source tree, and eliminated the pre-compiled osmbin.jar.
+Remove pbfmarshall project due to the same functionality being provided by the osm-binary project.
+Removed the internal Ivy repository due to all dependencies now being available on Maven Central.
+Rename --read-empty short name --re to --rem to avoid clash with --report-entity.
+Rename --read-empty-change short name --rec to --remc for consistency with --read-empty.
+
+0.42
+Fix PostgreSQL timestamp bugs in apidb replication logic.
+Fix replication file merging boundary corrections.  Occurs when catching up after outages.
+Replication logic correctly honours the max timestamp parameter.
+Prevent replication file downloader from reading beyond maximum available replication interval.
+Prevent replication file downloader from stalling if interval is too long.
+Improve error reporting when an unknown global option is specified.
+Disable automatic state.txt creation for --read-replication-interval.
+Add --tag-transform plugin and task.
+Reduce number of file handles consumed by file-based sorting.
+Make the default id tracker Dynamic for --used-node and --used-way.
+Use Gradle for the automated build scripts instead of Ant/Ivy.
+Fix PostgreSQL ident authentication.
+Remove obsolete debian build scripts.
+Eliminate use of deprecated Spring SimpleJdbcTemplate.
+Improve handling of invalid geometries in --pgsql-xxx tasks.
+Default keepInvalidWays option on --pgsql-xxx tasks to true.
+Enable keepInvalidWays functionality for --pgsql-xxx replication.
+Fix pgsnapshot COPY load script to use ST_ prefix for all PostGIS functions.
+
+0.41
+All entities can now pass metadata through the pipeline.  XML tasks can add these as additional attributes on output.
+Added --convert-change-to-full-history task.
+Added "initialize" support to all tasks to allow pre-processing setup to occur.
+Added a number of unit tests to increase test coverage, and reduced use of duplicated support classes.
+Added support for the XML bounds element, but still support old bound element.
+Added configurable log message prefix to the output of --log-progress-* tasks.
+Added completion step timings for --log-progress-* tasks.
+Added continuous replication looping support to apidb replication functionality.
+Added --write-replication task.
+Enhanced  --replicate-apidb to send output through the pipeline to tasks like --write-replication instead of being an all-in-one task.
+Added --send-replication-sequence task which notifies listeners when new replication data has been written.
+Added --send-replication-data task which provides streaming replication data to listeners.
+Added --receive-replication task which consumes data sent by --serve-replication-data.
+Added --replication-to-change task which converts a replication pipeline stream to a normal change stream.
+Added keyValueListFile option to --node-key-value and --way-key-value tasks.
+Added JaCoCo code coverage tool to the build process.
+Updated ant scripts to use build, checkstyle, test and all targets across all projects.
+Update pgsnapshot and pgsimple tasks to no longer use PostGIS deprecated function names.
+Added --read-pbf-fast PBF reader which utilises multi-threading to improve performance.
+Enhanced XML tasks to handle missing lat/lon attributes on deleted entities.
+
 0.40.1
 Fix the classworlds version in the Windows launcher.
 
diff --git a/package/copying.txt b/package/copying.txt
index f051124..4e6d150 100644
--- a/package/copying.txt
+++ b/package/copying.txt
@@ -1,5 +1,6 @@
 Osmosis consists of all files in this archive with the exception of the
-third party libraries in the lib and repo subdirectories.
+third party libraries in the lib sub-directory, and the osmosis-osm-binary
+library which is a re-packaged version of a third-party library.
 
 Osmosis is placed into the public domain and where this is not legally
 possible everybody is granted a perpetual, irrevocable license to use
diff --git a/package/ivy.xml b/package/ivy.xml
deleted file mode 100644
index 50bdc7a..0000000
--- a/package/ivy.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-package"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="osmosis" type="zip" ext="zip" conf="master"/>
-    	<artifact name="osmosis" type="tgz" ext="tgz" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-apidb" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-areafilter" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-pbf" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-dataset" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-extract" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-pgsimple" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-pgsnapshot" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-replication" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-set" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-tagfilter" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="runtime->default" changing="true"/>
-    	
-    	<dependency org="org.codehaus.plexus" name="plexus-classworlds" rev="${dependency.version.classworlds}" conf="runtime->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/package/script/contrib/apidb_0.6.sql b/package/script/contrib/apidb_0.6.sql
index 6739b71..ef88ab8 100644
--- a/package/script/contrib/apidb_0.6.sql
+++ b/package/script/contrib/apidb_0.6.sql
@@ -2466,7 +2466,8 @@ CREATE TABLE nodes (
     visible boolean NOT NULL,
     "timestamp" timestamp without time zone NOT NULL,
     tile bigint NOT NULL,
-    version bigint NOT NULL
+    version bigint NOT NULL,
+    redaction_id integer
 );
 
 
@@ -2582,7 +2583,8 @@ CREATE TABLE relations (
     changeset_id bigint NOT NULL,
     "timestamp" timestamp without time zone NOT NULL,
     version bigint NOT NULL,
-    visible boolean DEFAULT true NOT NULL
+    visible boolean DEFAULT true NOT NULL,
+    redaction_id integer
 );
 
 
@@ -2822,7 +2824,8 @@ CREATE TABLE ways (
     changeset_id bigint NOT NULL,
     "timestamp" timestamp without time zone NOT NULL,
     version bigint NOT NULL,
-    visible boolean DEFAULT true NOT NULL
+    visible boolean DEFAULT true NOT NULL,
+    redaction_id integer
 );
 
 
diff --git a/package/script/pgsimple_load_0.6.sql b/package/script/pgsimple_load_0.6.sql
index 11fbec5..2358efe 100644
--- a/package/script/pgsimple_load_0.6.sql
+++ b/package/script/pgsimple_load_0.6.sql
@@ -46,7 +46,7 @@ SELECT AddGeometryColumn('ways', 'linestring', 4326, 'GEOMETRY', 2);
 -- Comment these out if the COPY files include bbox or linestring column values.
 -- Update the bbox column of the way table.
 UPDATE ways SET bbox = (
-	SELECT Envelope(Collect(geom))
+	SELECT ST_Envelope(ST_Collect(geom))
 	FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id
 	WHERE way_nodes.way_id = ways.id
 );
diff --git a/package/script/pgsnapshot_load_0.6.sql b/package/script/pgsnapshot_load_0.6.sql
index 34bf01b..5480738 100644
--- a/package/script/pgsnapshot_load_0.6.sql
+++ b/package/script/pgsnapshot_load_0.6.sql
@@ -39,7 +39,7 @@ SELECT AddGeometryColumn('ways', 'linestring', 4326, 'GEOMETRY', 2);
 -- Comment these out if the COPY files include bbox or linestring column values.
 -- Update the bbox column of the way table.
 UPDATE ways SET bbox = (
-	SELECT Envelope(Collect(geom))
+	SELECT ST_Envelope(ST_Collect(geom))
 	FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id
 	WHERE way_nodes.way_id = ways.id
 );
diff --git a/package/script/pgsnapshot_schema_0.6.sql b/package/script/pgsnapshot_schema_0.6.sql
index cda35a3..595d160 100644
--- a/package/script/pgsnapshot_schema_0.6.sql
+++ b/package/script/pgsnapshot_schema_0.6.sql
@@ -1,4 +1,4 @@
--- Database creation script for the simple PostgreSQL schema.
+-- Database creation script for the snapshot PostgreSQL schema.
 
 -- Drop all tables if they exist.
 DROP TABLE IF EXISTS actions;
diff --git a/pbf/.checkstyle b/pbf/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/pbf/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/pbf/.classpath b/pbf/.classpath
deleted file mode 100644
index 26f9568..0000000
--- a/pbf/.classpath
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/osmpbf-1.1.1-754a33af.jar"/>
-	<classpathentry kind="lib" path="lib/test/protobuf-java-2.4.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/pbf/.gitignore b/pbf/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/pbf/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/pbf/.project b/pbf/.project
deleted file mode 100644
index 79baea0..0000000
--- a/pbf/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Pbf</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/pbf/build.xml b/pbf/build.xml
deleted file mode 100644
index c6d5e66..0000000
--- a/pbf/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Pbf" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/pbf/ivy.xml b/pbf/ivy.xml
deleted file mode 100644
index 720c556..0000000
--- a/pbf/ivy.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-pbf"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="com.google.protobuf" name="protobuf-java" rev="${dependency.version.protobuf}" conf="compile->default"/>
-    	<dependency org="crosby" name="osmpbf" rev="${dependency.version.osmpbf}" conf="compile->default" changing="true"/>
-        
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java b/pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java
deleted file mode 100644
index d593685..0000000
--- a/pbf/src/main/java/crosby/binary/osmosis/OsmosisBinaryParser.java
+++ /dev/null
@@ -1,261 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package crosby.binary.osmosis;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-import crosby.binary.BinaryParser;
-import crosby.binary.Osmformat;
-import crosby.binary.Osmformat.DenseInfo;
-
-/** Class that reads and parses binary files and sends the contained entities to the sink. */
-public class OsmosisBinaryParser extends BinaryParser {
-
-    @Override
-    public void complete() {
-        sink.complete();
-        sink.release();
-    }
-
-    /** Get the osmosis object representing a the user in a given Info protobuf.
-     * @param info The info protobuf.
-     * @return The OsmUser object */
-    OsmUser getUser(Osmformat.Info info) {
-        // System.out.println(info);
-        if (info.hasUid() && info.hasUserSid()) {
-            if (info.getUid() < 0) {
-              return OsmUser.NONE;
-            }
-            return new OsmUser(info.getUid(), getStringById(info.getUserSid()));
-        } else {
-            return OsmUser.NONE;
-        }
-    }
-
-    /** The magic number used to indicate no version number metadata for this entity. */
-    static final int NOVERSION = -1;
-    /** The magic number used to indicate no changeset metadata for this entity. */
-    static final int NOCHANGESET = -1;
-
-    @Override
-    protected void parseNodes(List<Osmformat.Node> nodes) {
-      for (Osmformat.Node i : nodes) {
-        List<Tag> tags = new ArrayList<Tag>();
-        for (int j = 0; j < i.getKeysCount(); j++) {
-          tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
-        }
-        // long id, int version, Date timestamp, OsmUser user,
-        // long changesetId, Collection<Tag> tags,
-        // double latitude, double longitude
-        Node tmp;
-        long id = i.getId();
-        double latf = parseLat(i.getLat()), lonf = parseLon(i.getLon());
-
-        if (i.hasInfo()) {
-          Osmformat.Info info = i.getInfo();
-          tmp = new Node(new CommonEntityData(id, info.getVersion(), getDate(info),
-              getUser(info), info.getChangeset(), tags), latf, lonf);
-        } else {
-          tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
-              NOCHANGESET, tags), latf, lonf);
-        }
-        sink.process(new NodeContainer(tmp));
-
-      }
-    }
-    
-    @Override
-    protected void parseDense(Osmformat.DenseNodes nodes) {
-        long lastId = 0, lastLat = 0, lastLon = 0;
-        
-        int j = 0; // Index into the keysvals array.
-
-        // Stuff for dense info
-        long lasttimestamp = 0, lastchangeset = 0;
-        int lastuserSid = 0, lastuid = 0;
-        DenseInfo di = null;
-        if (nodes.hasDenseinfo()) {
-          di = nodes.getDenseinfo();
-        }
-        for (int i = 0; i < nodes.getIdCount(); i++) {
-            Node tmp;
-            List<Tag> tags = new ArrayList<Tag>(0);
-            long lat = nodes.getLat(i) + lastLat;
-            lastLat = lat;
-            long lon = nodes.getLon(i) + lastLon;
-            lastLon = lon;
-            long id = nodes.getId(i) + lastId;
-            lastId = id;
-            double latf = parseLat(lat), lonf = parseLon(lon);
-            // If empty, assume that nothing here has keys or vals.
-            if (nodes.getKeysValsCount() > 0) {
-                while (nodes.getKeysVals(j) != 0) {
-                    int keyid = nodes.getKeysVals(j++);
-                    int valid = nodes.getKeysVals(j++);
-                    tags.add(new Tag(getStringById(keyid), getStringById(valid)));
-                }
-                j++; // Skip over the '0' delimiter.
-            }
-            // Handle dense info.
-            if (di != null) {
-              int uid = di.getUid(i) + lastuid; lastuid = uid;
-              int userSid = di.getUserSid(i) + lastuserSid; lastuserSid = userSid;
-              long timestamp = di.getTimestamp(i) + lasttimestamp; lasttimestamp = timestamp;
-              int version = di.getVersion(i); 
-              long changeset = di.getChangeset(i) + lastchangeset; lastchangeset = changeset;
-
-              Date date = new Date(date_granularity * timestamp);
-
-              OsmUser user;
-              if (uid < 0) {
-                user = OsmUser.NONE;
-              } else {
-                user = new OsmUser(uid, getStringById(userSid));
-              }
-              tmp = new Node(new CommonEntityData(id, version, date, user, changeset, tags), latf, lonf);
-            } else {
-                tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
-                        NOCHANGESET, tags), latf, lonf);
-            }
-            sink.process(new NodeContainer(tmp));
-        }
-    }
-
-    @Override
-    protected void parseWays(List<Osmformat.Way> ways) {
-        for (Osmformat.Way i : ways) {
-            List<Tag> tags = new ArrayList<Tag>();
-            for (int j = 0; j < i.getKeysCount(); j++) {
-                tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
-            }
-                
-            long lastId = 0;
-            List<WayNode> nodes = new ArrayList<WayNode>();
-            for (long j : i.getRefsList()) {
-                nodes.add(new WayNode(j + lastId));
-                lastId = j + lastId;
-            }
-
-            long id = i.getId();
-
-            // long id, int version, Date timestamp, OsmUser user,
-            // long changesetId, Collection<Tag> tags,
-            // List<WayNode> wayNodes
-            Way tmp;
-            if (i.hasInfo()) {
-                Osmformat.Info info = i.getInfo();
-                tmp = new Way(new CommonEntityData(id, info.getVersion(), getDate(info),
-                        getUser(info), info.getChangeset(), tags), nodes);
-            } else {
-                tmp = new Way(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE, NOCHANGESET,
-                        tags), nodes);
-            }
-            sink.process(new WayContainer(tmp));
-        }
-    }
-
-    @Override
-    protected void parseRelations(List<Osmformat.Relation> rels) {
-        for (Osmformat.Relation i : rels) {
-            List<Tag> tags = new ArrayList<Tag>();
-            for (int j = 0; j < i.getKeysCount(); j++) {
-                tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
-            }
-
-            long id = i.getId();
-
-            long lastMid = 0;
-            List<RelationMember> nodes = new ArrayList<RelationMember>();
-            for (int j = 0; j < i.getMemidsCount(); j++) {
-                long mid = lastMid + i.getMemids(j);
-                lastMid = mid;
-                String role = getStringById(i.getRolesSid(j));
-                EntityType etype = null;
-
-                if (i.getTypes(j) == Osmformat.Relation.MemberType.NODE) {
-                    etype = EntityType.Node;
-                } else if (i.getTypes(j) == Osmformat.Relation.MemberType.WAY) {
-                    etype = EntityType.Way;
-                } else if (i.getTypes(j) == Osmformat.Relation.MemberType.RELATION) {
-                    etype = EntityType.Relation;
-                } else {
-                    assert false; // TODO; Illegal file?
-                }
-
-                nodes.add(new RelationMember(mid, etype, role));
-            }
-            // long id, int version, TimestampContainer timestampContainer,
-            // OsmUser user,
-            // long changesetId, Collection<Tag> tags,
-            // List<RelationMember> members
-            Relation tmp;
-            if (i.hasInfo()) {
-                Osmformat.Info info = i.getInfo();
-                tmp = new Relation(new CommonEntityData(id, info.getVersion(), getDate(info),
-                        getUser(info), info.getChangeset(), tags), nodes);
-            } else {
-                tmp = new Relation(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
-                        NOCHANGESET, tags), nodes);
-            }
-            sink.process(new RelationContainer(tmp));
-        }
-    }
-
-    @Override
-    public void parse(Osmformat.HeaderBlock block) {
-        for (String s : block.getRequiredFeaturesList()) {
-            if (s.equals("OsmSchema-V0.6")) {
-              continue; // We can parse this.
-            }
-            if (s.equals("DenseNodes")) {
-              continue; // We can parse this.
-            }
-           throw new OsmosisRuntimeException("File requires unknown feature: " + s);
-        }
-        
-        if (block.hasBbox()) {
-            String source = OsmosisConstants.VERSION;
-            if (block.hasSource()) {
-                source = block.getSource();
-            }
-
-            double multiplier = .000000001;
-            double rightf = block.getBbox().getRight() * multiplier;
-            double leftf = block.getBbox().getLeft() * multiplier;
-            double topf = block.getBbox().getTop() * multiplier;
-            double bottomf = block.getBbox().getBottom() * multiplier;
-
-            Bound bounds = new Bound(rightf, leftf, topf, bottomf, source);
-            sink.process(new BoundContainer(bounds));
-        }
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    public void setSink(Sink sink) {
-       this.sink = sink;
-    }
-
-    private Sink sink;
-}
diff --git a/pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java b/pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java
deleted file mode 100644
index 94f7136..0000000
--- a/pbf/src/main/java/crosby/binary/osmosis/OsmosisReader.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package crosby.binary.osmosis;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-import crosby.binary.file.BlockInputStream;
-
-/** Glue code that implements a task that connects an InputStream a containing binary-format data to a Sink. 
- * @author crosby
- *
- */
-public class OsmosisReader implements RunnableSource {
-    /**
-     * Make a reader based on a target input stream. 
-     * @param input The input stream to read from. 
-     */
-    public OsmosisReader(InputStream input) {
-        if (input == null) {
-            throw new Error("Null input");
-        }
-        this.input = input;
-        parser = new OsmosisBinaryParser();
-    }
-
-    @Override
-    public void setSink(Sink sink) {
-        parser.setSink(sink);
-    }
-
-    @Override
-    public void run() {
-        try {
-            (new BlockInputStream(input, parser)).process();
-        } catch (IOException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-    }
-    /** Store the input stream we're using. */
-    InputStream input;
-    /** The binary parser object. */
-    OsmosisBinaryParser parser;
-}
diff --git a/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java b/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java
deleted file mode 100644
index 4348e22..0000000
--- a/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializer.java
+++ /dev/null
@@ -1,500 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package crosby.binary.osmosis;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-import crosby.binary.BinarySerializer;
-import crosby.binary.Osmformat;
-import crosby.binary.Osmformat.DenseInfo;
-import crosby.binary.StringTable;
-import crosby.binary.Osmformat.Relation.MemberType;
-import crosby.binary.file.BlockOutputStream;
-import crosby.binary.file.FileBlock;
-
-/**
- * Receives data from the Osmosis pipeline and stores it in the PBF format.
- */
-public class OsmosisSerializer extends BinarySerializer implements Sink {
-	private static final Logger LOG = Logger.getLogger(OsmosisSerializer.class.getName());
-	
-  /** Additional configuration flag for whether to serialize into DenseNodes/DenseInfo? */
-  protected boolean useDense = true;
-
-  /** Has the header been written yet? */
-  protected boolean headerWritten = false;
-  
-  /**
-   * Tracks the number of warnings that have occurred during serialisation.
-   */
-  static int warncount = 0;
-
-	/**
-	 * Construct a serializer that writes to the target BlockOutputStream.
-	 * 
-	 * @param output
-	 *            The PBF block stream to send serialized data.
-	 */
-  public OsmosisSerializer(BlockOutputStream output) {
-	  super(output);
-  }
-
-  /**
-	 * Change the flag of whether to use the dense format.
-	 * 
-	 * @param useDense
-	 *            The new use dense value.
-	 */
-  public void setUseDense(boolean useDense) {
-    this.useDense = useDense;
-  }
-
-  /** Base class containing common code needed for serializing each type of primitives. */
-    private abstract class Prim<T extends Entity> {
-      /** Queue that tracks the list of all primitives. */
-      ArrayList<T> contents = new ArrayList<T>();
-
-      /** Add to the queue.
-       * @param item The entity to add */
-        public void add(T item) {
-            contents.add(item);
-        }
-
-        /** Add all of the tags of all entities in the queue to the stringtable. */
-        public void addStringsToStringtable() {
-            StringTable stable = getStringTable();
-            for (T i : contents) {
-                Collection<Tag> tags = i.getTags();
-                for (Tag tag : tags) {
-                    stable.incr(tag.getKey());
-                    stable.incr(tag.getValue());
-                }
-                if (!omit_metadata) {
-                    stable.incr(i.getUser().getName());
-                }
-            }
-        }
-        private static final int MAXWARN = 100;
-        public void serializeMetadataDense(DenseInfo.Builder b, List<? extends Entity> entities) {
-			if (omit_metadata) {
-				return;
-			}
-
-			long lasttimestamp = 0, lastchangeset = 0;
-			int lastuserSid = 0, lastuid = 0;
-			StringTable stable = getStringTable();
-			for (Entity e : entities) {
-
-            if (e.getUser() == OsmUser.NONE && warncount  < MAXWARN) {
-              LOG.warning("Attention: Data being output lacks metadata. Please use omitmetadata=true");
-              warncount++;
-            }
-				int uid = e.getUser().getId();
-				int userSid = stable.getIndex(e.getUser().getName());
-				int timestamp = (int) (e.getTimestamp().getTime() / date_granularity);
-				int version = e.getVersion();
-				long changeset = e.getChangesetId();
-
-				b.addVersion(version);
-				b.addTimestamp(timestamp - lasttimestamp);
-				lasttimestamp = timestamp;
-				b.addChangeset(changeset - lastchangeset);
-				lastchangeset = changeset;
-				b.addUid(uid - lastuid);
-				lastuid = uid;
-				b.addUserSid(userSid - lastuserSid);
-				lastuserSid = userSid;
-			}
-        }
-         
-        public Osmformat.Info.Builder serializeMetadata(Entity e) {
-            StringTable stable = getStringTable();
-            Osmformat.Info.Builder b = Osmformat.Info.newBuilder();
-            if (!omit_metadata) {
-                if (e.getUser() == OsmUser.NONE && warncount  < MAXWARN) {
-                  LOG.warning("Attention: Data being output lacks metadata. Please use omitmetadata=true");
-                  warncount++;
-                }
-                if (e.getUser() != OsmUser.NONE) {
-                    b.setUid(e.getUser().getId());
-                    b.setUserSid(stable.getIndex(e.getUser().getName()));
-                }
-                b.setTimestamp((int) (e.getTimestamp().getTime() / date_granularity));
-                b.setVersion(e.getVersion());
-                b.setChangeset(e.getChangesetId());
-            }
-            return b;
-        }
-    }
-
-    private class NodeGroup extends Prim<Node> implements PrimGroupWriterInterface {
-
-      public Osmformat.PrimitiveGroup serialize() {
-          if (useDense) {
-            return serializeDense();
-          } else {
-            return serializeNonDense();
-          }
-      }
-        
-        /**
-         *  Serialize all nodes in the 'dense' format.
-         */
-        public Osmformat.PrimitiveGroup serializeDense() {
-            if (contents.size() == 0) {
-              return null;
-            }
-            // System.out.format("%d Dense   ",nodes.size());
-            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
-                    .newBuilder();
-            StringTable stable = getStringTable();
-
-            long lastlat = 0, lastlon = 0, lastid = 0;
-            Osmformat.DenseNodes.Builder bi = Osmformat.DenseNodes.newBuilder();
-            boolean doesBlockHaveTags = false;
-            // Does anything in this block have tags?
-            for (Node i : contents) {
-              doesBlockHaveTags = doesBlockHaveTags || (!i.getTags().isEmpty());
-            }
-            if (!omit_metadata) {
-              Osmformat.DenseInfo.Builder bdi = Osmformat.DenseInfo.newBuilder();
-              serializeMetadataDense(bdi, contents);
-              bi.setDenseinfo(bdi);
-            }
-              
-              for (Node i : contents) {
-                long id = i.getId();
-                int lat = mapDegrees(i.getLatitude());
-                int lon = mapDegrees(i.getLongitude());
-                bi.addId(id - lastid);
-                lastid = id;
-                bi.addLon(lon - lastlon);
-                lastlon = lon;
-                bi.addLat(lat - lastlat);
-                lastlat = lat;
-
-                // Then we must include tag information.
-                if (doesBlockHaveTags) {
-                  for (Tag t : i.getTags()) {
-                      bi.addKeysVals(stable.getIndex(t.getKey()));
-                      bi.addKeysVals(stable.getIndex(t.getValue()));
-                  }
-                  bi.addKeysVals(0); // Add delimiter.
-                }
-            }
-            builder.setDense(bi);
-            return builder.build();
-        }
-        
-        /**
-         *  Serialize all nodes in the non-dense format.
-         * 
-         * @param parentbuilder Add to this PrimitiveBlock.
-         */
-        public Osmformat.PrimitiveGroup serializeNonDense() {
-          if (contents.size() == 0) {
-            return null;
-          }
-          // System.out.format("%d Nodes   ",nodes.size());
-          StringTable stable = getStringTable();
-          Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
-          .newBuilder();
-          for (Node i : contents) {
-            long id = i.getId();
-            int lat = mapDegrees(i.getLatitude());
-            int lon = mapDegrees(i.getLongitude());
-            Osmformat.Node.Builder bi = Osmformat.Node.newBuilder();
-            bi.setId(id);
-            bi.setLon(lon);
-            bi.setLat(lat);
-            for (Tag t : i.getTags()) {
-              bi.addKeys(stable.getIndex(t.getKey()));
-              bi.addVals(stable.getIndex(t.getValue()));
-            }
-            if (!omit_metadata) {
-              bi.setInfo(serializeMetadata(i));
-            }
-            builder.addNodes(bi);
-          }
-          return builder.build();
-        }
-    
-    }
-
-    
-
-    private class WayGroup extends Prim<Way> implements PrimGroupWriterInterface {
-      public Osmformat.PrimitiveGroup serialize() {
-        if (contents.size() == 0) {
-          return null;
-        }
-
-            // System.out.format("%d Ways  ",contents.size());
-            StringTable stable = getStringTable();
-            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
-                    .newBuilder();
-            for (Way i : contents) {
-                Osmformat.Way.Builder bi = Osmformat.Way.newBuilder();
-                bi.setId(i.getId());
-                long lastid = 0;
-                for (WayNode j : i.getWayNodes()) {
-                    long id = j.getNodeId();
-                    bi.addRefs(id - lastid);
-                    lastid = id;
-                }
-                for (Tag t : i.getTags()) {
-                    bi.addKeys(stable.getIndex(t.getKey()));
-                    bi.addVals(stable.getIndex(t.getValue()));
-                }
-                if (!omit_metadata) {
-                    bi.setInfo(serializeMetadata(i));
-                }
-                builder.addWays(bi);
-            }
-            return builder.build();
-        }
-    }
-
-    private class RelationGroup extends Prim<Relation> implements
-            PrimGroupWriterInterface {
-        public void addStringsToStringtable() {
-            StringTable stable = getStringTable();
-            super.addStringsToStringtable();
-            for (Relation i : contents) {
-                for (RelationMember j : i.getMembers()) {
-                    stable.incr(j.getMemberRole());
-                }
-            }
-        }
-
-        public Osmformat.PrimitiveGroup serialize() {
-          if (contents.size() == 0) {
-            return null;
-          }
-
-          // System.out.format("%d Relations  ",contents.size());
-            StringTable stable = getStringTable();
-            Osmformat.PrimitiveGroup.Builder builder = Osmformat.PrimitiveGroup
-                    .newBuilder();
-            for (Relation i : contents) {
-                Osmformat.Relation.Builder bi = Osmformat.Relation.newBuilder();
-                bi.setId(i.getId());
-                RelationMember[] arr = new RelationMember[i.getMembers().size()];
-                i.getMembers().toArray(arr);
-                long lastid = 0;
-                for (RelationMember j : i.getMembers()) {
-                    long id = j.getMemberId();
-                    bi.addMemids(id - lastid);
-                    lastid = id;
-                    if (j.getMemberType() == EntityType.Node) {
-                        bi.addTypes(MemberType.NODE);
-                    } else if (j.getMemberType() == EntityType.Way) {
-                        bi.addTypes(MemberType.WAY);
-                    } else if (j.getMemberType() == EntityType.Relation) {
-                        bi.addTypes(MemberType.RELATION);
-                    } else {
-                        assert (false); // Software bug: Unknown entity.
-                    }
-                    bi.addRolesSid(stable.getIndex(j.getMemberRole()));
-                }
-
-                for (Tag t : i.getTags()) {
-                    bi.addKeys(stable.getIndex(t.getKey()));
-                    bi.addVals(stable.getIndex(t.getValue()));
-                }
-                if (!omit_metadata) {
-                    bi.setInfo(serializeMetadata(i));
-                }
-                builder.addRelations(bi);
-            }
-            return builder.build();
-        }
-    }
-
-    /* One list for each type */
-    private WayGroup ways;
-    private NodeGroup nodes;
-    private RelationGroup relations;
-
-    private Processor processor = new Processor();
-
-    /**
-     * Buffer up events into groups that are all of the same type, or all of the
-     * same length, then process each buffer.
-     */
-    public class Processor implements EntityProcessor {
-        @Override
-        public void process(BoundContainer bound) {
-            // Specialcase this. Assume we only ever get one contigious bound
-            // request.
-            switchTypes();
-            processBounds(bound.getEntity());
-        }
-
-		/**
-		 * Check if we've reached the batch size limit and process the batch if
-		 * we have.
-		 */
-        public void checkLimit() {
-            total_entities++;
-            if (++batch_size < batch_limit) {
-                return;
-            }
-            switchTypes();
-            processBatch();
-        }
-
-        @Override
-        public void process(NodeContainer node) {
-            if (nodes == null) {
-                writeEmptyHeaderIfNeeded();
-                // Need to switch types.
-                switchTypes();
-                nodes = new NodeGroup();
-            }
-            nodes.add(node.getEntity());
-            checkLimit();
-        }
-
-        @Override
-        public void process(WayContainer way) {
-            if (ways == null) {
-                writeEmptyHeaderIfNeeded();
-                switchTypes();
-                ways = new WayGroup();
-            }
-            ways.add(way.getEntity());
-            checkLimit();
-        }
-
-        @Override
-        public void process(RelationContainer relation) {
-            if (relations == null) {
-                writeEmptyHeaderIfNeeded();
-                switchTypes();
-                relations = new RelationGroup();
-            }
-            relations.add(relation.getEntity());
-            checkLimit();
-        }
-    }
-
-    /**
-     * At the end of this function, all of the lists of unprocessed 'things'
-     * must be null
-     */
-    private void switchTypes() {
-        if (nodes != null) {
-            groups.add(nodes);
-            nodes = null;
-        } else if (ways != null) {
-            groups.add(ways);
-            ways = null;
-        } else if (relations != null) {
-            groups.add(relations);
-            relations = null;
-        } else {
-            return; // No data. Is this an empty file?
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void processBounds(Bound entity) {
-        Osmformat.HeaderBlock.Builder headerblock = Osmformat.HeaderBlock
-                .newBuilder();
-        
-        Osmformat.HeaderBBox.Builder bbox = Osmformat.HeaderBBox.newBuilder();
-        bbox.setLeft(mapRawDegrees(entity.getLeft()));
-        bbox.setBottom(mapRawDegrees(entity.getBottom()));
-        bbox.setRight(mapRawDegrees(entity.getRight()));
-        bbox.setTop(mapRawDegrees(entity.getTop()));
-        headerblock.setBbox(bbox);
-
-        headerblock.setSource(entity.getOrigin());
-        finishHeader(headerblock);
-    }
-
-    /** Write empty header block when there's no bounds entity. */
-    public void writeEmptyHeaderIfNeeded() {
-      if (headerWritten) {
-        return;
-      }
-      Osmformat.HeaderBlock.Builder headerblock = Osmformat.HeaderBlock.newBuilder();
-      finishHeader(headerblock);
-    }
-
-    /** Write the header fields that are always needed.
-     * 
-     * @param headerblock Incomplete builder to complete and write.
-     * */
-    public void finishHeader(Osmformat.HeaderBlock.Builder headerblock) {
-      headerblock.setWritingprogram(OsmosisConstants.VERSION);
-      headerblock.addRequiredFeatures("OsmSchema-V0.6");
-      if (useDense) {
-        headerblock.addRequiredFeatures("DenseNodes");
-      }
-      Osmformat.HeaderBlock message = headerblock.build();
-      try {
-          output.write(FileBlock.newInstance("OSMHeader", message
-                  .toByteString(), null));
-      } catch (IOException e) {
-          throw new OsmosisRuntimeException("Unable to write OSM header.", e);
-      }
-      headerWritten = true;
-    }
-   
-    
-    /**
-     * {@inheritDoc}
-     */
-    public void process(EntityContainer entityContainer) {
-        entityContainer.process(processor);
-    }
-
-    @Override
-    public void complete() {
-        try {
-            switchTypes();
-            processBatch();
-            flush();
-        } catch (IOException e) {
-        	throw new OsmosisRuntimeException("Unable to complete the PBF file.", e);
-        }
-    }
-
-    @Override
-    public void release() {
-        try {
-            close();
-        } catch (IOException e) {
-        	LOG.log(Level.WARNING, "Unable to release PBF file resources during release.", e);
-        }
-
-    }
-}
diff --git a/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java b/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java
deleted file mode 100644
index 5140cc7..0000000
--- a/pbf/src/main/java/crosby/binary/osmosis/OsmosisSerializerFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package crosby.binary.osmosis;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
-
-import crosby.binary.file.BlockOutputStream;
-
-/**
- * The task manager factory for a binary (PBF) writer.
- */
-public class OsmosisSerializerFactory extends TaskManagerFactory {
-    private static final String ARG_FILE_NAME = "file";
-    private static final String DEFAULT_FILE_NAME = "dump.osm.pbf";
-
-    @Override
-    protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-        // TODO Auto-generated method stub
-        String fileName;
-        File file;
-        OsmosisSerializer task = null;
-
-        // Get the task arguments.
-        fileName = getStringArgument(taskConfig, ARG_FILE_NAME,
-                getDefaultStringArgument(taskConfig, DEFAULT_FILE_NAME));
-
-        // Create a file object from the file name provided.
-        file = new File(fileName);
-
-        // Build the task object.
-        try {
-            BlockOutputStream output = new BlockOutputStream(
-                    new FileOutputStream(file));
-            task = new OsmosisSerializer(output);
-            task.configBatchLimit(this.getIntegerArgument(taskConfig,
-                    "batchlimit", 8000));
-            task.configOmit(this.getBooleanArgument(taskConfig, "omitmetadata",
-                    false));
-            task.setUseDense(this.getBooleanArgument(taskConfig, "usedense",
-                true));
-            task.configGranularity(this.getIntegerArgument(taskConfig,
-                    "granularity", 100));
-
-            output.setCompress(this.getStringArgument(taskConfig, "compress",
-                    "deflate"));
-
-        } catch (FileNotFoundException e) {
-        	throw new OsmosisRuntimeException("Failed to initialize Osmosis pbf serializer.", e);
-        }
-
-        return new SinkManager(taskConfig.getId(), task, taskConfig
-                .getPipeArgs());
-    }
-}
diff --git a/pgsimple/.checkstyle b/pgsimple/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/pgsimple/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/pgsimple/.classpath b/pgsimple/.classpath
deleted file mode 100644
index 15313a2..0000000
--- a/pgsimple/.classpath
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Dataset"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgis-jdbc-1.3.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/pgsimple/.gitignore b/pgsimple/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/pgsimple/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/pgsimple/.project b/pgsimple/.project
deleted file mode 100644
index a871c55..0000000
--- a/pgsimple/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-PgSimple</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/pgsimple/build.xml b/pgsimple/build.xml
deleted file mode 100644
index fc908c2..0000000
--- a/pgsimple/build.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.PgSimple" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-	
-	<!-- Disable tests until continuous integration has a database configured. -->
-	<target name="test"/>
-</project>
diff --git a/pgsimple/ivy.xml b/pgsimple/ivy.xml
deleted file mode 100644
index cf2f9b4..0000000
--- a/pgsimple/ivy.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-pgsimple"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-dataset" rev="${project.version}" conf="test->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-    	
-    	<!--<dependency org="org.springframework" name="spring-jdbc" rev="${dependency.version.spring}" conf="compile->default"/>-->
-        <!--<dependency org="commons-dbcp" name="commons-dbcp" rev="${dependency.version.commons-dbcp}" conf="compile->default"/>-->
-    	<dependency org="postgresql" name="postgresql" rev="${dependency.version.postgresql}" conf="compile->default"/>
-        <dependency org="org.postgis" name="postgis-jdbc" rev="${dependency.version.postgis}" conf="compile->default">
-        	<exclude module="postgis-stubs"/>
-        </dependency>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java
deleted file mode 100644
index 97a7e8b..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlChangeWriter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsimple.common.SchemaVersionValidator;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ActionChangeWriter;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ChangeWriter;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-
-
-/**
- * A change sink writing to database tables. This aims to be suitable for
- * running at regular intervals with database overhead proportional to changeset
- * size.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlChangeWriter implements ChangeSink {
-	
-	private ChangeWriter changeWriter;
-	private Map<ChangeAction, ActionChangeWriter> actionWriterMap;
-	private DatabaseContext dbCtx;
-	private SchemaVersionValidator schemaVersionValidator;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 */
-	public PostgreSqlChangeWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
-		dbCtx = new DatabaseContext(loginCredentials);
-		changeWriter = new ChangeWriter(dbCtx);
-		actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
-		actionWriterMap.put(ChangeAction.Create, new ActionChangeWriter(changeWriter, ChangeAction.Create));
-		actionWriterMap.put(ChangeAction.Modify, new ActionChangeWriter(changeWriter, ChangeAction.Modify));
-		actionWriterMap.put(ChangeAction.Delete, new ActionChangeWriter(changeWriter, ChangeAction.Delete));
-		
-		schemaVersionValidator = new SchemaVersionValidator(dbCtx, preferences);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer change) {
-		ChangeAction action;
-		
-		// Verify that the schema version is supported.
-		schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
-		
-		action = change.getAction();
-		
-		if (!actionWriterMap.containsKey(action)) {
-			throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
-		}
-		
-		// Process the entity using the action writer appropriate for the change
-		// action.
-		change.getEntityContainer().process(actionWriterMap.get(action));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		changeWriter.complete();
-		
-		dbCtx.commit();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeWriter.release();
-		
-		dbCtx.release();
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java
deleted file mode 100644
index d9803e4..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlCopyWriter.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetLoader;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.DatabaseCapabilityChecker;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetBuilder;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.TempCopyFileset;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink for storing all data to a database using the COPY command.
- * This task is intended for writing to an empty database.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlCopyWriter implements Sink {
-	
-	private static final Logger LOG = Logger.getLogger(PostgreSqlCopyWriter.class.getName());
-	
-	private CopyFilesetBuilder copyFilesetBuilder;
-	private CopyFilesetLoader copyFilesetLoader;
-	private TempCopyFileset copyFileset;
-	private DatabaseLoginCredentials loginCredentials;
-	private DatabasePreferences preferences;
-	private NodeLocationStoreType storeType;
-	private boolean populateBbox;
-	private boolean populateLinestring;
-	private boolean initialized;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public PostgreSqlCopyWriter(
-			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-			NodeLocationStoreType storeType) {
-		this.loginCredentials = loginCredentials;
-		this.preferences = preferences;
-		this.storeType = storeType;
-		
-		copyFileset = new TempCopyFileset();
-	}
-	
-	
-	private void initialize() {
-		if (!initialized) {
-			DatabaseContext dbCtx;
-			DatabaseCapabilityChecker capabilityChecker;
-			
-			LOG.fine("Initializing the database and temporary processing files.");
-			
-			dbCtx = new DatabaseContext(loginCredentials);
-			
-			try {
-				capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-
-				populateBbox = capabilityChecker.isWayBboxSupported();
-				populateLinestring = capabilityChecker.isWayLinestringSupported();				
-
-				copyFilesetBuilder =
-					new CopyFilesetBuilder(copyFileset, populateBbox, populateLinestring, storeType);
-				
-				copyFilesetLoader = new CopyFilesetLoader(loginCredentials, preferences, copyFileset);
-				
-				LOG.fine("Processing input data, building geometries and creating database load files.");
-				
-			} finally {
-				dbCtx.release();
-			}
-			
-			initialized = true;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		initialize();
-		
-		copyFilesetBuilder.process(entityContainer);
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the files, then loads the files into the database. 
-	 */
-	public void complete() {
-		initialize();
-		
-		LOG.fine("All data has been received, beginning database load.");
-		copyFilesetBuilder.complete();
-		copyFilesetLoader.run();
-		
-		LOG.fine("Processing complete.");
-	}
-	
-	
-	/**
-	 * Releases all database resources.
-	 */
-	public void release() {
-		copyFilesetBuilder.release();
-		copyFileset.release();
-		
-		initialized = false;
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java
deleted file mode 100644
index 9017acf..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlDumpWriter.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.DirectoryCopyFileset;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.CopyFilesetBuilder;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink for storing all data to database dump files. This task is
- * intended for populating an empty database.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlDumpWriter implements Sink {
-	
-	private CopyFilesetBuilder copyFilesetBuilder;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param filePrefix
-	 *            The prefix to prepend to all generated file names.
-	 * @param enableBboxBuilder
-	 *            If true, the way bbox geometry is built during processing
-	 *            instead of relying on the database to build them after import.
-	 *            This increases processing but is faster than relying on the
-	 *            database.
-	 * @param enableLinestringBuilder
-	 *            If true, the way linestring geometry is built during
-	 *            processing instead of relying on the database to build them
-	 *            after import. This increases processing but is faster than
-	 *            relying on the database.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public PostgreSqlDumpWriter(
-			File filePrefix, boolean enableBboxBuilder,
-			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
-		DirectoryCopyFileset copyFileset;
-		
-		copyFileset = new DirectoryCopyFileset(filePrefix);
-		
-		copyFilesetBuilder =
-			new CopyFilesetBuilder(copyFileset, enableBboxBuilder, enableLinestringBuilder, storeType);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		copyFilesetBuilder.process(entityContainer);
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the database and commits. 
-	 */
-	public void complete() {
-		copyFilesetBuilder.complete();
-	}
-	
-	
-	/**
-	 * Releases all database resources.
-	 */
-	public void release() {
-		copyFilesetBuilder.release();
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java
deleted file mode 100644
index d4984d8..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/PostgreSqlWriter.java
+++ /dev/null
@@ -1,844 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.database.DbFeature;
-import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
-import org.openstreetmap.osmosis.core.database.ReleasableStatementContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsimple.common.SchemaVersionValidator;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.ActionDao;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.IndexManager;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.NodeMapper;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.RelationMapper;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.RelationMemberMapper;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.TagMapper;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.UserDao;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayGeometryBuilder;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayMapper;
-import org.openstreetmap.osmosis.pgsimple.v0_6.impl.WayNodeMapper;
-import org.postgis.Geometry;
-
-
-/**
- * An OSM data sink for storing all data to a database. This task is intended
- * for writing to an empty database.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlWriter implements Sink, EntityProcessor {
-	
-	private static final Logger LOG = Logger.getLogger(PostgreSqlWriter.class.getName());
-	
-	// These constants define how many rows of each data type will be inserted
-	// with single insert statements.
-	private static final int INSERT_BULK_ROW_COUNT_NODE = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_NODE_TAG = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_WAY = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_WAY_TAG = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_WAY_NODE = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_RELATION = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_RELATION_TAG = 1000;
-	private static final int INSERT_BULK_ROW_COUNT_RELATION_MEMBER = 1000;
-	
-	
-	private DatabaseContext dbCtx;
-	private boolean enableBboxBuilder;
-	private boolean enableLinestringBuilder;
-	private SchemaVersionValidator schemaVersionValidator;
-	private IndexManager indexManager;
-	private List<Node> nodeBuffer;
-	private List<DbFeature<Tag>> nodeTagBuffer;
-	private List<Way> wayBuffer;
-	private List<DbFeature<Tag>> wayTagBuffer;
-	private List<DbOrderedFeature<WayNode>> wayNodeBuffer;
-	private List<Relation> relationBuffer;
-	private List<DbFeature<Tag>> relationTagBuffer;
-	private List<DbOrderedFeature<RelationMember>> relationMemberBuffer;
-	private boolean initialized;
-	private HashSet<Integer> userSet;
-	private ActionDao actionDao;
-	private UserDao userDao;
-	private ReleasableStatementContainer statementContainer;
-	private PreparedStatement singleNodeStatement;
-	private PreparedStatement bulkNodeStatement;
-	private PreparedStatement singleNodeTagStatement;
-	private PreparedStatement bulkNodeTagStatement;
-	private PreparedStatement singleWayStatement;
-	private PreparedStatement bulkWayStatement;
-	private PreparedStatement singleWayTagStatement;
-	private PreparedStatement bulkWayTagStatement;
-	private PreparedStatement singleWayNodeStatement;
-	private PreparedStatement bulkWayNodeStatement;
-	private PreparedStatement singleRelationStatement;
-	private PreparedStatement bulkRelationStatement;
-	private PreparedStatement singleRelationTagStatement;
-	private PreparedStatement bulkRelationTagStatement;
-	private PreparedStatement singleRelationMemberStatement;
-	private PreparedStatement bulkRelationMemberStatement;
-	private int uncommittedEntityCount;
-	private NodeMapper nodeBuilder;
-	private WayMapper wayBuilder;
-	private RelationMapper relationBuilder;
-	private TagMapper nodeTagBuilder;
-	private TagMapper wayTagBuilder;
-	private TagMapper relationTagBuilder;
-	private WayNodeMapper wayNodeBuilder;
-	private RelationMemberMapper relationMemberBuilder;
-	private WayGeometryBuilder wayGeometryBuilder;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 * @param enableBboxBuilder
-	 *            If true, the way bbox geometry is built during processing
-	 *            instead of relying on the database to build them after import.
-	 *            This increases processing but is faster than relying on the
-	 *            database.
-	 * @param enableLinestringBuilder
-	 *            If true, the way linestring geometry is built during
-	 *            processing instead of relying on the database to build them
-	 *            after import. This increases processing but is faster than
-	 *            relying on the database.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public PostgreSqlWriter(
-			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-			boolean enableBboxBuilder, boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
-		dbCtx = new DatabaseContext(loginCredentials);
-		
-		this.enableBboxBuilder = enableBboxBuilder;
-		this.enableLinestringBuilder = enableLinestringBuilder;
-		
-		schemaVersionValidator = new SchemaVersionValidator(dbCtx, preferences);
-		indexManager = new IndexManager(dbCtx, !enableBboxBuilder, !enableLinestringBuilder);
-		
-		nodeBuffer = new ArrayList<Node>();
-		nodeTagBuffer = new ArrayList<DbFeature<Tag>>();
-		wayBuffer = new ArrayList<Way>();
-		wayTagBuffer = new ArrayList<DbFeature<Tag>>();
-		wayNodeBuffer = new ArrayList<DbOrderedFeature<WayNode>>();
-		relationBuffer = new ArrayList<Relation>();
-		relationTagBuffer = new ArrayList<DbFeature<Tag>>();
-		relationMemberBuffer = new ArrayList<DbOrderedFeature<RelationMember>>();
-		
-		// Create an action dao but disable it so that no records will be written.
-		actionDao = new ActionDao(dbCtx, false);
-		
-		userSet = new HashSet<Integer>();
-		userDao = new UserDao(dbCtx, actionDao);
-		
-		nodeBuilder = new NodeMapper();
-		wayBuilder = new WayMapper(enableBboxBuilder, enableLinestringBuilder);
-		relationBuilder = new RelationMapper();
-		nodeTagBuilder = new TagMapper(nodeBuilder.getEntityName());
-		wayTagBuilder = new TagMapper(wayBuilder.getEntityName());
-		relationTagBuilder = new TagMapper(relationBuilder.getEntityName());
-		wayNodeBuilder = new WayNodeMapper();
-		relationMemberBuilder = new RelationMemberMapper();
-		wayGeometryBuilder = new WayGeometryBuilder(storeType);
-		
-		uncommittedEntityCount = 0;
-		
-		statementContainer = new ReleasableStatementContainer();
-		
-		initialized = false;
-	}
-	
-	
-	/**
-	 * Initialises prepared statements and obtains database locks. Can be called
-	 * multiple times.
-	 */
-	private void initialize() {
-		if (!initialized) {
-			schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
-			
-			bulkNodeStatement = statementContainer.add(
-					dbCtx.prepareStatement(nodeBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_NODE)));
-			singleNodeStatement = statementContainer.add(
-					dbCtx.prepareStatement(nodeBuilder.getSqlInsert(1)));
-			bulkNodeTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(nodeTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_NODE_TAG)));
-			singleNodeTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(nodeTagBuilder.getSqlInsert(1)));
-			bulkWayStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY)));
-			singleWayStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayBuilder.getSqlInsert(1)));
-			bulkWayTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY_TAG)));
-			singleWayTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayTagBuilder.getSqlInsert(1)));
-			bulkWayNodeStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayNodeBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_WAY_NODE)));
-			singleWayNodeStatement = statementContainer.add(
-					dbCtx.prepareStatement(wayNodeBuilder.getSqlInsert(1)));
-			bulkRelationStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION)));
-			singleRelationStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationBuilder.getSqlInsert(1)));
-			bulkRelationTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationTagBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION_TAG)));
-			singleRelationTagStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationTagBuilder.getSqlInsert(1)));
-			bulkRelationMemberStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationMemberBuilder.getSqlInsert(INSERT_BULK_ROW_COUNT_RELATION_MEMBER)));
-			singleRelationMemberStatement = statementContainer.add(
-					dbCtx.prepareStatement(relationMemberBuilder.getSqlInsert(1)));
-			
-			// Drop all constraints and indexes.
-			indexManager.prepareForLoad();
-			
-			LOG.fine("Loading data.");
-			
-			initialized = true;
-		}
-	}
-	
-	
-	/**
-	 * Writes the specified user to the database if it hasn't already been.
-	 * 
-	 * @param user
-	 *            The user to add.
-	 */
-	private void writeUser(OsmUser user) {
-		// Write the user to the database if it hasn't already been.
-		if (!user.equals(OsmUser.NONE)) {
-			if (!userSet.contains(user.getId())) {
-				userDao.addUser(user);
-				
-				userSet.add(user.getId());
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes nodes to the database. If complete is false, this will only write
-	 * nodes until the remaining node count is less than the multi-row insert
-	 * statement row count. If complete is true, all remaining rows will be
-	 * written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushNodes(boolean complete) {
-		while (nodeBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE) {
-			List<Node> processedNodes;
-			int prmIndex;
-			
-			processedNodes = new ArrayList<Node>(INSERT_BULK_ROW_COUNT_NODE);
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE; i++) {
-				Node node;
-				
-				node = nodeBuffer.remove(0);
-				processedNodes.add(node);
-				
-				prmIndex = nodeBuilder.populateEntityParameters(bulkNodeStatement, prmIndex, node);
-				
-				uncommittedEntityCount++;
-			}
-			
-			try {
-				bulkNodeStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert nodes into the database.", e);
-			}
-			
-			for (Node node : processedNodes) {
-				addNodeTags(node);
-			}
-		}
-		
-		if (complete) {
-			while (nodeBuffer.size() > 0) {
-				Node node;
-				
-				node = nodeBuffer.remove(0);
-				
-				nodeBuilder.populateEntityParameters(singleNodeStatement, 1, node);
-				
-				uncommittedEntityCount++;
-				
-				try {
-					singleNodeStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a node into the database.", e);
-				}
-				
-				addNodeTags(node);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Process the node tags.
-	 * 
-	 * @param node
-	 *            The node to be processed.
-	 */
-	private void addNodeTags(Node node) {
-		for (Tag tag : node.getTags()) {
-			nodeTagBuffer.add(new DbFeature<Tag>(node.getId(), tag));
-		}
-		
-		flushNodeTags(false);
-	}
-	
-	
-	/**
-	 * Flushes node tags to the database. If complete is false, this will only
-	 * write node tags until the remaining node tag count is less than the
-	 * multi-row insert statement row count. If complete is true, all remaining
-	 * rows will be written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushNodeTags(boolean complete) {
-		while (nodeTagBuffer.size() >= INSERT_BULK_ROW_COUNT_NODE_TAG) {
-			int prmIndex;
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_NODE_TAG; i++) {
-				prmIndex = nodeTagBuilder.populateEntityParameters(
-						bulkNodeTagStatement, prmIndex, nodeTagBuffer.remove(0));
-			}
-			
-			try {
-				bulkNodeTagStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert node tags into the database.", e);
-			}
-		}
-		
-		if (complete) {
-			while (nodeTagBuffer.size() > 0) {
-				nodeTagBuilder.populateEntityParameters(singleNodeTagStatement, 1, nodeTagBuffer.remove(0));
-				
-				try {
-					singleNodeTagStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a node tag into the database.", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes ways to the database. If complete is false, this will only write
-	 * ways until the remaining way count is less than the multi-row insert
-	 * statement row count. If complete is true, all remaining rows will be
-	 * written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushWays(boolean complete) {
-		while (wayBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY) {
-			List<Way> processedWays;
-			int prmIndex;
-			
-			processedWays = new ArrayList<Way>(INSERT_BULK_ROW_COUNT_WAY);
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY; i++) {
-				Way way;
-				List<Geometry> geometries;
-				
-				way = wayBuffer.remove(0);
-				processedWays.add(way);
-				
-				geometries = new ArrayList<Geometry>();
-				if (enableBboxBuilder) {
-					geometries.add(wayGeometryBuilder.createWayBbox(way));
-				}
-				if (enableLinestringBuilder) {
-					geometries.add(wayGeometryBuilder.createWayLinestring(way));
-				}
-				prmIndex = wayBuilder.populateEntityParameters(bulkWayStatement, prmIndex, way, geometries);
-				
-				uncommittedEntityCount++;
-			}
-			
-			try {
-				bulkWayStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert ways into the database.", e);
-			}
-			
-			for (Way way : processedWays) {
-				addWayTags(way);
-				addWayNodes(way);
-			}
-		}
-		
-		if (complete) {
-			while (wayBuffer.size() > 0) {
-				Way way;
-				List<Geometry> geometries;
-				
-				way = wayBuffer.remove(0);
-				
-				geometries = new ArrayList<Geometry>();
-				if (enableBboxBuilder) {
-					geometries.add(wayGeometryBuilder.createWayBbox(way));
-				}
-				if (enableLinestringBuilder) {
-					geometries.add(wayGeometryBuilder.createWayLinestring(way));
-				}
-				wayBuilder.populateEntityParameters(singleWayStatement, 1, way, geometries);
-				
-				uncommittedEntityCount++;
-				
-				try {
-					singleWayStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a way into the database.", e);
-				}
-				
-				addWayTags(way);
-				addWayNodes(way);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Process the way tags.
-	 * 
-	 * @param way
-	 *            The way to be processed.
-	 */
-	private void addWayTags(Way way) {
-		for (Tag tag : way.getTags()) {
-			wayTagBuffer.add(new DbFeature<Tag>(way.getId(), tag));
-		}
-		
-		flushWayTags(false);
-	}
-	
-	
-	/**
-	 * Process the way nodes.
-	 * 
-	 * @param way
-	 *            The way to be processed.
-	 */
-	private void addWayNodes(Way way) {
-		int sequenceId;
-		
-		sequenceId = 0;
-		for (WayNode wayNode : way.getWayNodes()) {
-			wayNodeBuffer.add(new DbOrderedFeature<WayNode>(way.getId(), wayNode, sequenceId++));
-		}
-		
-		flushWayNodes(false);
-	}
-	
-	
-	/**
-	 * Flushes way tags to the database. If complete is false, this will only
-	 * write way tags until the remaining way tag count is less than the
-	 * multi-row insert statement row count. If complete is true, all remaining
-	 * rows will be written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushWayTags(boolean complete) {
-		while (wayTagBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_TAG) {
-			int prmIndex;
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_TAG; i++) {
-				prmIndex = wayTagBuilder.populateEntityParameters(
-						bulkWayTagStatement, prmIndex, wayTagBuffer.remove(0));
-			}
-			
-			try {
-				bulkWayTagStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert way tags into the database.", e);
-			}
-		}
-		
-		if (complete) {
-			while (wayTagBuffer.size() > 0) {
-				wayTagBuilder.populateEntityParameters(singleWayTagStatement, 1, wayTagBuffer.remove(0));
-				
-				try {
-					singleWayTagStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a way tag into the database.", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes way nodes to the database. If complete is false, this will only
-	 * write way nodes until the remaining way node count is less than the
-	 * multi-row insert statement row count. If complete is true, all remaining
-	 * rows will be written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushWayNodes(boolean complete) {
-		while (wayNodeBuffer.size() >= INSERT_BULK_ROW_COUNT_WAY_NODE) {
-			int prmIndex;
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_WAY_NODE; i++) {
-				prmIndex = wayNodeBuilder.populateEntityParameters(
-						bulkWayNodeStatement, prmIndex, wayNodeBuffer.remove(0));
-			}
-			
-			try {
-				bulkWayNodeStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert way nodes into the database.", e);
-			}
-		}
-		
-		if (complete) {
-			while (wayNodeBuffer.size() > 0) {
-				wayNodeBuilder.populateEntityParameters(singleWayNodeStatement, 1, wayNodeBuffer.remove(0));
-				
-				try {
-					singleWayNodeStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a way node into the database.", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes relations to the database. If complete is false, this will only write
-	 * relations until the remaining relation count is less than the multi-row insert
-	 * statement row count. If complete is true, all remaining rows will be
-	 * written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushRelations(boolean complete) {
-		while (relationBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION) {
-			List<Relation> processedRelations;
-			int prmIndex;
-			
-			processedRelations = new ArrayList<Relation>(INSERT_BULK_ROW_COUNT_RELATION);
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION; i++) {
-				Relation relation;
-				
-				relation = relationBuffer.remove(0);
-				processedRelations.add(relation);
-				
-				prmIndex = relationBuilder.populateEntityParameters(bulkRelationStatement, prmIndex, relation);
-				
-				uncommittedEntityCount++;
-			}
-			
-			try {
-				bulkRelationStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert relations into the database.", e);
-			}
-			
-			for (Relation relation : processedRelations) {
-				addRelationTags(relation);
-				addRelationMembers(relation);
-			}
-		}
-		
-		if (complete) {
-			while (relationBuffer.size() > 0) {
-				Relation relation;
-				
-				relation = relationBuffer.remove(0);
-				
-				relationBuilder.populateEntityParameters(singleRelationStatement, 1, relation);
-				
-				uncommittedEntityCount++;
-				
-				try {
-					singleRelationStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a relation into the database.", e);
-				}
-				
-				addRelationTags(relation);
-				addRelationMembers(relation);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Process the relation tags.
-	 * 
-	 * @param relation
-	 *            The relation to be processed.
-	 */
-	private void addRelationTags(Relation relation) {
-		for (Tag tag : relation.getTags()) {
-			relationTagBuffer.add(new DbFeature<Tag>(relation.getId(), tag));
-		}
-		
-		flushRelationTags(false);
-	}
-	
-	
-	/**
-	 * Process the relation members.
-	 * 
-	 * @param relation
-	 *            The relation to be processed.
-	 */
-	private void addRelationMembers(Relation relation) {
-		int sequenceId;
-		
-		sequenceId = 0;
-		for (RelationMember relationMember : relation.getMembers()) {
-			relationMemberBuffer.add(
-					new DbOrderedFeature<RelationMember>(relation.getId(), relationMember, sequenceId++));
-		}
-		
-		flushRelationMembers(false);
-	}
-	
-	
-	/**
-	 * Flushes relation tags to the database. If complete is false, this will only
-	 * write relation tags until the remaining relation tag count is less than the
-	 * multi-row insert statement row count. If complete is true, all remaining
-	 * rows will be written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushRelationTags(boolean complete) {
-		while (relationTagBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_TAG) {
-			int prmIndex;
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_TAG; i++) {
-				prmIndex = relationTagBuilder.populateEntityParameters(
-						bulkRelationTagStatement, prmIndex, relationTagBuffer.remove(0));
-			}
-			
-			try {
-				bulkRelationTagStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert relation tags into the database.", e);
-			}
-		}
-		
-		if (complete) {
-			while (relationTagBuffer.size() > 0) {
-				relationTagBuilder.populateEntityParameters(singleRelationTagStatement, 1, relationTagBuffer.remove(0));
-				
-				try {
-					singleRelationTagStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a relation tag into the database.", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Flushes relation members to the database. If complete is false, this will only
-	 * write relation members until the remaining relation member count is less than the
-	 * multi-row insert statement row count. If complete is true, all remaining
-	 * rows will be written using single row insert statements.
-	 * 
-	 * @param complete
-	 *            If true, all data will be written to the database. If false,
-	 *            some data may be left until more data is available.
-	 */
-	private void flushRelationMembers(boolean complete) {
-		while (relationMemberBuffer.size() >= INSERT_BULK_ROW_COUNT_RELATION_MEMBER) {
-			int prmIndex;
-			
-			prmIndex = 1;
-			for (int i = 0; i < INSERT_BULK_ROW_COUNT_RELATION_MEMBER; i++) {
-				prmIndex = relationMemberBuilder.populateEntityParameters(
-						bulkRelationMemberStatement, prmIndex, relationMemberBuffer.remove(0));
-			}
-			
-			try {
-				bulkRelationMemberStatement.executeUpdate();
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Unable to bulk insert relation members into the database.", e);
-			}
-		}
-		
-		if (complete) {
-			while (relationMemberBuffer.size() > 0) {
-				relationMemberBuilder.populateEntityParameters(
-						singleRelationMemberStatement, 1, relationMemberBuffer.remove(0));
-				
-				try {
-					singleRelationMemberStatement.executeUpdate();
-				} catch (SQLException e) {
-					throw new OsmosisRuntimeException("Unable to insert a relation member into the database.", e);
-				}
-			}
-		}
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the database and commits. 
-	 */
-	public void complete() {
-		initialize();
-		
-		LOG.fine("Flushing buffers.");
-		
-		flushNodes(true);
-		flushNodeTags(true);
-		flushWays(true);
-		flushWayTags(true);
-		flushWayNodes(true);
-		flushRelations(true);
-		flushRelationTags(true);
-		flushRelationMembers(true);
-		
-		LOG.fine("Data load complete.");
-		
-		// Add all constraints and indexes.
-		indexManager.completeAfterLoad();
-		
-		LOG.fine("Committing changes.");
-		dbCtx.commit();
-		
-		LOG.fine("Vacuuming database.");
-		dbCtx.setAutoCommit(true);
-		dbCtx.executeStatement("VACUUM ANALYZE");
-		
-		LOG.fine("Complete.");
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		initialize();
-		
-		// Write the user to the database if required.
-		writeUser(entityContainer.getEntity().getUser());
-		
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(BoundContainer bound) {
-		// Do nothing.
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer nodeContainer) {
-		if (enableBboxBuilder || enableLinestringBuilder) {
-			wayGeometryBuilder.addNodeLocation(nodeContainer.getEntity());
-		}
-		
-		nodeBuffer.add(nodeContainer.getEntity());
-		
-		flushNodes(false);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		// Ignore ways with a single node because they can't be loaded into postgis.
-		if (wayContainer.getEntity().getWayNodes().size() > 1) {
-			wayBuffer.add(wayContainer.getEntity());
-		}
-		
-		flushWays(false);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		relationBuffer.add(relationContainer.getEntity());
-		
-		flushRelations(false);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		statementContainer.release();
-		wayGeometryBuilder.release();
-		
-		dbCtx.release();
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java
deleted file mode 100644
index 6c0a4e1..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/CopyFilesetBuilder.java
+++ /dev/null
@@ -1,250 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
-import org.openstreetmap.osmosis.pgsimple.common.CopyFileWriter;
-import org.openstreetmap.osmosis.pgsimple.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsimple.common.PointBuilder;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink for storing all data to a set of database dump files. These
- * files can be used for populating an empty database.
- * 
- * @author Brett Henderson
- */
-public class CopyFilesetBuilder implements Sink, EntityProcessor {
-	
-	private boolean enableBboxBuilder;
-	private boolean enableLinestringBuilder;
-	private WayGeometryBuilder wayGeometryBuilder;
-	private CompletableContainer writerContainer;
-	private MemberTypeValueMapper memberTypeValueMapper;
-	private CopyFileWriter userWriter;
-	private CopyFileWriter nodeWriter;
-	private CopyFileWriter nodeTagWriter;
-	private CopyFileWriter wayWriter;
-	private CopyFileWriter wayTagWriter;
-	private CopyFileWriter wayNodeWriter;
-	private CopyFileWriter relationWriter;
-	private CopyFileWriter relationTagWriter;
-	private CopyFileWriter relationMemberWriter;
-	private PointBuilder pointBuilder;
-	private Set<Integer> userSet;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param copyFileset
-	 *            The set of COPY files to be populated.
-	 * @param enableBboxBuilder
-	 *            If true, the way bbox geometry is built during processing
-	 *            instead of relying on the database to build them after import.
-	 *            This increases processing but is faster than relying on the
-	 *            database.
-	 * @param enableLinestringBuilder
-	 *            If true, the way linestring geometry is built during
-	 *            processing instead of relying on the database to build them
-	 *            after import. This increases processing but is faster than
-	 *            relying on the database.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public CopyFilesetBuilder(
-			CopyFileset copyFileset, boolean enableBboxBuilder,
-			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
-		this.enableBboxBuilder = enableBboxBuilder;
-		this.enableLinestringBuilder = enableLinestringBuilder;
-		
-		writerContainer = new CompletableContainer();
-		
-		userWriter = writerContainer.add(new CopyFileWriter(copyFileset.getUserFile()));
-		nodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeFile()));
-		nodeTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeTagFile()));
-		wayWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayFile()));
-		wayTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayTagFile()));
-		wayNodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayNodeFile()));
-		relationWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationFile()));
-		relationTagWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationTagFile()));
-		relationMemberWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationMemberFile()));
-		
-		pointBuilder = new PointBuilder();
-		wayGeometryBuilder = new WayGeometryBuilder(storeType);
-		memberTypeValueMapper = new MemberTypeValueMapper();
-		memberTypeValueMapper = new MemberTypeValueMapper();
-		
-		userSet = new HashSet<Integer>();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		OsmUser user;
-		
-		// Write a user entry if the user doesn't already exist.
-		user = entityContainer.getEntity().getUser();
-		if (!user.equals(OsmUser.NONE)) {
-			if (!userSet.contains(user.getId())) {
-				userWriter.writeField(user.getId());
-				userWriter.writeField(user.getName());
-				userWriter.endRecord();
-				
-				userSet.add(user.getId());
-			}
-		}
-		
-		// Process the entity itself.
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// Do nothing.
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer nodeContainer) {
-		Node node;
-		
-		node = nodeContainer.getEntity();
-		
-		nodeWriter.writeField(node.getId());
-		nodeWriter.writeField(node.getVersion());
-		nodeWriter.writeField(node.getUser().getId());
-		nodeWriter.writeField(node.getTimestamp());
-		nodeWriter.writeField(node.getChangesetId());
-		nodeWriter.writeField(pointBuilder.createPoint(node.getLatitude(), node.getLongitude()));
-		nodeWriter.endRecord();
-		
-		for (Tag tag : node.getTags()) {
-			nodeTagWriter.writeField(node.getId());
-			nodeTagWriter.writeField(tag.getKey());
-			nodeTagWriter.writeField(tag.getValue());
-			nodeTagWriter.endRecord();
-		}
-		
-		if (enableBboxBuilder || enableLinestringBuilder) {
-			wayGeometryBuilder.addNodeLocation(node);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		Way way;
-		int sequenceId;
-		
-		way = wayContainer.getEntity();
-		
-		// Ignore ways with a single node because they can't be loaded into postgis.
-		if (way.getWayNodes().size() > 1) {
-			wayWriter.writeField(way.getId());
-			wayWriter.writeField(way.getVersion());
-			wayWriter.writeField(way.getUser().getId());
-			wayWriter.writeField(way.getTimestamp());
-			wayWriter.writeField(way.getChangesetId());
-			if (enableBboxBuilder) {
-				wayWriter.writeField(wayGeometryBuilder.createWayBbox(way));
-			}
-			if (enableLinestringBuilder) {
-				wayWriter.writeField(wayGeometryBuilder.createWayLinestring(way));
-			}
-			wayWriter.endRecord();
-			
-			for (Tag tag : way.getTags()) {
-				wayTagWriter.writeField(way.getId());
-				wayTagWriter.writeField(tag.getKey());
-				wayTagWriter.writeField(tag.getValue());
-				wayTagWriter.endRecord();
-			}
-			
-			sequenceId = 0;
-			for (WayNode wayNode : way.getWayNodes()) {
-				wayNodeWriter.writeField(way.getId());
-				wayNodeWriter.writeField(wayNode.getNodeId());
-				wayNodeWriter.writeField(sequenceId++);
-				wayNodeWriter.endRecord();
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		Relation relation;
-		int memberSequenceId;
-		
-		relation = relationContainer.getEntity();
-		
-		relationWriter.writeField(relation.getId());
-		relationWriter.writeField(relation.getVersion());
-		relationWriter.writeField(relation.getUser().getId());
-		relationWriter.writeField(relation.getTimestamp());
-		relationWriter.writeField(relation.getChangesetId());
-		relationWriter.endRecord();
-		
-		for (Tag tag : relation.getTags()) {
-			relationTagWriter.writeField(relation.getId());
-			relationTagWriter.writeField(tag.getKey());
-			relationTagWriter.writeField(tag.getValue());
-			relationTagWriter.endRecord();
-		}
-		
-		memberSequenceId = 0;
-		for (RelationMember member : relation.getMembers()) {
-			relationMemberWriter.writeField(relation.getId());
-			relationMemberWriter.writeField(member.getMemberId());
-			relationMemberWriter.writeField(memberTypeValueMapper.getMemberType(member.getMemberType()));
-			relationMemberWriter.writeField(member.getMemberRole());
-			relationMemberWriter.writeField(memberSequenceId++);
-			relationMemberWriter.endRecord();
-		}
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the database and commits. 
-	 */
-	public void complete() {
-		writerContainer.complete();
-	}
-	
-	
-	/**
-	 * Releases all resources.
-	 */
-	public void release() {
-		writerContainer.release();
-		wayGeometryBuilder.release();
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java
deleted file mode 100644
index a27e8ca..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/IndexManager.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-
-
-/**
- * Drops and creates indexes in support of bulk load activities.
- * 
- * @author Brett Henderson
- */
-public class IndexManager {
-	
-	private static final Logger LOG = Logger.getLogger(IndexManager.class.getName());
-	
-	
-	private static final String[] PRE_LOAD_SQL = {
-		"ALTER TABLE users DROP CONSTRAINT pk_users",
-		"ALTER TABLE nodes DROP CONSTRAINT pk_nodes",
-		"ALTER TABLE ways DROP CONSTRAINT pk_ways",
-		"ALTER TABLE way_nodes DROP CONSTRAINT pk_way_nodes",
-		"ALTER TABLE relations DROP CONSTRAINT pk_relations",
-		"ALTER TABLE relation_members DROP CONSTRAINT pk_relation_members",
-		"DROP INDEX idx_node_tags_node_id",
-		"DROP INDEX idx_nodes_geom",
-		"DROP INDEX idx_way_tags_way_id",
-		"DROP INDEX idx_relation_tags_relation_id",
-		"DROP INDEX idx_way_nodes_node_id"
-	};
-	private static final String[] PRE_LOAD_SQL_WAY_BBOX = {
-		"DROP INDEX idx_ways_bbox"
-	};
-	private static final String[] PRE_LOAD_SQL_WAY_LINESTRING = {
-		"DROP INDEX idx_ways_linestring"
-	};
-	
-	private static final String[] POST_LOAD_SQL = {
-		"ALTER TABLE ONLY users ADD CONSTRAINT pk_users PRIMARY KEY (id)",
-		"ALTER TABLE ONLY nodes ADD CONSTRAINT pk_nodes PRIMARY KEY (id)",
-		"ALTER TABLE ONLY ways ADD CONSTRAINT pk_ways PRIMARY KEY (id)",
-		"ALTER TABLE ONLY way_nodes ADD CONSTRAINT pk_way_nodes PRIMARY KEY (way_id, sequence_id)",
-		"ALTER TABLE ONLY relations ADD CONSTRAINT pk_relations PRIMARY KEY (id)",
-		"ALTER TABLE ONLY relation_members ADD CONSTRAINT pk_relation_members PRIMARY KEY (relation_id, sequence_id)",
-		"CREATE INDEX idx_node_tags_node_id ON node_tags USING btree (node_id)",
-		"CREATE INDEX idx_nodes_geom ON nodes USING gist (geom)",
-		"CREATE INDEX idx_way_tags_way_id ON way_tags USING btree (way_id)",
-		"CREATE INDEX idx_relation_tags_relation_id ON relation_tags USING btree (relation_id)",
-		"CREATE INDEX idx_way_nodes_node_id ON way_nodes USING btree (node_id)"
-	};
-	private static final String[] POST_LOAD_SQL_WAY_BBOX = {
-		"CREATE INDEX idx_ways_bbox ON ways USING gist (bbox)"
-	};
-	private static final String[] POST_LOAD_SQL_WAY_LINESTRING = {
-		"CREATE INDEX idx_ways_linestring ON ways USING gist (linestring)"
-	};
-	private static final String POST_LOAD_SQL_POPULATE_WAY_BBOX =
-		"UPDATE ways SET bbox = ("
-		+ "SELECT Envelope(Collect(geom)) FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
-		+ " WHERE way_nodes.way_id = ways.id"
-		+ ")";
-	private static final String POST_LOAD_SQL_POPULATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ "SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ "SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ ") c"
-		+ ")";
-	
-	
-	private DatabaseContext dbCtx;
-	private DatabaseCapabilityChecker capabilityChecker;
-	private boolean populateBbox;
-	private boolean populateLinestring;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            Provides access to the database.
-	 * @param populateBbox
-	 *            If true, the bbox colum on the way table will be populated
-	 *            after load.
-	 * @param populateLinestring
-	 *            If true, the linestring column on the way table will be
-	 *            populated after load.
-	 */
-	public IndexManager(DatabaseContext dbCtx, boolean populateBbox, boolean populateLinestring) {
-		this.dbCtx = dbCtx;
-		this.populateBbox = populateBbox;
-		this.populateLinestring = populateLinestring;
-		
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-	}
-	
-	
-	/**
-	 * Drops indexes and constraints in the database.
-	 */
-	public void prepareForLoad() {
-		LOG.fine("Running pre-load SQL statements.");
-		for (int i = 0; i < PRE_LOAD_SQL.length; i++) {
-			LOG.finer("SQL: " + PRE_LOAD_SQL[i]);
-			dbCtx.executeStatement(PRE_LOAD_SQL[i]);
-		}
-		if (capabilityChecker.isWayBboxSupported()) {
-			LOG.fine("Running pre-load bbox SQL statements.");
-			for (int i = 0; i < PRE_LOAD_SQL_WAY_BBOX.length; i++) {
-				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_BBOX[i]);
-				dbCtx.executeStatement(PRE_LOAD_SQL_WAY_BBOX[i]);
-			}
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			LOG.fine("Running pre-load linestring SQL statements.");
-			for (int i = 0; i < PRE_LOAD_SQL_WAY_LINESTRING.length; i++) {
-				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_LINESTRING[i]);
-				dbCtx.executeStatement(PRE_LOAD_SQL_WAY_LINESTRING[i]);
-			}
-		}
-		LOG.fine("Pre-load SQL statements complete.");
-	}
-	
-	
-	/**
-	 * Creates indexes in the database and populates derived columns.
-	 */
-	public void completeAfterLoad() {
-		LOG.fine("Running post-load SQL.");
-		for (int i = 0; i < POST_LOAD_SQL.length; i++) {
-			LOG.finer("SQL: " + POST_LOAD_SQL[i]);
-			dbCtx.executeStatement(POST_LOAD_SQL[i]);
-		}
-		if (capabilityChecker.isWayBboxSupported()) {
-			LOG.fine("Running post-load bbox SQL statements.");
-			if (populateBbox) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_BBOX);
-				dbCtx.executeStatement(POST_LOAD_SQL_POPULATE_WAY_BBOX);
-			}
-			for (int i = 0; i < POST_LOAD_SQL_WAY_BBOX.length; i++) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_BBOX[i]);
-				dbCtx.executeStatement(POST_LOAD_SQL_WAY_BBOX[i]);
-			}
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			LOG.fine("Running post-load linestring SQL statements.");
-			if (populateLinestring) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
-				dbCtx.executeStatement(POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
-			}
-			for (int i = 0; i < POST_LOAD_SQL_WAY_LINESTRING.length; i++) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_LINESTRING[i]);
-				dbCtx.executeStatement(POST_LOAD_SQL_WAY_LINESTRING[i]);
-			}
-		}
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java
deleted file mode 100644
index 6b76355..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/NodeDao.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-
-
-/**
- * Performs all node-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class NodeDao extends EntityDao<Node> {
-	private static final String SQL_UPDATE_WAY_BBOX =
-		"UPDATE ways w SET bbox = ("
-		+ " SELECT Envelope(Collect(n.geom))"
-		+ " FROM nodes n INNER JOIN way_nodes wn ON wn.node_id = n.id"
-		+ " WHERE wn.way_id = w.id"
-		+ " )"
-		+ " WHERE w.id IN ("
-		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
-		+ " )";
-	private static final String SQL_UPDATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ " ) c"
-		+ " )"
-		+ " WHERE w.id IN ("
-		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
-		+ " )";
-	
-	
-	private DatabaseCapabilityChecker capabilityChecker;
-	private PreparedStatement updateWayBboxStatement;
-	private PreparedStatement updateWayLinestringStatement;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public NodeDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		super(dbCtx, new NodeMapper(), actionDao);
-		
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void loadFeatures(long entityId, Node entity) {
-		// Nodes have no additional features.
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void modifyEntity(Node entity) {
-		super.modifyEntity(entity);
-		
-		if (capabilityChecker.isWayBboxSupported()) {
-			if (updateWayBboxStatement == null) {
-				updateWayBboxStatement = prepareStatement(SQL_UPDATE_WAY_BBOX);
-			}
-			
-			try {
-				int prmIndex;
-				
-				prmIndex = 1;
-				updateWayBboxStatement.setLong(prmIndex++, entity.getId());
-				updateWayBboxStatement.executeUpdate();
-				
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Update bbox failed for node " + entity.getId() + ".");
-			}
-		}
-		
-		if (capabilityChecker.isWayLinestringSupported()) {
-			if (updateWayLinestringStatement == null) {
-				updateWayLinestringStatement = prepareStatement(SQL_UPDATE_WAY_LINESTRING);
-			}
-			
-			try {
-				int prmIndex;
-				
-				prmIndex = 1;
-				updateWayLinestringStatement.setLong(prmIndex++, entity.getId());
-				updateWayLinestringStatement.executeUpdate();
-				
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Update linestring failed for node " + entity.getId() + ".");
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReleasableIterator<Node> iterate() {
-		return new NodeReader(getDatabaseContext());
-	}
-}
diff --git a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java b/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java
deleted file mode 100644
index cff82e5..0000000
--- a/pgsimple/src/main/java/org/openstreetmap/osmosis/pgsimple/v0_6/impl/WayDao.java
+++ /dev/null
@@ -1,182 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
-
-
-/**
- * Performs all way-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class WayDao extends EntityDao<Way> {
-	
-	private static final String SQL_UPDATE_WAY_BBOX =
-		"UPDATE ways SET bbox = ("
-		+ " SELECT Envelope(Collect(geom))"
-		+ " FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
-		+ " WHERE way_nodes.way_id = ways.id"
-		+ " )"
-		+ " WHERE ways.id = ?";
-	private static final String SQL_UPDATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ " ) c"
-		+ " )"
-		+ " WHERE w.id  = ?";
-	
-	private DatabaseCapabilityChecker capabilityChecker;
-	private EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>> wayNodeDao;
-	private PreparedStatement updateWayBboxStatement;
-	private PreparedStatement updateWayLinestringStatement;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public WayDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		super(dbCtx, new WayMapper(), actionDao);
-		
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-		wayNodeDao = new EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>>(dbCtx, new WayNodeMapper());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void loadFeatures(long entityId, Way entity) {
-		entity.getWayNodes().addAll(wayNodeDao.getAllRaw(entityId));
-	}
-
-
-	/**
-	 * Adds the specified way node list to the database.
-	 * 
-	 * @param entityId
-	 *            The identifier of the entity to add these features to.
-	 * @param wayNodeList
-	 *            The list of features to add.
-	 */
-	private void addWayNodeList(long entityId, List<WayNode> wayNodeList) {
-		List<DbOrderedFeature<WayNode>> dbList;
-		
-		dbList = new ArrayList<DbOrderedFeature<WayNode>>(wayNodeList.size());
-		
-		for (int i = 0; i < wayNodeList.size(); i++) {
-			dbList.add(new DbOrderedFeature<WayNode>(entityId, wayNodeList.get(i), i));
-		}
-		
-		wayNodeDao.addAll(dbList);
-	}
-	
-	
-	/**
-	 * Updates the bounding box column for the specified way.
-	 * 
-	 * @param wayId
-	 *            The way bounding box.
-	 */
-	private void updateWayGeometries(long wayId) {
-		if (capabilityChecker.isWayBboxSupported()) {
-			if (updateWayBboxStatement == null) {
-				updateWayBboxStatement = prepareStatement(SQL_UPDATE_WAY_BBOX);
-			}
-			
-			try {
-				int prmIndex;
-				
-				prmIndex = 1;
-				updateWayBboxStatement.setLong(prmIndex++, wayId);
-				updateWayBboxStatement.executeUpdate();
-				
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Update bbox failed for way " + wayId + ".");
-			}
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			if (updateWayLinestringStatement == null) {
-				updateWayLinestringStatement = prepareStatement(SQL_UPDATE_WAY_LINESTRING);
-			}
-			
-			try {
-				int prmIndex;
-				
-				prmIndex = 1;
-				updateWayLinestringStatement.setLong(prmIndex++, wayId);
-				updateWayLinestringStatement.executeUpdate();
-				
-			} catch (SQLException e) {
-				throw new OsmosisRuntimeException("Update linestring failed for way " + wayId + ".");
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void addEntity(Way entity) {
-		super.addEntity(entity);
-		
-		addWayNodeList(entity.getId(), entity.getWayNodes());
-		
-		updateWayGeometries(entity.getId());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void modifyEntity(Way entity) {
-		long wayId;
-		
-		super.modifyEntity(entity);
-		
-		wayId = entity.getId();
-		wayNodeDao.removeList(wayId);
-		addWayNodeList(entity.getId(), entity.getWayNodes());
-		
-		updateWayGeometries(entity.getId());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void removeEntity(long entityId) {
-		wayNodeDao.removeList(entityId);
-		
-		super.removeEntity(entityId);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReleasableIterator<Way> iterate() {
-		return new WayReader(getDatabaseContext());
-	}
-}
diff --git a/pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm
deleted file mode 100644
index d0486aa..0000000
--- a/pgsimple/src/test/resources/data/template/v0_6/db-changeset-expected.osm
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1-revised"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <member type="node" ref="7" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm b/pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm
deleted file mode 100644
index e37f94e..0000000
--- a/pgsimple/src/test/resources/data/template/v0_6/db-dataset-expected.osm
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
-    <tag k="change" v="new tag"/>
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="7" version="16" timestamp="2008-01-02T18:19:20Z" changeset="93" lat="-11" lon="-12">
-    <tag k="change" v="new node"/>
-    <tag k="created_by" v="Me7"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm b/pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm
deleted file mode 100644
index 0792662..0000000
--- a/pgsimple/src/test/resources/data/template/v0_6/db-snapshot.osm
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/pgsnapshot/.checkstyle b/pgsnapshot/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/pgsnapshot/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/pgsnapshot/.classpath b/pgsnapshot/.classpath
deleted file mode 100644
index 4e9e591..0000000
--- a/pgsnapshot/.classpath
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Dataset"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Hstore"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/aopalliance-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-dbcp-1.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-pool-1.5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgis-jdbc-1.3.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/postgresql-9.0-801.jdbc4.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-aop-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-asm-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-beans-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-context-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-core-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-expression-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-jdbc-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/spring-tx-3.0.6.RELEASE.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/pgsnapshot/.gitignore b/pgsnapshot/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/pgsnapshot/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/pgsnapshot/.project b/pgsnapshot/.project
deleted file mode 100644
index 5e75911..0000000
--- a/pgsnapshot/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-PgSnapshot</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/pgsnapshot/build.xml b/pgsnapshot/build.xml
deleted file mode 100644
index 6ed3c37..0000000
--- a/pgsnapshot/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.PgSnapshot" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/pgsnapshot/ivy.xml b/pgsnapshot/ivy.xml
deleted file mode 100644
index f7712aa..0000000
--- a/pgsnapshot/ivy.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-pgsnapshot"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-hstore" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-dataset" rev="${project.version}" conf="test->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-    	
-    	<dependency org="org.springframework" name="spring-jdbc" rev="${dependency.version.spring}" conf="compile->default"/>
-        <dependency org="commons-dbcp" name="commons-dbcp" rev="${dependency.version.commons-dbcp}" conf="compile->default"/>
-    	<dependency org="postgresql" name="postgresql" rev="${dependency.version.postgresql}" conf="compile->default"/>
-        <dependency org="org.postgis" name="postgis-jdbc" rev="${dependency.version.postgis}" conf="compile->default">
-        	<exclude module="postgis-stubs"/>
-        </dependency>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java
deleted file mode 100644
index 8cceefd..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/DatabaseContext.java
+++ /dev/null
@@ -1,320 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.common;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.sql.DataSource;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.postgresql.copy.CopyManager;
-import org.postgresql.core.BaseConnection;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-import org.springframework.jdbc.datasource.DataSourceTransactionManager;
-import org.springframework.jdbc.datasource.DataSourceUtils;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.DefaultTransactionDefinition;
-import org.springframework.transaction.support.TransactionCallback;
-import org.springframework.transaction.support.TransactionTemplate;
-
-
-/**
- * This class manages the lifecycle of JDBC objects to minimise the risk of connection leaks and to
- * support a consistent approach to database access.
- * 
- * @author Brett Henderson
- */
-public class DatabaseContext {
-
-    private static final Logger LOG = Logger.getLogger(DatabaseContext.class.getName());
-
-    private DataSourceManager dataSourceManager;
-    private DataSource dataSource;
-    private PlatformTransactionManager txnManager;
-    private TransactionTemplate txnTemplate;
-    private TransactionStatus transaction;
-    private JdbcTemplate jdbcTemplate;
-    private SimpleJdbcTemplate simpleJdbcTemplate;
-    
-
-    /**
-     * Creates a new instance.
-     * 
-     * @param loginCredentials Contains all information required to connect to the database.
-     */
-    public DatabaseContext(DatabaseLoginCredentials loginCredentials) {
-    	dataSourceManager = new DataSourceManager(loginCredentials);
-    	dataSource = dataSourceManager.getDataSource();
-    	txnManager = new DataSourceTransactionManager(dataSource);
-    	txnTemplate = new TransactionTemplate(txnManager);
-    	jdbcTemplate = new JdbcTemplate(dataSource);
-    	simpleJdbcTemplate = new SimpleJdbcTemplate(jdbcTemplate);
-    	
-    	setStatementFetchSizeForStreaming();
-    }
-
-
-	/**
-	 * Begins a new database transaction. This is not required if
-	 * executeWithinTransaction is being used.
-	 */
-    public void beginTransaction() {
-    	if (transaction != null) {
-    		throw new OsmosisRuntimeException("A transaction is already active.");
-    	}
-    	
-    	transaction = txnManager.getTransaction(new DefaultTransactionDefinition());
-    }
-    
-    
-    /**
-     * Commits an existing database transaction.
-     */
-    public void commitTransaction() {
-    	if (transaction == null) {
-    		throw new OsmosisRuntimeException("No transaction is currently active.");
-    	}
-    	
-    	try {
-    		txnManager.commit(transaction);
-    	} finally {
-    		transaction = null;
-    	}
-    }
-    
-    
-    /**
-     * Gets the jdbc template which provides simple access to database functions.
-     * 
-     * @return The jdbc template.
-     */
-    public SimpleJdbcTemplate getSimpleJdbcTemplate() {
-    	return simpleJdbcTemplate;
-    }
-    
-    
-    /**
-     * Gets the jdbc template which provides access to database functions.
-     * 
-     * @return The jdbc template.
-     */
-    public JdbcTemplate getJdbcTemplate() {
-    	return jdbcTemplate;
-    }
-    
-    
-	/**
-	 * Invokes the provided callback code within a transaction.
-	 * 
-	 * @param txnCallback
-	 *            The logic to be invoked within a transaction.
-	 * @param <T>
-	 *            The return type of the transaction callback.
-	 * 
-	 * @return The result.
-	 */
-    public <T> Object executeWithinTransaction(TransactionCallback<T> txnCallback) {
-    	return txnTemplate.execute(txnCallback);
-    }
-    
-    
-    private void setStatementFetchSizeForStreaming() {
-        jdbcTemplate.setFetchSize(10000);
-    }
-	
-
-    /**
-     * Releases all database resources. This method is guaranteed not to throw transactions and
-     * should always be called in a finally block whenever this class is used.
-     */
-    public void release() {
-    	if (transaction != null) {
-    		try {
-    			txnManager.rollback(transaction);
-    		} finally {
-    			transaction = null;
-    		}
-    	}
-    	
-    	dataSourceManager.release();
-    }
-    
-
-    /**
-     * Indicates if the specified column exists in the database.
-     * 
-     * @param tableName The table to check for.
-     * @param columnName The column to check for.
-     * @return True if the column exists, false otherwise.
-     */
-    public boolean doesColumnExist(String tableName, String columnName) {
-        ResultSet resultSet = null;
-        boolean result;
-
-        try {
-        	Connection connection;
-        	
-            LOG.finest("Checking if column {" + columnName + "} in table {" + tableName + "} exists.");
-
-            // This connection may not be freed if an exception occurs. It's a small chance and the
-			// additional code to avoid it is cumbersome.
-            connection = DataSourceUtils.getConnection(dataSource);
-            
-            resultSet = connection.getMetaData().getColumns(null, null, tableName, columnName);
-            result = resultSet.next();
-            resultSet.close();
-            resultSet = null;
-            
-            DataSourceUtils.releaseConnection(connection, dataSource);
-
-            return result;
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to check for the existence of column " + tableName + "."
-                    + columnName + ".", e);
-        } finally {
-            if (resultSet != null) {
-                try {
-                    resultSet.close();
-                } catch (SQLException e) {
-                    // We are already in an error condition so log and continue.
-                    LOG.log(Level.WARNING, "Unable to close column existence result set.", e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Indicates if the specified table exists in the database.
-     * 
-     * @param tableName The table to check for.
-     * @return True if the table exists, false otherwise.
-     */
-    public boolean doesTableExist(String tableName) {
-        ResultSet resultSet = null;
-        boolean result;
-
-        try {
-        	Connection connection;
-        	
-            LOG.finest("Checking if table {" + tableName + "} exists.");
-
-            // This connection may not be freed if an exception occurs. It's a small chance and the
-			// additional code to avoid it is cumbersome.
-            connection = DataSourceUtils.getConnection(dataSource);
-
-            resultSet = connection.getMetaData().getTables(null, null, tableName, new String[] {"TABLE"});
-            result = resultSet.next();
-            resultSet.close();
-            resultSet = null;
-            
-            DataSourceUtils.releaseConnection(connection, dataSource);
-
-            return result;
-
-        } catch (SQLException e) {
-            throw new OsmosisRuntimeException("Unable to check for the existence of table " + tableName + ".", e);
-        } finally {
-            if (resultSet != null) {
-                try {
-                    resultSet.close();
-                } catch (SQLException e) {
-                    // We are already in an error condition so log and continue.
-                    LOG.log(Level.WARNING, "Unable to close table existence result set.", e);
-                }
-            }
-        }
-    }
-
-
-	/**
-	 * Loads a table from a COPY file.
-	 * 
-	 * @param copyFile
-	 *            The file to be loaded.
-	 * @param tableName
-	 *            The table to load the data into.
-	 * @param columns
-	 *            The columns to be loaded (optional).
-	 */
-    public void loadCopyFile(File copyFile, String tableName, String ... columns) {
-    	CopyManager copyManager;
-    	InputStream inStream = null;
-    	
-    	try {
-    		StringBuilder copyStatement;
-    		InputStream bufferedInStream;
-    		Connection conn;
-    		
-    		copyStatement = new StringBuilder();
-    		copyStatement.append("COPY ");
-    		copyStatement.append(tableName);
-    		if (columns.length > 0) {
-    			copyStatement.append('(');
-    			for (int i = 0; i < columns.length; i++) {
-    				if (i > 0) {
-    					copyStatement.append(',');
-    				}
-    				copyStatement.append(columns[i]);
-    			}
-    			copyStatement.append(')');
-    		}
-    		copyStatement.append(" FROM STDIN");
-    		
-    		inStream = new FileInputStream(copyFile);
-    		bufferedInStream = new BufferedInputStream(inStream, 65536);
-    		
-    		conn = DataSourceUtils.getConnection(dataSource);
-    		try {
-	    		copyManager = new CopyManager(conn.unwrap(BaseConnection.class));
-	    		
-	    		copyManager.copyIn(copyStatement.toString(), bufferedInStream);
-    		} finally {
-    			DataSourceUtils.releaseConnection(conn, dataSource);
-    		}
-			
-    		inStream.close();
-			inStream = null;
-			
-    	} catch (IOException e) {
-    		throw new OsmosisRuntimeException("Unable to process COPY file " + copyFile + ".", e);
-    	} catch (SQLException e) {
-    		throw new OsmosisRuntimeException("Unable to process COPY file " + copyFile + ".", e);
-    	} finally {
-    		if (inStream != null) {
-				try {
-					inStream.close();
-				} catch (IOException e) {
-					LOG.log(Level.SEVERE, "Unable to close COPY file.", e);
-				}
-				inStream = null;
-			}
-    	}
-    }
-    
-
-    /**
-     * Enforces cleanup of any remaining resources during garbage collection. This is a safeguard
-     * and should not be required if release is called appropriately.
-     * 
-     * @throws Throwable If a problem occurs during finalization.
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        release();
-
-        super.finalize();
-    }
-
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java
deleted file mode 100644
index 368da9b..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/common/SchemaVersionValidator.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.common;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Reads the version number stored in the schema_info table and verifies that it
- * matches the expected version.
- * 
- * @author Brett Henderson
- */
-public class SchemaVersionValidator {
-	private static final String SELECT_SQL = "SELECT version FROM schema_info";
-	
-	private DatabasePreferences preferences;
-	private SimpleJdbcTemplate jdbcTemplate;
-	private boolean validated;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Provides access to the database.
-	 * @param preferences
-	 *            The database preferences.
-	 */
-	public SchemaVersionValidator(SimpleJdbcTemplate jdbcTemplate, DatabasePreferences preferences) {
-		this.jdbcTemplate = jdbcTemplate;
-		this.preferences = preferences;
-	}
-	
-	
-	/**
-	 * Validates that the version number of the schema matches the expected
-	 * version. This method caches the result allowing it to be called multiple
-	 * times without a performance penalty.
-	 * 
-	 * @param expectedVersion
-	 *            The expected version number.
-	 */
-	public void validateVersion(int expectedVersion) {
-		if (!validated) {
-			validateDBVersion(expectedVersion);
-			
-			validated = true;
-		}
-	}
-	
-	
-	/**
-	 * Performs the database lookup and validates the expected version.
-	 * 
-	 * @param expectedVersion
-	 *            The expected version number.
-	 */
-	private void validateDBVersion(int expectedVersion) {
-		if (preferences.getValidateSchemaVersion()) {
-			int dbVersion;
-			
-			dbVersion = jdbcTemplate.queryForInt(SELECT_SQL);
-			
-			if (dbVersion != expectedVersion) {
-				throw new OsmosisRuntimeException(
-					"The database schema version of " + dbVersion
-					+ " does not match the expected version of " + expectedVersion + "."
-				);
-			}
-		}
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java
deleted file mode 100644
index 55716c4..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriter.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.ActionChangeWriter;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.ChangeWriter;
-
-
-/**
- * A change sink writing to database tables. This aims to be suitable for
- * running at regular intervals with database overhead proportional to changeset
- * size.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlChangeWriter implements ChangeSink {
-	
-	private ChangeWriter changeWriter;
-	private Map<ChangeAction, ActionChangeWriter> actionWriterMap;
-	private DatabaseContext dbCtx;
-	private SchemaVersionValidator schemaVersionValidator;
-	private boolean initialized;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 */
-	public PostgreSqlChangeWriter(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
-		dbCtx = new DatabaseContext(loginCredentials);
-		changeWriter = new ChangeWriter(dbCtx);
-		actionWriterMap = new HashMap<ChangeAction, ActionChangeWriter>();
-		actionWriterMap.put(ChangeAction.Create, new ActionChangeWriter(changeWriter, ChangeAction.Create));
-		actionWriterMap.put(ChangeAction.Modify, new ActionChangeWriter(changeWriter, ChangeAction.Modify));
-		actionWriterMap.put(ChangeAction.Delete, new ActionChangeWriter(changeWriter, ChangeAction.Delete));
-		
-		schemaVersionValidator = new SchemaVersionValidator(dbCtx.getSimpleJdbcTemplate(), preferences);
-		
-		initialized = false;
-	}
-	
-	
-	private void initialize() {
-		if (!initialized) {
-			dbCtx.beginTransaction();
-			
-			initialized = true;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer change) {
-		ChangeAction action;
-		
-		initialize();
-		
-		// Verify that the schema version is supported.
-		schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
-		
-		action = change.getAction();
-		
-		if (!actionWriterMap.containsKey(action)) {
-			throw new OsmosisRuntimeException("The action " + action + " is unrecognized.");
-		}
-		
-		// Process the entity using the action writer appropriate for the change
-		// action.
-		change.getEntityContainer().process(actionWriterMap.get(action));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		initialize();
-		
-		changeWriter.complete();
-		
-		dbCtx.commitTransaction();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		changeWriter.release();
-		
-		dbCtx.release();
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java
deleted file mode 100644
index b2c1bf6..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlChangeWriterFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.ChangeSinkManager;
-
-
-/**
- * The task manager factory for a database change writer.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlChangeWriterFactory extends DatabaseTaskManagerFactory {
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		DatabaseLoginCredentials loginCredentials;
-		DatabasePreferences preferences;
-		
-		// Get the task arguments.
-		loginCredentials = getDatabaseLoginCredentials(taskConfig);
-		preferences = getDatabasePreferences(taskConfig);
-		
-		return new ChangeSinkManager(
-			taskConfig.getId(),
-			new PostgreSqlChangeWriter(
-				loginCredentials,
-				preferences
-			),
-			taskConfig.getPipeArgs()
-		);
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java
deleted file mode 100644
index 062e9a2..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriter.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetBuilder;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetLoader;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.DatabaseCapabilityChecker;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.TempCopyFileset;
-
-
-/**
- * An OSM data sink for storing all data to a database using the COPY command.
- * This task is intended for writing to an empty database.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlCopyWriter implements Sink {
-	
-	private static final Logger LOG = Logger.getLogger(PostgreSqlCopyWriter.class.getName());
-	
-	private CopyFilesetBuilder copyFilesetBuilder;
-	private CopyFilesetLoader copyFilesetLoader;
-	private TempCopyFileset copyFileset;
-	private DatabaseLoginCredentials loginCredentials;
-	private DatabasePreferences preferences;
-	private NodeLocationStoreType storeType;
-	private boolean populateBbox;
-	private boolean populateLinestring;
-	private boolean initialized;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public PostgreSqlCopyWriter(
-			DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-			NodeLocationStoreType storeType) {
-		this.loginCredentials = loginCredentials;
-		this.preferences = preferences;
-		this.storeType = storeType;
-		
-		copyFileset = new TempCopyFileset();
-	}
-	
-	
-	private void initialize() {
-		if (!initialized) {
-			DatabaseContext dbCtx;
-			DatabaseCapabilityChecker capabilityChecker;
-			
-			LOG.fine("Initializing the database and temporary processing files.");
-			
-			dbCtx = new DatabaseContext(loginCredentials);
-			try {
-				capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-
-				populateBbox = capabilityChecker.isWayBboxSupported();
-				populateLinestring = capabilityChecker.isWayLinestringSupported();
-			} finally {
-				dbCtx.release();
-			}
-
-			copyFilesetBuilder =
-				new CopyFilesetBuilder(copyFileset, populateBbox, populateLinestring, storeType);
-			
-			copyFilesetLoader = new CopyFilesetLoader(loginCredentials, preferences, copyFileset);
-			
-			LOG.fine("Processing input data, building geometries and creating database load files.");
-			
-			initialized = true;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		initialize();
-		
-		copyFilesetBuilder.process(entityContainer);
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the files, then loads the files into the database. 
-	 */
-	public void complete() {
-		initialize();
-		
-		copyFilesetBuilder.complete();
-		
-		LOG.fine("All data has been received, beginning database load.");
-		copyFilesetLoader.run();
-		
-		LOG.fine("Processing complete.");
-	}
-	
-	
-	/**
-	 * Releases all database resources.
-	 */
-	public void release() {
-		if (copyFilesetBuilder != null) {
-			copyFilesetBuilder.release();
-			copyFilesetBuilder = null;
-		}
-		copyFileset.release();
-		
-		initialized = false;
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java
deleted file mode 100644
index e7340c4..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlCopyWriterFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.database.DatabaseTaskManagerFactory;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
-
-
-/**
- * The task manager factory for a database writer using the PostgreSQL COPY method.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlCopyWriterFactory extends DatabaseTaskManagerFactory {
-	private static final String ARG_NODE_LOCATION_STORE_TYPE = "nodeLocationStoreType";
-	private static final String DEFAULT_NODE_LOCATION_STORE_TYPE = "CompactTempFile";
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		DatabaseLoginCredentials loginCredentials;
-		DatabasePreferences preferences;
-		NodeLocationStoreType storeType;
-		
-		// Get the task arguments.
-		loginCredentials = getDatabaseLoginCredentials(taskConfig);
-		preferences = getDatabasePreferences(taskConfig);
-		storeType = Enum.valueOf(
-				NodeLocationStoreType.class,
-				getStringArgument(taskConfig, ARG_NODE_LOCATION_STORE_TYPE, DEFAULT_NODE_LOCATION_STORE_TYPE));
-		
-		return new SinkManager(
-			taskConfig.getId(),
-			new PostgreSqlCopyWriter(loginCredentials, preferences,	storeType),
-			taskConfig.getPipeArgs()
-		);
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java
deleted file mode 100644
index 70cc750..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriter.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.DirectoryCopyFileset;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.impl.CopyFilesetBuilder;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * An OSM data sink for storing all data to database dump files. This task is
- * intended for populating an empty database.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlDumpWriter implements Sink {
-	
-	private CopyFilesetBuilder copyFilesetBuilder;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param filePrefix
-	 *            The prefix to prepend to all generated file names.
-	 * @param enableBboxBuilder
-	 *            If true, the way bbox geometry is built during processing
-	 *            instead of relying on the database to build them after import.
-	 *            This increases processing but is faster than relying on the
-	 *            database.
-	 * @param enableLinestringBuilder
-	 *            If true, the way linestring geometry is built during
-	 *            processing instead of relying on the database to build them
-	 *            after import. This increases processing but is faster than
-	 *            relying on the database.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public PostgreSqlDumpWriter(
-			File filePrefix, boolean enableBboxBuilder,
-			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
-		DirectoryCopyFileset copyFileset;
-		
-		copyFileset = new DirectoryCopyFileset(filePrefix);
-		
-		copyFilesetBuilder =
-			new CopyFilesetBuilder(copyFileset, enableBboxBuilder, enableLinestringBuilder, storeType);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		copyFilesetBuilder.process(entityContainer);
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the database and commits. 
-	 */
-	public void complete() {
-		copyFilesetBuilder.complete();
-	}
-	
-	
-	/**
-	 * Releases all database resources.
-	 */
-	public void release() {
-		copyFilesetBuilder.release();
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java
deleted file mode 100644
index 427b623..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlDumpWriterFactory.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
-
-
-/**
- * The task manager factory for a database dump writer.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlDumpWriterFactory extends TaskManagerFactory {
-	private static final String ARG_ENABLE_BBOX_BUILDER = "enableBboxBuilder";
-	private static final String ARG_ENABLE_LINESTRING_BUILDER = "enableLinestringBuilder";
-	private static final String ARG_FILE_NAME = "directory";
-	private static final String ARG_NODE_LOCATION_STORE_TYPE = "nodeLocationStoreType";
-	private static final boolean DEFAULT_ENABLE_BBOX_BUILDER = false;
-	private static final boolean DEFAULT_ENABLE_LINESTRING_BUILDER = false;
-	private static final String DEFAULT_FILE_PREFIX = "pgimport";
-	private static final String DEFAULT_NODE_LOCATION_STORE_TYPE = "CompactTempFile";
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		String filePrefixString;
-		File filePrefix;
-		boolean enableBboxBuilder;
-		boolean enableLinestringBuilder;
-		NodeLocationStoreType storeType;
-		
-		// Get the task arguments.
-		filePrefixString = getStringArgument(
-				taskConfig, ARG_FILE_NAME, DEFAULT_FILE_PREFIX);
-		enableBboxBuilder = getBooleanArgument(
-				taskConfig, ARG_ENABLE_BBOX_BUILDER, DEFAULT_ENABLE_BBOX_BUILDER);
-		enableLinestringBuilder = getBooleanArgument(
-				taskConfig, ARG_ENABLE_LINESTRING_BUILDER, DEFAULT_ENABLE_LINESTRING_BUILDER);
-		storeType = Enum.valueOf(
-				NodeLocationStoreType.class,
-				getStringArgument(taskConfig, ARG_NODE_LOCATION_STORE_TYPE, DEFAULT_NODE_LOCATION_STORE_TYPE));
-		
-		// Create a file object representing the directory from the file name provided.
-		filePrefix = new File(filePrefixString);
-		
-		return new SinkManager(
-			taskConfig.getId(),
-			new PostgreSqlDumpWriter(filePrefix, enableBboxBuilder, enableLinestringBuilder, storeType),
-			taskConfig.getPipeArgs()
-		);
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java
deleted file mode 100644
index 6750ba2..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTruncator.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.task.common.RunnableTask;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
-
-
-/**
- * A standalone OSM task with no inputs or outputs that truncates tables in a
- * PostgreSQL database. This is used for removing all existing data from tables.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlTruncator implements RunnableTask {
-	
-	private static final Logger LOG = Logger.getLogger(PostgreSqlTruncator.class.getName());
-	
-	
-	// These tables will be truncated.
-	private static final String[] SQL_TABLE_NAMES = {
-		"actions",
-		"users",
-		"nodes", "node_tags",
-		"ways", "way_tags", "way_nodes",
-		"relations", "relation_tags", "relation_members"
-	};
-	
-	
-	private DatabaseContext dbCtx;
-	private SchemaVersionValidator schemaVersionValidator;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 */
-	public PostgreSqlTruncator(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
-		dbCtx = new DatabaseContext(loginCredentials);
-		
-		schemaVersionValidator = new SchemaVersionValidator(dbCtx.getSimpleJdbcTemplate(), preferences);
-	}
-	
-	
-	/**
-	 * Truncates all data from the database.
-	 */
-	public void run() {
-		try {
-			schemaVersionValidator.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
-			
-			dbCtx.beginTransaction();
-			
-			LOG.fine("Truncating tables.");
-			for (int i = 0; i < SQL_TABLE_NAMES.length; i++) {
-				if (dbCtx.doesTableExist(SQL_TABLE_NAMES[i])) {
-					LOG.finer("Truncating table " + SQL_TABLE_NAMES[i] + ".");
-					dbCtx.getSimpleJdbcTemplate().update("TRUNCATE " + SQL_TABLE_NAMES[i]);
-				} else {
-					LOG.finer("Skipping table " + SQL_TABLE_NAMES[i] + " which doesn't exist in the current schema.");
-				}
-			}
-			
-			LOG.fine("Committing changes.");
-			dbCtx.commitTransaction();
-			
-			LOG.fine("Vacuuming database.");
-			dbCtx.getSimpleJdbcTemplate().update("VACUUM ANALYZE");
-			LOG.fine("Complete.");
-			
-		} finally {
-			dbCtx.release();
-		}
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java
deleted file mode 100644
index 1812cc6..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionChangeWriter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-
-
-/**
- * Writes entities to a database according to a specific action.
- * 
- * @author Brett Henderson
- */
-public class ActionChangeWriter implements EntityProcessor {
-	private ChangeWriter changeWriter;
-	private ChangeAction action;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param changeWriter
-	 *            The underlying change writer.
-	 * @param action
-	 *            The action to apply to all writes.
-	 */
-	public ActionChangeWriter(ChangeWriter changeWriter, ChangeAction action) {
-		this.changeWriter = changeWriter;
-		this.action = action;
-	}
-	
-	
-	/**
-     * {@inheritDoc}
-     */
-    public void process(BoundContainer bound) {
-        // Do nothing.
-    }
-    
-    
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer nodeContainer) {
-		changeWriter.write(nodeContainer.getEntity(), action);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		changeWriter.write(wayContainer.getEntity(), action);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		changeWriter.write(relationContainer.getEntity(), action);
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java
deleted file mode 100644
index f1b5bc6..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ActionDao.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Performs all action db operations.
- * 
- * @author Brett Henderson
- */
-public class ActionDao {
-	private static final String SQL_INSERT = "INSERT INTO actions(data_type, action, id) VALUES(?, ?, ?)";
-	private static final String SQL_TRUNCATE = "TRUNCATE actions";
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private DatabaseCapabilityChecker capabilityChecker;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 */
-	public ActionDao(DatabaseContext dbCtx) {
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-	}
-	
-	
-	/**
-	 * Adds the specified action to the database.
-	 * 
-	 * @param dataType The type of data being represented by this action. 
-	 * @param action The action being performed on the data.
-	 * @param id The identifier of the data. 
-	 */
-	public void addAction(ActionDataType dataType, ChangesetAction action, long id) {
-		if (capabilityChecker.isActionSupported()) {
-			jdbcTemplate.update(SQL_INSERT, dataType.getDatabaseValue(), action.getDatabaseValue(), id);
-		}
-	}
-	
-	
-	/**
-	 * Removes all action records.
-	 */
-	public void truncate() {
-		if (capabilityChecker.isActionSupported()) {
-			jdbcTemplate.update(SQL_TRUNCATE);
-		}
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java
deleted file mode 100644
index c4eb089..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/ChangeWriter.java
+++ /dev/null
@@ -1,221 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.sql.CallableStatement;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
-import org.springframework.jdbc.core.CallableStatementCreator;
-import org.springframework.jdbc.core.SqlParameter;
-
-
-/**
- * Writes changes to a database.
- * 
- * @author Brett Henderson
- */
-public class ChangeWriter {
-	
-	private DatabaseContext dbCtx;
-	private ActionDao actionDao;
-	private UserDao userDao;
-	private NodeDao nodeDao;
-	private WayDao wayDao;
-	private RelationDao relationDao;
-	private Set<Integer> userSet;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 */
-	public ChangeWriter(DatabaseContext dbCtx) {
-		this.dbCtx = dbCtx;
-		
-		actionDao = new ActionDao(dbCtx);
-		userDao = new UserDao(dbCtx, actionDao);
-		nodeDao = new NodeDao(dbCtx, actionDao);
-		wayDao = new WayDao(dbCtx, actionDao);
-		relationDao = new RelationDao(dbCtx, actionDao);
-		
-		userSet = new HashSet<Integer>();
-	}
-
-
-	/**
-	 * Writes the specified user to the database.
-	 * 
-	 * @param user
-	 *            The user to write.
-	 */
-	private void writeUser(OsmUser user) {
-		// Entities without a user assigned should not be written.
-		if (!OsmUser.NONE.equals(user)) {
-			// Users will only be updated in the database once per changeset
-			// run.
-			if (!userSet.contains(user.getId())) {
-				int userId;
-				OsmUser existingUser;
-
-				userId = user.getId();
-
-				try {
-					existingUser = userDao.getUser(userId);
-
-					if (!user.equals(existingUser)) {
-						userDao.updateUser(user);
-					}
-
-				} catch (NoSuchRecordException e) {
-					userDao.addUser(user);
-				}
-
-				userSet.add(user.getId());
-			}
-		}
-	}
-
-
-	/**
-	 * Performs any validation and pre-processing required for all entity types.
-	 */
-	private void processEntityPrerequisites(Entity entity) {
-		// We can't write an entity with a null timestamp.
-		if (entity.getTimestamp() == null) {
-			throw new OsmosisRuntimeException("Entity(" + entity.getType()
-					+ ") " + entity.getId() + " does not have a timestamp set.");
-		}
-		
-		// Process the user data.
-		writeUser(entity.getUser());
-	}
-
-
-	/**
-	 * Writes the specified node change to the database.
-	 * 
-	 * @param node
-	 *            The node to be written.
-	 * @param action
-	 *            The change to be applied.
-	 */
-	public void write(Node node, ChangeAction action) {
-		processEntityPrerequisites(node);
-
-		// If this is a create or modify, we must create or modify the records
-		// in the database. Note that we don't use the input source to
-		// distinguish between create and modify, we make this determination
-		// based on our current data set.
-		if (ChangeAction.Create.equals(action)
-				|| ChangeAction.Modify.equals(action)) {
-			if (nodeDao.exists(node.getId())) {
-				nodeDao.modifyEntity(node);
-			} else {
-				nodeDao.addEntity(node);
-			}
-
-		} else {
-			// Remove the node from the database.
-			nodeDao.removeEntity(node.getId());
-		}
-	}
-
-
-	/**
-	 * Writes the specified way change to the database.
-	 * 
-	 * @param way
-	 *            The way to be written.
-	 * @param action
-	 *            The change to be applied.
-	 */
-	public void write(Way way, ChangeAction action) {
-		processEntityPrerequisites(way);
-
-		// If this is a create or modify, we must create or modify the records
-		// in the database. Note that we don't use the input source to
-		// distinguish between create and modify, we make this determination
-		// based on our current data set.
-		if (ChangeAction.Create.equals(action)
-				|| ChangeAction.Modify.equals(action)) {
-			if (wayDao.exists(way.getId())) {
-				wayDao.modifyEntity(way);
-			} else {
-				wayDao.addEntity(way);
-			}
-
-		} else {
-			// Remove the way from the database.
-			wayDao.removeEntity(way.getId());
-		}
-	}
-
-
-	/**
-	 * Writes the specified relation change to the database.
-	 * 
-	 * @param relation
-	 *            The relation to be written.
-	 * @param action
-	 *            The change to be applied.
-	 */
-	public void write(Relation relation, ChangeAction action) {
-		processEntityPrerequisites(relation);
-
-		// If this is a create or modify, we must create or modify the records
-		// in the database. Note that we don't use the input source to
-		// distinguish between create and modify, we make this determination
-		// based on our current data set.
-		if (ChangeAction.Create.equals(action)
-				|| ChangeAction.Modify.equals(action)) {
-			if (relationDao.exists(relation.getId())) {
-				relationDao.modifyEntity(relation);
-			} else {
-				relationDao.addEntity(relation);
-			}
-
-		} else {
-			// Remove the relation from the database.
-			relationDao.removeEntity(relation.getId());
-		}
-	}
-
-
-	/**
-	 * Performs post-change database updates.
-	 */
-	public void complete() {
-		dbCtx.getJdbcTemplate().call(
-				new CallableStatementCreator() {
-					@Override
-					public CallableStatement createCallableStatement(Connection con) throws SQLException {
-						return con.prepareCall("{call osmosisUpdate()}");
-					}
-				}, new ArrayList<SqlParameter>());
-		
-		// Clear all action records.
-		actionDao.truncate();
-	}
-
-
-	/**
-	 * Releases all resources.
-	 */
-	public void release() {
-		// Nothing to do.
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java
deleted file mode 100644
index fd587a2..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetBuilder.java
+++ /dev/null
@@ -1,249 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.lifecycle.CompletableContainer;
-import org.openstreetmap.osmosis.pgsnapshot.common.CopyFileWriter;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsnapshot.common.PointBuilder;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.hstore.PGHStore;
-
-
-/**
- * An OSM data sink for storing all data to a set of database dump files. These
- * files can be used for populating an empty database.
- * 
- * @author Brett Henderson
- */
-public class CopyFilesetBuilder implements Sink, EntityProcessor {
-	
-	private boolean enableBboxBuilder;
-	private boolean enableLinestringBuilder;
-	private WayGeometryBuilder wayGeometryBuilder;
-	private CompletableContainer writerContainer;
-	private MemberTypeValueMapper memberTypeValueMapper;
-	private CopyFileWriter userWriter;
-	private CopyFileWriter nodeWriter;
-	private CopyFileWriter wayWriter;
-	private CopyFileWriter wayNodeWriter;
-	private CopyFileWriter relationWriter;
-	private CopyFileWriter relationMemberWriter;
-	private PointBuilder pointBuilder;
-	private Set<Integer> userSet;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param copyFileset
-	 *            The set of COPY files to be populated.
-	 * @param enableBboxBuilder
-	 *            If true, the way bbox geometry is built during processing
-	 *            instead of relying on the database to build them after import.
-	 *            This increases processing but is faster than relying on the
-	 *            database.
-	 * @param enableLinestringBuilder
-	 *            If true, the way linestring geometry is built during
-	 *            processing instead of relying on the database to build them
-	 *            after import. This increases processing but is faster than
-	 *            relying on the database.
-	 * @param storeType
-	 *            The node location storage type used by the geometry builders.
-	 */
-	public CopyFilesetBuilder(
-			CopyFileset copyFileset, boolean enableBboxBuilder,
-			boolean enableLinestringBuilder, NodeLocationStoreType storeType) {
-		this.enableBboxBuilder = enableBboxBuilder;
-		this.enableLinestringBuilder = enableLinestringBuilder;
-		
-		writerContainer = new CompletableContainer();
-		
-		userWriter = writerContainer.add(new CopyFileWriter(copyFileset.getUserFile()));
-		nodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getNodeFile()));
-		wayWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayFile()));
-		wayNodeWriter = writerContainer.add(new CopyFileWriter(copyFileset.getWayNodeFile()));
-		relationWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationFile()));
-		relationMemberWriter = writerContainer.add(new CopyFileWriter(copyFileset.getRelationMemberFile()));
-		
-		pointBuilder = new PointBuilder();
-		wayGeometryBuilder = new WayGeometryBuilder(storeType);
-		memberTypeValueMapper = new MemberTypeValueMapper();
-		memberTypeValueMapper = new MemberTypeValueMapper();
-		
-		userSet = new HashSet<Integer>();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		OsmUser user;
-		
-		// Write a user entry if the user doesn't already exist.
-		user = entityContainer.getEntity().getUser();
-		if (!user.equals(OsmUser.NONE)) {
-			if (!userSet.contains(user.getId())) {
-				userWriter.writeField(user.getId());
-				userWriter.writeField(user.getName());
-				userWriter.endRecord();
-				
-				userSet.add(user.getId());
-			}
-		}
-		
-		// Process the entity itself.
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// Do nothing.
-	}
-	
-	
-	private PGHStore buildTags(Entity entity) {
-		PGHStore tags;
-		
-		tags = new PGHStore();
-		for (Tag tag : entity.getTags()) {
-			tags.put(tag.getKey(), tag.getValue());
-		}
-		
-		return tags;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer nodeContainer) {
-		Node node;
-		
-		node = nodeContainer.getEntity();
-		
-		nodeWriter.writeField(node.getId());
-		nodeWriter.writeField(node.getVersion());
-		nodeWriter.writeField(node.getUser().getId());
-		nodeWriter.writeField(node.getTimestamp());
-		nodeWriter.writeField(node.getChangesetId());
-		nodeWriter.writeField(buildTags(node));
-		nodeWriter.writeField(pointBuilder.createPoint(node.getLatitude(), node.getLongitude()));
-		nodeWriter.endRecord();
-		
-		if (enableBboxBuilder || enableLinestringBuilder) {
-			wayGeometryBuilder.addNodeLocation(node);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer wayContainer) {
-		Way way;
-		int sequenceId;
-		List<Long> nodeIds;
-		
-		way = wayContainer.getEntity();
-		
-		nodeIds = new ArrayList<Long>(way.getWayNodes().size());
-		for (WayNode wayNode : way.getWayNodes()) {
-			nodeIds.add(wayNode.getNodeId());
-		}
-		
-		// Ignore ways with a single node because they can't be loaded into postgis.
-		if (way.getWayNodes().size() > 1) {
-			wayWriter.writeField(way.getId());
-			wayWriter.writeField(way.getVersion());
-			wayWriter.writeField(way.getUser().getId());
-			wayWriter.writeField(way.getTimestamp());
-			wayWriter.writeField(way.getChangesetId());
-			wayWriter.writeField(buildTags(way));
-			wayWriter.writeField(nodeIds);
-			if (enableBboxBuilder) {
-				wayWriter.writeField(wayGeometryBuilder.createWayBbox(way));
-			}
-			if (enableLinestringBuilder) {
-				wayWriter.writeField(wayGeometryBuilder.createWayLinestring(way));
-			}
-			wayWriter.endRecord();
-			
-			sequenceId = 0;
-			for (WayNode wayNode : way.getWayNodes()) {
-				wayNodeWriter.writeField(way.getId());
-				wayNodeWriter.writeField(wayNode.getNodeId());
-				wayNodeWriter.writeField(sequenceId++);
-				wayNodeWriter.endRecord();
-			}
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer relationContainer) {
-		Relation relation;
-		int memberSequenceId;
-		
-		relation = relationContainer.getEntity();
-		
-		relationWriter.writeField(relation.getId());
-		relationWriter.writeField(relation.getVersion());
-		relationWriter.writeField(relation.getUser().getId());
-		relationWriter.writeField(relation.getTimestamp());
-		relationWriter.writeField(relation.getChangesetId());
-		relationWriter.writeField(buildTags(relation));
-		relationWriter.endRecord();
-		
-		memberSequenceId = 0;
-		for (RelationMember member : relation.getMembers()) {
-			relationMemberWriter.writeField(relation.getId());
-			relationMemberWriter.writeField(member.getMemberId());
-			relationMemberWriter.writeField(memberTypeValueMapper.getMemberType(member.getMemberType()));
-			relationMemberWriter.writeField(member.getMemberRole());
-			relationMemberWriter.writeField(memberSequenceId++);
-			relationMemberWriter.endRecord();
-		}
-	}
-	
-	
-	/**
-	 * Writes any buffered data to the database and commits. 
-	 */
-	public void complete() {
-		writerContainer.complete();
-	}
-	
-	
-	/**
-	 * Releases all resources.
-	 */
-	public void release() {
-		writerContainer.release();
-		wayGeometryBuilder.release();
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java
deleted file mode 100644
index 30d651b..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/CopyFilesetLoader.java
+++ /dev/null
@@ -1,127 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.PostgreSqlVersionConstants;
-
-
-/**
- * Loads a COPY fileset into a database.
- * 
- * @author Brett Henderson
- */
-public class CopyFilesetLoader implements Runnable {
-	
-	private static final Logger LOG = Logger.getLogger(CopyFilesetLoader.class.getName());
-	
-	
-	private static String[] appendColumn(String[] columns, String newColumn) {
-		String[] result;
-		
-		result = new String[columns.length + 1];
-		
-		System.arraycopy(columns, 0, result, 0, columns.length);
-		result[columns.length] = newColumn;
-		
-		return result;
-	}
-	
-	
-	private static final String[] COMMON_COLUMNS = {"id", "version", "user_id", "tstamp", "changeset_id", "tags"};
-	private static final String[] NODE_COLUMNS = appendColumn(COMMON_COLUMNS, "geom");
-	private static final String[] WAY_COLUMNS = appendColumn(COMMON_COLUMNS, "nodes");
-	private static final String[] RELATION_COLUMNS = COMMON_COLUMNS;
-	
-	
-	private DatabaseLoginCredentials loginCredentials;
-	private DatabasePreferences preferences;
-	private CopyFileset copyFileset;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 * @param copyFileset
-	 *            The set of COPY files to be loaded into the database.
-	 */
-	public CopyFilesetLoader(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences,
-			CopyFileset copyFileset) {
-		this.loginCredentials = loginCredentials;
-		this.preferences = preferences;
-		this.copyFileset = copyFileset;
-	}
-    
-
-    /**
-     * Reads all data from the database and send it to the sink.
-     */
-    public void run() {
-    	DatabaseContext dbCtx = new DatabaseContext(loginCredentials);
-    	
-    	try {
-    		DatabaseCapabilityChecker capabilityChecker;
-			IndexManager indexManager;
-			String[] wayColumns;
-			
-			dbCtx.beginTransaction();
-			
-			capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-			new SchemaVersionValidator(dbCtx.getSimpleJdbcTemplate(), preferences)
-				.validateVersion(PostgreSqlVersionConstants.SCHEMA_VERSION);
-			
-			wayColumns = WAY_COLUMNS;
-			if (capabilityChecker.isWayBboxSupported()) {
-				wayColumns = appendColumn(wayColumns, "bbox");
-			}
-			if (capabilityChecker.isWayLinestringSupported()) {
-				wayColumns = appendColumn(wayColumns, "linestring");
-			}
-    		
-    		indexManager = new IndexManager(dbCtx, false, false);
-    		
-			// Drop all constraints and indexes.
-			indexManager.prepareForLoad();
-    		
-    		LOG.finer("Loading users.");
-    		dbCtx.loadCopyFile(copyFileset.getUserFile(), "users");
-    		LOG.finer("Loading nodes.");
-    		dbCtx.loadCopyFile(copyFileset.getNodeFile(), "nodes", NODE_COLUMNS);
-    		LOG.finer("Loading ways.");
-    		dbCtx.loadCopyFile(copyFileset.getWayFile(), "ways", wayColumns);
-    		LOG.finer("Loading way nodes.");
-    		dbCtx.loadCopyFile(copyFileset.getWayNodeFile(), "way_nodes");
-    		LOG.finer("Loading relations.");
-    		dbCtx.loadCopyFile(copyFileset.getRelationFile(), "relations", RELATION_COLUMNS);
-    		LOG.finer("Loading relation members.");
-    		dbCtx.loadCopyFile(copyFileset.getRelationMemberFile(), "relation_members");
-    		LOG.finer("Committing changes.");
-    		
-    		LOG.fine("Data load complete.");
-    		
-    		// Add all constraints and indexes.
-    		indexManager.completeAfterLoad();
-    		
-    		dbCtx.commitTransaction();
-    		
-    		LOG.fine("Clustering database.");
-    		dbCtx.getSimpleJdbcTemplate().update("CLUSTER");
-    		
-    		LOG.fine("Vacuuming database.");
-    		dbCtx.getSimpleJdbcTemplate().update("VACUUM ANALYZE");
-    		
-    		LOG.fine("Complete.");
-    		
-    	} finally {
-    		dbCtx.release();
-    	}
-    }
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java
deleted file mode 100644
index 9b3d724..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityDao.java
+++ /dev/null
@@ -1,255 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.database.FeaturePopulator;
-import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableContainer;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntitySubClassComparator;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
-import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
-import org.openstreetmap.osmosis.pgsnapshot.common.RowMapperRowCallbackListener;
-import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Provides functionality common to all top level entity daos.
- * 
- * @author Brett Henderson
- * @param <T>
- *            The entity type to be supported.
- */
-public abstract class EntityDao<T extends Entity> {
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private ActionDao actionDao;
-	private EntityMapper<T> entityMapper;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Provides access to the database.
-	 * @param entityMapper
-	 *            Provides entity type specific JDBC support.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	protected EntityDao(SimpleJdbcTemplate jdbcTemplate, EntityMapper<T> entityMapper, ActionDao actionDao) {
-		this.jdbcTemplate = jdbcTemplate;
-		this.entityMapper = entityMapper;
-		this.actionDao = actionDao;
-	}
-	
-	
-	/**
-	 * Gets the entity mapper implementation.
-	 * 
-	 * @return The entity mapper.
-	 */
-	protected EntityMapper<T> getEntityMapper() {
-		return entityMapper;
-	}
-	
-	
-	/**
-	 * Checks if the specified entity exists in the database.
-	 * 
-	 * @param entityId
-	 *            The unique identifier of the entity.
-	 * @return True if the entity exists in the database.
-	 */
-	public boolean exists(long entityId) {
-		return jdbcTemplate.queryForInt(entityMapper.getSqlSelectCount(true), entityId) > 0;
-	}
-	
-	
-	/**
-	 * Loads the specified entity from the database.
-	 * 
-	 * @param entityId
-	 *            The unique identifier of the entity.
-	 * @return The loaded entity.
-	 */
-	public T getEntity(long entityId) {
-		T entity;
-		
-		try {
-			entity = jdbcTemplate.queryForObject(entityMapper.getSqlSelect(true, false), entityMapper.getRowMapper(),
-					entityId);
-		} catch (EmptyResultDataAccessException e) {
-			throw new NoSuchRecordException(entityMapper.getEntityName()
-					+ " " + entityId + " doesn't exist.", e);
-		}
-		
-		return entity;
-	}
-	
-	
-	/**
-	 * Adds the specified entity to the database.
-	 * 
-	 * @param entity
-	 *            The entity to add.
-	 */
-	public void addEntity(T entity) {
-		Map<String, Object> args;
-		
-		args = new HashMap<String, Object>();
-		entityMapper.populateEntityParameters(args, entity);
-		
-		jdbcTemplate.update(entityMapper.getSqlInsert(1), args);
-		
-		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.CREATE, entity.getId());
-	}
-	
-	
-	/**
-	 * Updates the specified entity details in the database.
-	 * 
-	 * @param entity
-	 *            The entity to update.
-	 */
-	public void modifyEntity(T entity) {
-		Map<String, Object> args;
-		
-		args = new HashMap<String, Object>();
-		entityMapper.populateEntityParameters(args, entity);
-		
-		jdbcTemplate.update(entityMapper.getSqlUpdate(true), args);
-		
-		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.MODIFY, entity.getId());
-	}
-	
-	
-	/**
-	 * Removes the specified entity from the database.
-	 * 
-	 * @param entityId
-	 *            The id of the entity to remove.
-	 */
-	public void removeEntity(long entityId) {
-		Map<String, Object> args;
-		
-		args = new HashMap<String, Object>();
-		args.put("id", entityId);
-		
-		jdbcTemplate.update(entityMapper.getSqlDelete(true), args);
-		
-		actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.DELETE, entityId);
-	}
-	
-	
-	private ReleasableIterator<T> getFeaturelessEntity(String tablePrefix) {
-		FileBasedSort<T> sortingStore;
-		
-		sortingStore =
-			new FileBasedSort<T>(
-				new SingleClassObjectSerializationFactory(entityMapper.getEntityClass()),
-				new EntitySubClassComparator<T>(new EntityByTypeThenIdComparator()), true);
-		
-		try {
-			String sql;
-			SortingStoreRowMapperListener<T> storeListener;
-			RowMapperRowCallbackListener<T> rowCallbackListener;
-			ReleasableIterator<T> resultIterator;
-			
-			sql = entityMapper.getSqlSelect(tablePrefix, false, false);
-			
-			// Sends all received data into the object store.
-			storeListener = new SortingStoreRowMapperListener<T>(sortingStore);
-			// Converts result set rows into objects and passes them into the store.
-			rowCallbackListener = new RowMapperRowCallbackListener<T>(entityMapper.getRowMapper(), storeListener);
-			
-			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
-			jdbcTemplate.getJdbcOperations().query(sql, rowCallbackListener);
-			
-			// Open a iterator on the store that will release the store upon completion.
-			resultIterator = new StoreReleasingIterator<T>(sortingStore.iterate(), sortingStore);
-			
-			// The store itself shouldn't be released now that it has been attached to the iterator.
-			sortingStore = null;
-			
-			return resultIterator;
-			
-		} finally {
-			if (sortingStore != null) {
-				sortingStore.release();
-			}
-		}
-	}
-	
-	
-	/**
-	 * Gets the feature populators for the entity type.
-	 * 
-	 * @param tablePrefix
-	 *            The prefix for the entity table name. This allows another table to be queried if
-	 *            necessary such as a temporary results table.
-	 * @return The feature populators.
-	 */
-	protected abstract List<FeaturePopulator<T>> getFeaturePopulators(String tablePrefix);
-	
-	
-	/**
-	 * Returns an iterator providing access to all entities in the database.
-	 * 
-	 * @param tablePrefix
-	 *            The prefix for the entity table name. This allows another table to be queried if
-	 *            necessary such as a temporary results table.
-	 * @return The entity iterator.
-	 */
-	public ReleasableIterator<T> iterate(String tablePrefix) {
-		ReleasableContainer releasableContainer;
-		
-		releasableContainer = new ReleasableContainer();
-		
-		try {
-			ReleasableIterator<T> entityIterator;
-			List<FeaturePopulator<T>> featurePopulators;
-			
-			// Create the featureless entity iterator but also store it temporarily in the
-			// releasable container so that it will get freed if we fail during retrieval of feature
-			// populators.
-			entityIterator = releasableContainer.add(getFeaturelessEntity(tablePrefix));
-			
-			// Retrieve the feature populators also adding them to the temporary releasable container.
-			featurePopulators = getFeaturePopulators(tablePrefix);
-			for (FeaturePopulator<T> featurePopulator : featurePopulators) {
-				releasableContainer.add(featurePopulator);
-			}
-			
-			// Build an entity reader capable of merging all sources together.
-			entityIterator = new EntityReader<T>(entityIterator, featurePopulators);
-			
-			// The sources are now all attached to the history reader so we don't want to release
-			// them in the finally block.
-			releasableContainer.clear();
-			
-			return entityIterator;
-			
-		} finally {
-			releasableContainer.release();
-		}
-	}
-	
-	
-	/**
-	 * Returns an iterator providing access to all entities in the database.
-	 * 
-	 * @return The entity iterator.
-	 */
-	public ReleasableIterator<T> iterate() {
-		return iterate("");
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java
deleted file mode 100644
index 031c38f..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/EntityFeatureDao.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.database.DbFeature;
-import org.openstreetmap.osmosis.core.store.Storeable;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Provides functionality common to all entity feature daos.
- * 
- * @author Brett Henderson
- * @param <Tef>
- *            The entity feature type to be supported.
- * @param <Tdb>
- *            The entity feature database wrapper type to be used.
- */
-public class EntityFeatureDao<Tef extends Storeable, Tdb extends DbFeature<Tef>> {
-	private EntityFeatureMapper<Tdb> entityFeatureMapper;
-	private SimpleJdbcTemplate jdbcTemplate;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param jdbcTemplate
-	 *            Provides access to the database.
-	 * @param entityFeatureMapper
-	 *            Provides entity type specific JDBC support.
-	 */
-	protected EntityFeatureDao(SimpleJdbcTemplate jdbcTemplate, EntityFeatureMapper<Tdb> entityFeatureMapper) {
-		this.jdbcTemplate = jdbcTemplate;
-		
-		this.entityFeatureMapper = entityFeatureMapper;
-	}
-	
-	
-	/**
-	 * Loads all instances of this feature for the specified entity from the database.
-	 * 
-	 * @param entityId
-	 *            The unique identifier of the entity.
-	 * @return All instances of this feature type for the entity.
-	 */
-	public Collection<Tdb> getAll(long entityId) {
-		return jdbcTemplate.query(entityFeatureMapper.getSqlSelect("", true, true), entityFeatureMapper.getRowMapper());
-	}
-	
-	
-	/**
-	 * Loads all instances of this feature for the specified entity from the database.
-	 * 
-	 * @param entityId
-	 *            The unique identifier of the entity.
-	 * @return All instances of this feature type for the entity.
-	 */
-	public Collection<Tef> getAllRaw(long entityId) {
-		Collection<Tdb> dbFeatures;
-		Collection<Tef> rawFeatures;
-		
-		dbFeatures = getAll(entityId);
-		rawFeatures = new ArrayList<Tef>(dbFeatures.size());
-		for (Tdb dbFeature : dbFeatures) {
-			rawFeatures.add(dbFeature.getFeature());
-		}
-		
-		return rawFeatures;
-	}
-	
-	
-	/**
-	 * Adds the specified features to the database.
-	 * 
-	 * @param features
-	 *            The features to add.
-	 */
-	public void addAll(Collection<Tdb> features) {
-		Map<String, Object> args;
-		
-		args = new HashMap<String, Object>();
-		
-		for (Tdb feature : features) {
-			args.clear();
-			entityFeatureMapper.populateParameters(args, feature);
-			
-			jdbcTemplate.update(entityFeatureMapper.getSqlInsert(1), args);
-		}
-	}
-	
-	
-	/**
-	 * Removes the specified feature list from the database.
-	 * 
-	 * @param entityId
-	 *            The id of the entity to remove.
-	 */
-	public void removeList(long entityId) {
-		jdbcTemplate.update(entityFeatureMapper.getSqlDelete(true), entityId);
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java
deleted file mode 100644
index 4901c52..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/IndexManager.java
+++ /dev/null
@@ -1,155 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Drops and creates indexes in support of bulk load activities.
- * 
- * @author Brett Henderson
- */
-public class IndexManager {
-	
-	private static final Logger LOG = Logger.getLogger(IndexManager.class.getName());
-	
-	
-	private static final String[] PRE_LOAD_SQL = {
-		"ALTER TABLE users DROP CONSTRAINT pk_users",
-		"ALTER TABLE nodes DROP CONSTRAINT pk_nodes",
-		"ALTER TABLE ways DROP CONSTRAINT pk_ways",
-		"ALTER TABLE way_nodes DROP CONSTRAINT pk_way_nodes",
-		"ALTER TABLE relations DROP CONSTRAINT pk_relations",
-		"ALTER TABLE relation_members DROP CONSTRAINT pk_relation_members",
-		"DROP INDEX idx_nodes_geom",
-		"DROP INDEX idx_way_nodes_node_id",
-		"DROP INDEX idx_relation_members_member_id_and_type"
-	};
-	private static final String[] PRE_LOAD_SQL_WAY_BBOX = {
-		"DROP INDEX idx_ways_bbox"
-	};
-	private static final String[] PRE_LOAD_SQL_WAY_LINESTRING = {
-		"DROP INDEX idx_ways_linestring"
-	};
-	
-	private static final String[] POST_LOAD_SQL = {
-		"ALTER TABLE ONLY users ADD CONSTRAINT pk_users PRIMARY KEY (id)",
-		"ALTER TABLE ONLY nodes ADD CONSTRAINT pk_nodes PRIMARY KEY (id)",
-		"ALTER TABLE ONLY ways ADD CONSTRAINT pk_ways PRIMARY KEY (id)",
-		"ALTER TABLE ONLY way_nodes ADD CONSTRAINT pk_way_nodes PRIMARY KEY (way_id, sequence_id)",
-		"ALTER TABLE ONLY relations ADD CONSTRAINT pk_relations PRIMARY KEY (id)",
-		"ALTER TABLE ONLY relation_members ADD CONSTRAINT pk_relation_members PRIMARY KEY (relation_id, sequence_id)",
-		"CREATE INDEX idx_nodes_geom ON nodes USING gist (geom)",
-		"CREATE INDEX idx_way_nodes_node_id ON way_nodes USING btree (node_id)",
-		"CREATE INDEX idx_relation_members_member_id_and_type ON relation_members USING btree (member_id, member_type)"
-	};
-	private static final String[] POST_LOAD_SQL_WAY_BBOX = {
-		"CREATE INDEX idx_ways_bbox ON ways USING gist (bbox)"
-	};
-	private static final String[] POST_LOAD_SQL_WAY_LINESTRING = {
-		"CREATE INDEX idx_ways_linestring ON ways USING gist (linestring)"
-	};
-	private static final String POST_LOAD_SQL_POPULATE_WAY_BBOX =
-		"UPDATE ways SET bbox = ("
-		+ "SELECT Envelope(Collect(geom)) FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
-		+ " WHERE way_nodes.way_id = ways.id"
-		+ ")";
-	private static final String POST_LOAD_SQL_POPULATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ "SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ "SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ ") c"
-		+ ")";
-	
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private DatabaseCapabilityChecker capabilityChecker;
-	private boolean populateBbox;
-	private boolean populateLinestring;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            Provides access to the database.
-	 * @param populateBbox
-	 *            If true, the bbox colum on the way table will be populated
-	 *            after load.
-	 * @param populateLinestring
-	 *            If true, the linestring column on the way table will be
-	 *            populated after load.
-	 */
-	public IndexManager(DatabaseContext dbCtx, boolean populateBbox, boolean populateLinestring) {
-		this.populateBbox = populateBbox;
-		this.populateLinestring = populateLinestring;
-		
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-	}
-	
-	
-	/**
-	 * Drops indexes and constraints in the database.
-	 */
-	public void prepareForLoad() {
-		LOG.fine("Running pre-load SQL statements.");
-		for (int i = 0; i < PRE_LOAD_SQL.length; i++) {
-			LOG.finer("SQL: " + PRE_LOAD_SQL[i]);
-			jdbcTemplate.update(PRE_LOAD_SQL[i]);
-		}
-		if (capabilityChecker.isWayBboxSupported()) {
-			LOG.fine("Running pre-load bbox SQL statements.");
-			for (int i = 0; i < PRE_LOAD_SQL_WAY_BBOX.length; i++) {
-				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_BBOX[i]);
-				jdbcTemplate.update(PRE_LOAD_SQL_WAY_BBOX[i]);
-			}
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			LOG.fine("Running pre-load linestring SQL statements.");
-			for (int i = 0; i < PRE_LOAD_SQL_WAY_LINESTRING.length; i++) {
-				LOG.finer("SQL: " + PRE_LOAD_SQL_WAY_LINESTRING[i]);
-				jdbcTemplate.update(PRE_LOAD_SQL_WAY_LINESTRING[i]);
-			}
-		}
-		LOG.fine("Pre-load SQL statements complete.");
-	}
-	
-	
-	/**
-	 * Creates indexes in the database and populates derived columns.
-	 */
-	public void completeAfterLoad() {
-		LOG.fine("Running post-load SQL.");
-		for (int i = 0; i < POST_LOAD_SQL.length; i++) {
-			LOG.finer("SQL: " + POST_LOAD_SQL[i]);
-			jdbcTemplate.update(POST_LOAD_SQL[i]);
-		}
-		if (capabilityChecker.isWayBboxSupported()) {
-			LOG.fine("Running post-load bbox SQL statements.");
-			if (populateBbox) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_BBOX);
-				jdbcTemplate.update(POST_LOAD_SQL_POPULATE_WAY_BBOX);
-			}
-			for (int i = 0; i < POST_LOAD_SQL_WAY_BBOX.length; i++) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_BBOX[i]);
-				jdbcTemplate.update(POST_LOAD_SQL_WAY_BBOX[i]);
-			}
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			LOG.fine("Running post-load linestring SQL statements.");
-			if (populateLinestring) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
-				jdbcTemplate.update(POST_LOAD_SQL_POPULATE_WAY_LINESTRING);
-			}
-			for (int i = 0; i < POST_LOAD_SQL_WAY_LINESTRING.length; i++) {
-				LOG.finer("SQL: " + POST_LOAD_SQL_WAY_LINESTRING[i]);
-				jdbcTemplate.update(POST_LOAD_SQL_WAY_LINESTRING[i]);
-			}
-		}
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java
deleted file mode 100644
index c012724..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/NodeDao.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.database.FeaturePopulator;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Performs all node-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class NodeDao extends EntityDao<Node> {
-	private static final String SQL_UPDATE_WAY_BBOX =
-		"UPDATE ways w SET bbox = ("
-		+ " SELECT Envelope(Collect(n.geom))"
-		+ " FROM nodes n INNER JOIN way_nodes wn ON wn.node_id = n.id"
-		+ " WHERE wn.way_id = w.id"
-		+ " )"
-		+ " WHERE w.id IN ("
-		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
-		+ " )";
-	private static final String SQL_UPDATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ " ) c"
-		+ " )"
-		+ " WHERE w.id IN ("
-		+ " SELECT w.id FROM ways w INNER JOIN way_nodes wn ON w.id = wn.way_id WHERE wn.node_id = ? GROUP BY w.id"
-		+ " )";
-	
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private DatabaseCapabilityChecker capabilityChecker;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public NodeDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		super(dbCtx.getSimpleJdbcTemplate(), new NodeMapper(), actionDao);
-		
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void modifyEntity(Node entity) {
-		super.modifyEntity(entity);
-		
-		if (capabilityChecker.isWayBboxSupported()) {
-			jdbcTemplate.update(SQL_UPDATE_WAY_BBOX, entity.getId());
-		}
-		
-		if (capabilityChecker.isWayLinestringSupported()) {
-			jdbcTemplate.update(SQL_UPDATE_WAY_LINESTRING, entity.getId());
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected List<FeaturePopulator<Node>> getFeaturePopulators(String tablePrefix) {
-		return Collections.emptyList();
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java
deleted file mode 100644
index 5a2fa0b..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/PostgreSqlDatasetContext.java
+++ /dev/null
@@ -1,441 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainerIterator;
-import org.openstreetmap.osmosis.core.container.v0_6.DatasetContext;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityManager;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainerIterator;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainerIterator;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainerIterator;
-import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
-import org.openstreetmap.osmosis.core.database.DatabasePreferences;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.MultipleSourceIterator;
-import org.openstreetmap.osmosis.core.store.ReleasableAdaptorForIterator;
-import org.openstreetmap.osmosis.core.store.UpcastIterator;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.PolygonBuilder;
-import org.openstreetmap.osmosis.pgsnapshot.common.SchemaVersionValidator;
-import org.openstreetmap.osmosis.pgsnapshot.v0_6.PostgreSqlVersionConstants;
-import org.postgis.PGgeometry;
-import org.postgis.Point;
-import org.postgis.Polygon;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Provides read-only access to a PostgreSQL dataset store. Each thread
- * accessing the store must create its own reader. It is important that all
- * iterators obtained from this reader are released before releasing the reader
- * itself.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlDatasetContext implements DatasetContext {
-	
-	private static final Logger LOG = Logger.getLogger(PostgreSqlDatasetContext.class.getName());
-	
-	
-	private DatabaseLoginCredentials loginCredentials;
-	private DatabasePreferences preferences;
-	private DatabaseCapabilityChecker capabilityChecker;
-	private boolean initialized;
-	private DatabaseContext dbCtx;
-	private SimpleJdbcTemplate jdbcTemplate;
-	private UserDao userDao;
-	private NodeDao nodeDao;
-	private WayDao wayDao;
-	private RelationDao relationDao;
-	private PostgreSqlEntityManager<Node> nodeManager;
-	private PostgreSqlEntityManager<Way> wayManager;
-	private PostgreSqlEntityManager<Relation> relationManager;
-	private PolygonBuilder polygonBuilder;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param loginCredentials
-	 *            Contains all information required to connect to the database.
-	 * @param preferences
-	 *            Contains preferences configuring database behaviour.
-	 */
-	public PostgreSqlDatasetContext(DatabaseLoginCredentials loginCredentials, DatabasePreferences preferences) {
-		this.loginCredentials = loginCredentials;
-		this.preferences = preferences;
-		
-		polygonBuilder = new PolygonBuilder();
-		
-		initialized = false;
-	}
-	
-	
-	/**
-	 * Initialises the database connection and associated data access objects.
-	 */
-	private void initialize() {
-		if (dbCtx == null) {
-			ActionDao actionDao;
-			
-			dbCtx = new DatabaseContext(loginCredentials);
-			jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-			
-			dbCtx.beginTransaction();
-			
-			new SchemaVersionValidator(jdbcTemplate, preferences).validateVersion(
-					PostgreSqlVersionConstants.SCHEMA_VERSION);
-			
-			capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-			
-			actionDao = new ActionDao(dbCtx);
-			userDao = new UserDao(dbCtx, actionDao);
-			nodeDao = new NodeDao(dbCtx, actionDao);
-			wayDao = new WayDao(dbCtx, actionDao);
-			relationDao = new RelationDao(dbCtx, actionDao);
-			
-			nodeManager = new PostgreSqlEntityManager<Node>(nodeDao, userDao);
-			wayManager = new PostgreSqlEntityManager<Way>(wayDao, userDao);
-			relationManager = new PostgreSqlEntityManager<Relation>(relationDao, userDao);
-		}
-		
-		initialized = true;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	@Deprecated
-	public Node getNode(long id) {
-		return getNodeManager().getEntity(id);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	@Deprecated
-	public Way getWay(long id) {
-		return getWayManager().getEntity(id);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	@Deprecated
-	public Relation getRelation(long id) {
-		return getRelationManager().getEntity(id);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityManager<Node> getNodeManager() {
-		if (!initialized) {
-			initialize();
-		}
-		
-		return nodeManager;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityManager<Way> getWayManager() {
-		if (!initialized) {
-			initialize();
-		}
-		
-		return wayManager;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public EntityManager<Relation> getRelationManager() {
-		if (!initialized) {
-			initialize();
-		}
-		
-		return relationManager;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReleasableIterator<EntityContainer> iterate() {
-		List<Bound> bounds;
-		List<ReleasableIterator<EntityContainer>> sources;
-		
-		if (!initialized) {
-			initialize();
-		}
-		
-		// Build the bounds list.
-		bounds = new ArrayList<Bound>();
-		bounds.add(new Bound("Osmosis " + OsmosisConstants.VERSION));
-		
-		sources = new ArrayList<ReleasableIterator<EntityContainer>>();
-		
-		sources.add(new UpcastIterator<EntityContainer, BoundContainer>(
-				new BoundContainerIterator(new ReleasableAdaptorForIterator<Bound>(bounds.iterator()))));
-		sources.add(new UpcastIterator<EntityContainer, NodeContainer>(
-				new NodeContainerIterator(nodeDao.iterate())));
-		sources.add(new UpcastIterator<EntityContainer, WayContainer>(
-				new WayContainerIterator(wayDao.iterate())));
-		sources.add(new UpcastIterator<EntityContainer, RelationContainer>(
-				new RelationContainerIterator(relationDao.iterate())));
-		
-		return new MultipleSourceIterator<EntityContainer>(sources);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ReleasableIterator<EntityContainer> iterateBoundingBox(
-			double left, double right, double top, double bottom, boolean completeWays) {
-		List<Bound> bounds;
-		Point[] bboxPoints;
-		Polygon bboxPolygon;
-		int rowCount;
-		List<ReleasableIterator<EntityContainer>> resultSets;
-		
-		if (!initialized) {
-			initialize();
-		}
-		
-		// Build the bounds list.
-		bounds = new ArrayList<Bound>();
-		bounds.add(new Bound(right, left, top, bottom, "Osmosis " + OsmosisConstants.VERSION));
-		
-		// PostgreSQL sometimes incorrectly chooses to perform full table scans, these options
-		// prevent this. Note that this is not recommended practice according to documentation
-		// but fixing this would require modifying the table statistics gathering
-		// configuration to produce better plans.
-		jdbcTemplate.update("SET enable_seqscan = false");
-		jdbcTemplate.update("SET enable_mergejoin = false");
-		jdbcTemplate.update("SET enable_hashjoin = false");
-		
-		// Build a polygon representing the bounding box.
-		// Sample box for query testing may be:
-		// GeomFromText('POLYGON((144.93912192855174 -37.82981987499741,
-		// 144.93912192855174 -37.79310006709244, 144.98188026000003
-		// -37.79310006709244, 144.98188026000003 -37.82981987499741,
-		// 144.93912192855174 -37.82981987499741))', -1)
-		bboxPoints = new Point[5];
-		bboxPoints[0] = new Point(left, bottom);
-		bboxPoints[1] = new Point(left, top);
-		bboxPoints[2] = new Point(right, top);
-		bboxPoints[3] = new Point(right, bottom);
-		bboxPoints[4] = new Point(left, bottom);
-		bboxPolygon = polygonBuilder.createPolygon(bboxPoints);
-		
-		// Select all nodes inside the box into the node temp table.
-		LOG.finer("Selecting all nodes inside bounding box.");
-		rowCount = jdbcTemplate.update(
-				"CREATE TEMPORARY TABLE bbox_nodes ON COMMIT DROP AS"
-				+ " SELECT * FROM nodes WHERE (geom && ?)",
-				new PGgeometry(bboxPolygon));
-		
-		LOG.finer("Adding a primary key to the temporary nodes table.");
-		jdbcTemplate.update("ALTER TABLE ONLY bbox_nodes ADD CONSTRAINT pk_bbox_nodes PRIMARY KEY (id)");
-		
-		LOG.finer("Updating query analyzer statistics on the temporary nodes table.");
-		jdbcTemplate.update("ANALYZE bbox_nodes");
-		
-		// Select all ways inside the bounding box into the way temp table.
-		if (capabilityChecker.isWayLinestringSupported()) {
-			LOG.finer("Selecting all ways inside bounding box using way linestring geometry.");
-			// We have full way geometry available so select ways
-			// overlapping the requested bounding box.
-			rowCount = jdbcTemplate.update(
-					"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
-					+ " SELECT * FROM ways WHERE (linestring && ?)",
-					new PGgeometry(bboxPolygon));
-			
-		} else if (capabilityChecker.isWayBboxSupported()) {
-			LOG.finer("Selecting all ways inside bounding box using dynamically built"
-					+ " way linestring with way bbox indexing.");
-			// The inner query selects the way id and node coordinates for
-			// all ways constrained by the way bounding box which is
-			// indexed.
-			// The middle query converts the way node coordinates into
-			// linestrings.
-			// The outer query constrains the query to the linestrings
-			// inside the bounding box. These aren't indexed but the inner
-			// query way bbox constraint will minimise the unnecessary data.
-			rowCount = jdbcTemplate.update(
-				"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
-					+ " SELECT w.* FROM ("
-					+ "SELECT c.id AS id, First(c.version) AS version, First(c.user_id) AS user_id,"
-					+ " First(c.tstamp) AS tstamp, First(c.changeset_id) AS changeset_id, First(c.tags) AS tags,"
-					+ " First(c.nodes) AS nodes, ST_MakeLine(c.geom) AS way_line FROM ("
-					+ "SELECT w.*, n.geom AS geom FROM nodes n"
-					+ " INNER JOIN way_nodes wn ON n.id = wn.node_id"
-					+ " INNER JOIN ways w ON wn.way_id = w.id"
-					+ " WHERE (w.bbox && ?) ORDER BY wn.way_id, wn.sequence_id"
-					+ ") c "
-					+ "GROUP BY c.id"
-					+ ") w "
-					+ "WHERE (w.way_line && ?)",
-					new PGgeometry(bboxPolygon),
-					new PGgeometry(bboxPolygon)
-			);
-			
-		} else {
-			LOG.finer("Selecting all way ids inside bounding box using already selected nodes.");
-			// No way bbox support is available so select ways containing
-			// the selected nodes.
-			rowCount = jdbcTemplate.update(
-				"CREATE TEMPORARY TABLE bbox_ways ON COMMIT DROP AS"
-					+ " SELECT w.* FROM ways w"
-					+ " INNER JOIN ("
-					+ " SELECT wn.way_id FROM way_nodes wn"
-					+ " INNER JOIN bbox_nodes n ON wn.node_id = n.id GROUP BY wn.way_id"
-					+ ") wids ON w.id = wids.way_id"
-			);
-		}
-		LOG.finer(rowCount + " rows affected.");
-		
-		LOG.finer("Adding a primary key to the temporary ways table.");
-		jdbcTemplate.update("ALTER TABLE ONLY bbox_ways ADD CONSTRAINT pk_bbox_ways PRIMARY KEY (id)");
-		
-		LOG.finer("Updating query analyzer statistics on the temporary ways table.");
-		jdbcTemplate.update("ANALYZE bbox_ways");
-		
-		// Select all relations containing the nodes or ways into the relation table.
-		LOG.finer("Selecting all relation ids containing selected nodes or ways.");
-		rowCount = jdbcTemplate.update(
-			"CREATE TEMPORARY TABLE bbox_relations ON COMMIT DROP AS"
-				+ " SELECT r.* FROM relations r"
-				+ " INNER JOIN ("
-				+ "    SELECT relation_id FROM ("
-				+ "        SELECT rm.relation_id AS relation_id FROM relation_members rm"
-				+ "        INNER JOIN bbox_nodes n ON rm.member_id = n.id WHERE rm.member_type = 'N' "
-				+ "        UNION "
-				+ "        SELECT rm.relation_id AS relation_id FROM relation_members rm"
-				+ "        INNER JOIN bbox_ways w ON rm.member_id = w.id WHERE rm.member_type = 'W'"
-				+ "     ) rids GROUP BY relation_id"
-				+ ") rids ON r.id = rids.relation_id"
-		);
-		LOG.finer(rowCount + " rows affected.");
-		
-		LOG.finer("Adding a primary key to the temporary relations table.");
-		jdbcTemplate.update("ALTER TABLE ONLY bbox_relations ADD CONSTRAINT pk_bbox_relations PRIMARY KEY (id)");
-		
-		LOG.finer("Updating query analyzer statistics on the temporary relations table.");
-		jdbcTemplate.update("ANALYZE bbox_relations");
-		
-		// Include all relations containing the current relations into the
-		// relation table and repeat until no more inclusions occur.
-		do {
-			LOG.finer("Selecting parent relations of selected relations.");
-			rowCount = jdbcTemplate.update(
-				"INSERT INTO bbox_relations "
-					+ "SELECT r.* FROM relations r INNER JOIN ("
-					+ "    SELECT rm.relation_id FROM relation_members rm"
-					+ "    INNER JOIN bbox_relations br ON rm.member_id = br.id"
-					+ "    WHERE rm.member_type = 'R' AND NOT EXISTS ("
-					+ "        SELECT * FROM bbox_relations br2 WHERE rm.relation_id = br2.id"
-					+ "    ) GROUP BY rm.relation_id"
-					+ ") rids ON r.id = rids.relation_id"
-			);
-			LOG.finer(rowCount + " rows affected.");
-		} while (rowCount > 0);
-		
-		LOG.finer("Updating query analyzer statistics on the temporary relations table.");
-		jdbcTemplate.update("ANALYZE bbox_relations");
-		
-		// If complete ways is set, select all nodes contained by the ways into the node temp table.
-		if (completeWays) {
-			LOG.finer("Selecting all nodes for selected ways.");
-			jdbcTemplate.update("CREATE TEMPORARY TABLE bbox_way_nodes (id bigint) ON COMMIT DROP");
-			jdbcTemplate.queryForList("SELECT unnest_bbox_way_nodes()");
-			jdbcTemplate.update(
-					"CREATE TEMPORARY TABLE bbox_missing_way_nodes ON COMMIT DROP AS "
-					+ "SELECT buwn.id FROM (SELECT DISTINCT bwn.id FROM bbox_way_nodes bwn) buwn "
-					+ "WHERE NOT EXISTS ("
-					+ "    SELECT * FROM bbox_nodes WHERE id = buwn.id"
-					+ ");"
-			);
-			jdbcTemplate.update("ALTER TABLE ONLY bbox_missing_way_nodes"
-					+ " ADD CONSTRAINT pk_bbox_missing_way_nodes PRIMARY KEY (id)");
-			jdbcTemplate.update("ANALYZE bbox_missing_way_nodes");
-			rowCount = jdbcTemplate.update("INSERT INTO bbox_nodes "
-					+ "SELECT n.* FROM nodes n INNER JOIN bbox_missing_way_nodes bwn ON n.id = bwn.id;");
-			LOG.finer(rowCount + " rows affected.");
-		}
-		
-		LOG.finer("Updating query analyzer statistics on the temporary nodes table.");
-		jdbcTemplate.update("ANALYZE bbox_nodes");
-		
-		// Create iterators for the selected records for each of the entity types.
-		LOG.finer("Iterating over results.");
-		resultSets = new ArrayList<ReleasableIterator<EntityContainer>>();
-		resultSets.add(
-				new UpcastIterator<EntityContainer, BoundContainer>(
-						new BoundContainerIterator(new ReleasableAdaptorForIterator<Bound>(bounds.iterator()))));
-		resultSets.add(
-				new UpcastIterator<EntityContainer, NodeContainer>(
-						new NodeContainerIterator(nodeDao.iterate("bbox_"))));
-		resultSets.add(
-				new UpcastIterator<EntityContainer, WayContainer>(
-						new WayContainerIterator(wayDao.iterate("bbox_"))));
-		resultSets.add(
-				new UpcastIterator<EntityContainer, RelationContainer>(
-						new RelationContainerIterator(relationDao.iterate("bbox_"))));
-		
-		// Merge all readers into a single result iterator and return.			
-		return new MultipleSourceIterator<EntityContainer>(resultSets);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		dbCtx.commitTransaction();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		if (dbCtx != null) {
-			dbCtx.release();
-			
-			dbCtx = null;
-		}
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java
deleted file mode 100644
index 2f65717..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/RelationDao.java
+++ /dev/null
@@ -1,195 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.database.DbFeature;
-import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
-import org.openstreetmap.osmosis.core.database.FeaturePopulator;
-import org.openstreetmap.osmosis.core.database.RelationMemberCollectionLoader;
-import org.openstreetmap.osmosis.core.database.SortingStoreRowMapperListener;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.sort.common.FileBasedSort;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.store.StoreReleasingIterator;
-import org.openstreetmap.osmosis.core.store.UpcastIterator;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.RowMapperRowCallbackListener;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Performs all relation-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class RelationDao extends EntityDao<Relation> {
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private EntityFeatureDao<RelationMember, DbOrderedFeature<RelationMember>> relationMemberDao;
-	private RelationMemberMapper relationMemberMapper;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public RelationDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		super(dbCtx.getSimpleJdbcTemplate(), new RelationMapper(), actionDao);
-		
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		relationMemberMapper = new RelationMemberMapper();
-		relationMemberDao = new EntityFeatureDao<RelationMember, DbOrderedFeature<RelationMember>>(
-				dbCtx.getSimpleJdbcTemplate(), relationMemberMapper);
-	}
-	
-	
-	private void loadFeatures(long entityId, Relation entity) {
-		entity.getMembers().addAll(relationMemberDao.getAllRaw(entityId));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Relation getEntity(long entityId) {
-		Relation entity;
-		
-		entity = super.getEntity(entityId);
-		
-		loadFeatures(entityId, entity);
-		
-		return entity;
-	}
-	
-	
-	/**
-	 * Adds the specified relation member list to the database.
-	 * 
-	 * @param entityId
-	 *            The identifier of the entity to add these features to.
-	 * @param memberList
-	 *            The list of features to add.
-	 */
-	private void addMembers(long entityId, List<RelationMember> memberList) {
-		List<DbOrderedFeature<RelationMember>> dbList;
-		
-		dbList = new ArrayList<DbOrderedFeature<RelationMember>>(memberList.size());
-
-		for (int i = 0; i < memberList.size(); i++) {
-			dbList.add(new DbOrderedFeature<RelationMember>(entityId, memberList.get(i), i));
-		}
-		
-		relationMemberDao.addAll(dbList);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void addEntity(Relation entity) {
-		super.addEntity(entity);
-		
-		addMembers(entity.getId(), entity.getMembers());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void modifyEntity(Relation entity) {
-		long relationId;
-		
-		super.modifyEntity(entity);
-		
-		relationId = entity.getId();
-		relationMemberDao.removeList(relationId);
-		addMembers(entity.getId(), entity.getMembers());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void removeEntity(long entityId) {
-		relationMemberDao.removeList(entityId);
-		
-		super.removeEntity(entityId);
-	}
-	
-	
-	private ReleasableIterator<DbOrderedFeature<RelationMember>> getRelationMembers(String tablePrefix) {
-		
-		FileBasedSort<DbOrderedFeature<RelationMember>> sortingStore =
-			new FileBasedSort<DbOrderedFeature<RelationMember>>(
-				new SingleClassObjectSerializationFactory(DbOrderedFeature.class),
-				new DbOrderedFeatureComparator<RelationMember>(), true);
-		
-		try {
-			String sql;
-			SortingStoreRowMapperListener<DbOrderedFeature<RelationMember>> storeListener;
-			RowMapperRowCallbackListener<DbOrderedFeature<RelationMember>> rowCallbackListener;
-			ReleasableIterator<DbOrderedFeature<RelationMember>> resultIterator;
-			
-			sql = relationMemberMapper.getSqlSelect(tablePrefix, false, false);
-			
-			// Sends all received data into the object store.
-			storeListener = new SortingStoreRowMapperListener<DbOrderedFeature<RelationMember>>(sortingStore);
-			// Converts result set rows into objects and passes them into the store.
-			rowCallbackListener = new RowMapperRowCallbackListener<DbOrderedFeature<RelationMember>>(
-					relationMemberMapper.getRowMapper(), storeListener);
-			
-			// Perform the query passing the row mapper chain to process rows in a streamy fashion.
-			jdbcTemplate.getJdbcOperations().query(sql, rowCallbackListener);
-			
-			// Open a iterator on the store that will release the store upon completion.
-			resultIterator =
-				new StoreReleasingIterator<DbOrderedFeature<RelationMember>>(sortingStore.iterate(), sortingStore);
-			
-			// The store itself shouldn't be released now that it has been attached to the iterator.
-			sortingStore = null;
-			
-			return resultIterator;
-			
-		} finally {
-			if (sortingStore != null) {
-				sortingStore.release();
-			}
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected List<FeaturePopulator<Relation>> getFeaturePopulators(String tablePrefix) {
-		ReleasableIterator<DbFeature<RelationMember>> relationMemberIterator;
-		List<FeaturePopulator<Relation>> featurePopulators;
-		
-		featurePopulators = new ArrayList<FeaturePopulator<Relation>>();
-		
-		// Get the way nodes for the selected entities.
-		relationMemberIterator = new UpcastIterator<DbFeature<RelationMember>, DbOrderedFeature<RelationMember>>(
-				getRelationMembers(tablePrefix));
-		
-		// Wrap the way node source into a feature populator that can attach them to their
-		// owning ways.
-		featurePopulators.add(
-				new FeaturePopulatorImpl<Relation, RelationMember, DbFeature<RelationMember>>(
-						relationMemberIterator, new RelationMemberCollectionLoader()));
-		
-		return featurePopulators;
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java
deleted file mode 100644
index e79bdd1..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/UserDao.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.openstreetmap.osmosis.pgsnapshot.common.NoSuchRecordException;
-import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Performs all user-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class UserDao {
-	private static final String SELECT_USER = "SELECT id, name FROM users WHERE id = ?";
-	private static final String INSERT_USER = "INSERT INTO users(id, name) VALUES(?, ?)";
-	private static final String UPDATE_USER = "UPDATE users SET name = ? WHERE id = ?";
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private ActionDao actionDao;
-	private UserRowMapper rowMapper;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public UserDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		this.actionDao = actionDao;
-		
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		
-		rowMapper = new UserRowMapper();
-	}
-	
-	
-	/**
-	 * Loads the specified way from the database.
-	 * 
-	 * @param userId
-	 *            The unique identifier of the user.
-	 * @return The loaded user.
-	 */
-	public OsmUser getUser(long userId) {
-		OsmUser user;
-		
-		try {
-			user = jdbcTemplate.queryForObject(SELECT_USER, rowMapper, userId);
-		} catch (EmptyResultDataAccessException e) {
-			throw new NoSuchRecordException("User " + userId + " doesn't exist.", e);
-		}
-		
-		return user;
-	}
-	
-	
-	/**
-	 * Adds the specified user to the database.
-	 * 
-	 * @param user
-	 *            The user to add.
-	 */
-	public void addUser(OsmUser user) {
-		jdbcTemplate.update(INSERT_USER, user.getId(), user.getName());
-		
-		actionDao.addAction(ActionDataType.USER, ChangesetAction.CREATE, user.getId());
-	}
-	
-	
-	/**
-	 * Updates the specified user record in the database.
-	 * 
-	 * @param user
-	 *            The user to update.
-	 */
-	public void updateUser(OsmUser user) {
-		jdbcTemplate.update(UPDATE_USER, user.getName(), user.getId());
-		
-		actionDao.addAction(ActionDataType.USER, ChangesetAction.MODIFY, user.getId());
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java
deleted file mode 100644
index 3dfab34..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayDao.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.database.DbOrderedFeature;
-import org.openstreetmap.osmosis.core.database.FeaturePopulator;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.pgsnapshot.common.DatabaseContext;
-import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
-
-
-/**
- * Performs all way-specific db operations.
- * 
- * @author Brett Henderson
- */
-public class WayDao extends EntityDao<Way> {
-	
-	private static final String SQL_UPDATE_WAY_BBOX =
-		"UPDATE ways SET bbox = ("
-		+ " SELECT Envelope(Collect(geom))"
-		+ " FROM nodes JOIN way_nodes ON way_nodes.node_id = nodes.id"
-		+ " WHERE way_nodes.way_id = ways.id"
-		+ " )"
-		+ " WHERE ways.id = ?";
-	private static final String SQL_UPDATE_WAY_LINESTRING =
-		"UPDATE ways w SET linestring = ("
-		+ " SELECT ST_MakeLine(c.geom) AS way_line FROM ("
-		+ " SELECT n.geom AS geom FROM nodes n INNER JOIN way_nodes wn ON n.id = wn.node_id"
-		+ " WHERE (wn.way_id = w.id) ORDER BY wn.sequence_id"
-		+ " ) c"
-		+ " )"
-		+ " WHERE w.id  = ?";
-	
-	private SimpleJdbcTemplate jdbcTemplate;
-	private DatabaseCapabilityChecker capabilityChecker;
-	private EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>> wayNodeDao;
-	private WayNodeMapper wayNodeMapper;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param dbCtx
-	 *            The database context to use for accessing the database.
-	 * @param actionDao
-	 *            The dao to use for adding action records to the database.
-	 */
-	public WayDao(DatabaseContext dbCtx, ActionDao actionDao) {
-		super(dbCtx.getSimpleJdbcTemplate(), new WayMapper(), actionDao);
-		
-		jdbcTemplate = dbCtx.getSimpleJdbcTemplate();
-		capabilityChecker = new DatabaseCapabilityChecker(dbCtx);
-		wayNodeMapper = new WayNodeMapper();
-		wayNodeDao = new EntityFeatureDao<WayNode, DbOrderedFeature<WayNode>>(jdbcTemplate, wayNodeMapper);
-	}
-	
-	
-	private void loadFeatures(long entityId, Way entity) {
-		entity.getWayNodes().addAll(wayNodeDao.getAllRaw(entityId));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Way getEntity(long entityId) {
-		Way entity;
-		
-		entity = super.getEntity(entityId);
-		
-		loadFeatures(entityId, entity);
-		
-		return entity;
-	}
-
-
-	/**
-	 * Adds the specified way node list to the database.
-	 * 
-	 * @param entityId
-	 *            The identifier of the entity to add these features to.
-	 * @param wayNodeList
-	 *            The list of features to add.
-	 */
-	private void addWayNodeList(long entityId, List<WayNode> wayNodeList) {
-		List<DbOrderedFeature<WayNode>> dbList;
-		
-		dbList = new ArrayList<DbOrderedFeature<WayNode>>(wayNodeList.size());
-		
-		for (int i = 0; i < wayNodeList.size(); i++) {
-			dbList.add(new DbOrderedFeature<WayNode>(entityId, wayNodeList.get(i), i));
-		}
-		
-		wayNodeDao.addAll(dbList);
-	}
-	
-	
-	/**
-	 * Updates the bounding box column for the specified way.
-	 * 
-	 * @param wayId
-	 *            The way bounding box.
-	 */
-	private void updateWayGeometries(long wayId) {
-		if (capabilityChecker.isWayBboxSupported()) {
-			jdbcTemplate.update(SQL_UPDATE_WAY_BBOX, wayId);
-		}
-		if (capabilityChecker.isWayLinestringSupported()) {
-			jdbcTemplate.update(SQL_UPDATE_WAY_LINESTRING, wayId);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void addEntity(Way entity) {
-		super.addEntity(entity);
-		
-		addWayNodeList(entity.getId(), entity.getWayNodes());
-		
-		updateWayGeometries(entity.getId());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void modifyEntity(Way entity) {
-		long wayId;
-		
-		super.modifyEntity(entity);
-		
-		wayId = entity.getId();
-		wayNodeDao.removeList(wayId);
-		addWayNodeList(entity.getId(), entity.getWayNodes());
-		
-		updateWayGeometries(entity.getId());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void removeEntity(long entityId) {
-		wayNodeDao.removeList(entityId);
-		
-		super.removeEntity(entityId);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected List<FeaturePopulator<Way>> getFeaturePopulators(String tablePrefix) {
-		return Collections.emptyList();
-	}
-}
diff --git a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java b/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java
deleted file mode 100644
index 12da790..0000000
--- a/pgsnapshot/src/main/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/impl/WayGeometryBuilder.java
+++ /dev/null
@@ -1,229 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.lifecycle.Releasable;
-import org.openstreetmap.osmosis.pgsnapshot.common.CompactPersistentNodeLocationStore;
-import org.openstreetmap.osmosis.pgsnapshot.common.InMemoryNodeLocationStore;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocation;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStore;
-import org.openstreetmap.osmosis.pgsnapshot.common.NodeLocationStoreType;
-import org.openstreetmap.osmosis.pgsnapshot.common.PersistentNodeLocationStore;
-import org.postgis.LineString;
-import org.postgis.LinearRing;
-import org.postgis.Point;
-import org.postgis.Polygon;
-
-
-/**
- * Caches a set of node latitudes and longitudes and uses these to calculate the
- * geometries for ways.
- * 
- * @author Brett Henderson
- */
-public class WayGeometryBuilder implements Releasable {
-	/**
-	 * Stores the locations of nodes so that they can be used to build the way
-	 * geometries.
-	 */
-	protected NodeLocationStore locationStore;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param storeType
-	 *            The type of storage to use for holding node locations.
-	 */
-	public WayGeometryBuilder(NodeLocationStoreType storeType) {
-		if (NodeLocationStoreType.InMemory.equals(storeType)) {
-			locationStore = new InMemoryNodeLocationStore();
-		} else if (NodeLocationStoreType.TempFile.equals(storeType)) {
-			locationStore = new PersistentNodeLocationStore();
-		} else if (NodeLocationStoreType.CompactTempFile.equals(storeType)) {
-			locationStore = new CompactPersistentNodeLocationStore();
-		} else {
-			throw new OsmosisRuntimeException("The store type " + storeType + " is not recognized.");
-		}
-	}
-	
-	
-	/**
-	 * Adds the location of the node to the internal store.
-	 * 
-	 * @param node
-	 *            The node to add.
-	 */
-	public void addNodeLocation(Node node) {
-		locationStore.addLocation(node.getId(), new NodeLocation(node.getLongitude(), node.getLatitude()));
-	}
-
-    /**
-     * Get NodeLocation from internal store.
-     *
-     * @param nodeId
-     *              Id of the node we want the location for.
-     * @return Location of node
-     */
-    public NodeLocation getNodeLocation(long nodeId) {
-        return locationStore.getNodeLocation(nodeId);
-    }
-	
-	private Polygon createWayBbox(double left, double right, double bottom, double top) {
-		Point[] points;
-		LinearRing ring;
-		Polygon bbox;
-		
-		points = new Point[5];
-		points[0] = new Point(left, bottom);
-		points[1] = new Point(left, top);
-		points[2] = new Point(right, top);
-		points[3] = new Point(right, bottom);
-		points[4] = new Point(left, bottom);
-		
-		ring = new LinearRing(points);
-		
-		bbox = new Polygon(new LinearRing[] {ring});
-		bbox.srid = 4326;
-		
-		return bbox;
-	}
-	
-	
-	/**
-	 * Creates a linestring from a list of points.
-	 * 
-	 * @param points
-	 *            The points making up the line.
-	 * @return The linestring.
-	 */
-	public LineString createLinestring(List<Point> points) {
-		LineString lineString;
-		
-		lineString = new LineString(points.toArray(new Point[]{}));
-		lineString.srid = 4326;
-		
-		return lineString;
-	}
-
-
-    /**
-     * @param nodeId
-     *             Id of the node.
-     * @return Point object
-     */
-    public Point createPoint(long nodeId) {
-	    NodeLocation nodeLocation = locationStore.getNodeLocation(nodeId);
-        Point point = new Point(nodeLocation.getLongitude(), nodeLocation.getLatitude());
-        point.srid = 4326;
-
-        return point;
-    }
-
-	
-	/**
-	 * Builds a bounding box geometry object from the node references in the
-	 * specified way. Unknown nodes will be ignored.
-	 * 
-	 * @param way
-	 *            The way to create the bounding box for.
-	 * @return The bounding box surrounding the way.
-	 */
-	public Polygon createWayBbox(Way way) {
-		double left;
-		double right;
-		double top;
-		double bottom;
-		boolean nodesFound;
-		
-		nodesFound = false;
-		left = 0;
-		right = 0;
-		bottom = 0;
-		top = 0;
-		for (WayNode wayNode : way.getWayNodes()) {
-			NodeLocation nodeLocation;
-			double longitude;
-			double latitude;
-			
-			nodeLocation = locationStore.getNodeLocation(wayNode.getNodeId());
-			longitude = nodeLocation.getLongitude();
-			latitude = nodeLocation.getLatitude();
-			
-			if (nodeLocation.isValid()) {
-				if (nodesFound) {
-					if (longitude < left) {
-						left = longitude;
-					}
-					if (longitude > right) {
-						right = longitude;
-					}
-					if (latitude < bottom) {
-						bottom = latitude;
-					}
-					if (latitude > top) {
-						top = latitude;
-					}
-				} else {
-					left = longitude;
-					right = longitude;
-					bottom = latitude;
-					top = latitude;
-					
-					nodesFound = true;
-				}
-			}
-		}
-		
-		return createWayBbox(left, right, bottom, top);
-	}
-	
-	
-	/**
-	 * Builds a linestring geometry object from the node references in the
-	 * specified way. Unknown nodes will be ignored.
-	 * 
-	 * @param way
-	 *            The way to create the linestring for.
-	 * @return The linestring representing the way.
-	 */
-	public LineString createWayLinestring(Way way) {
-		List<Point> linePoints;
-		int numValidNodes = 0;
-		
-		linePoints = new ArrayList<Point>();
-		for (WayNode wayNode : way.getWayNodes()) {
-			NodeLocation nodeLocation;
-			
-			nodeLocation = locationStore.getNodeLocation(wayNode.getNodeId());
-	
-			if (nodeLocation.isValid()) {
-				numValidNodes++;
-				linePoints.add(new Point(nodeLocation.getLongitude(), nodeLocation.getLatitude()));
-			} else {
-				return null;
-			}
-		}
-	
-		if (numValidNodes >= 2) {	
-			return createLinestring(linePoints);
-		} else {
-			return null;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		locationStore.release();
-	}
-}
diff --git a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java b/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java
deleted file mode 100644
index 07c76b0..0000000
--- a/pgsnapshot/src/test/java/org/openstreetmap/osmosis/pgsnapshot/v0_6/PostgreSqlTest.java
+++ /dev/null
@@ -1,277 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.pgsnapshot.v0_6;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.Osmosis;
-import org.openstreetmap.osmosis.testutil.AbstractDataTest;
-
-
-/**
- * Tests for PostgreSQL tasks.
- * 
- * @author Brett Henderson
- */
-public class PostgreSqlTest extends AbstractDataTest {
-	
-	private File getAuthFile() {
-		return dataUtils.createDataFile("db.pgsql.authfile", "v0_6/pgsql-authfile.txt");
-	}
-	
-	
-	/**
-	 * A basic test loading an osm file into a pgsql database, then dumping it
-	 * again and verifying that it is identical.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void testLoadAndDump() throws IOException {
-		File authFile;
-		File inputFile;
-		File outputFile;
-		
-		// Generate input files.
-		authFile = getAuthFile();
-		inputFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
-		outputFile = dataUtils.newFile();
-		
-		// Remove all existing data from the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--truncate-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Load the database with a dataset.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-0.6",
-				inputFile.getPath(),
-				"--write-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Dump the database to an osm file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-pgsql-0.6",
-				"authFile=" + authFile.getPath(),
-				"--dataset-dump-0.6",
-				"--tag-sort-0.6",
-				"--write-xml-0.6",
-				outputFile.getPath()
-			}
-		);
-		
-		// Validate that the output file matches the input file.
-		dataUtils.compareFiles(inputFile, outputFile);
-	}
-	
-	
-	/**
-	 * A test loading an osm file into a pgsql database, then applying a
-	 * changeset, then dumping it again and verifying the output is as expected.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void testChangeset() throws IOException {
-		File authFile;
-		File snapshotFile;
-		File changesetFile;
-		File expectedResultFile;
-		File actualResultFile;
-		
-		// Generate input files.
-		authFile = getAuthFile();
-		snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
-		changesetFile = dataUtils.createDataFile("v0_6/db-changeset.osc");
-		expectedResultFile = dataUtils.createDataFile("v0_6/db-changeset-expected.osm");
-		actualResultFile = dataUtils.newFile();
-		
-		// Remove all existing data from the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--truncate-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Load the database with the snapshot file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-0.6",
-				snapshotFile.getPath(),
-				"--write-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Apply the changeset file to the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-change-0.6",
-				changesetFile.getPath(),
-				"--write-pgsql-change-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Dump the database to an osm file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-pgsql-0.6",
-				"authFile=" + authFile.getPath(),
-				"--dataset-dump-0.6",
-				"--tag-sort-0.6",
-				"--write-xml-0.6",
-				actualResultFile.getPath()
-			}
-		);
-		
-		// Validate that the dumped file matches the expected result.
-		dataUtils.compareFiles(expectedResultFile, actualResultFile);
-	}
-
-
-	/**
-	 * A test loading an osm file into a pgsql database, then making some modifications via the
-	 * dataset api, then dumping it again and verifying the output is as expected.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void testDataset() throws IOException {
-		File authFile;
-		File snapshotFile;
-		File expectedResultFile;
-		File actualResultFile;
-		
-		// Generate input files.
-		authFile = getAuthFile();
-		snapshotFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
-		expectedResultFile = dataUtils.createDataFile("v0_6/db-dataset-expected.osm");
-		actualResultFile = dataUtils.newFile();
-		
-		// Remove all existing data from the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--truncate-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Load the database with the snapshot file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-0.6",
-				snapshotFile.getPath(),
-				"--write-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Invoke the dataset driver task task to manipulate the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"-p",
-				DatasetDriverPlugin.class.getName(),
-				"--read-pgsql-0.6",
-				"authFile=" + authFile.getPath(),
-				"--drive-dataset"
-			}
-		);
-		
-		// Dump the database to an osm file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-pgsql-0.6",
-				"authFile=" + authFile.getPath(),
-				"--dataset-dump-0.6",
-				"--tag-sort-0.6",
-				"--write-xml-0.6",
-				actualResultFile.getPath()
-			}
-		);
-		
-		// Validate that the dumped file matches the expected result.
-		dataUtils.compareFiles(expectedResultFile, actualResultFile);
-	}
-
-
-	/**
-	 * A test loading an osm file into a pgsql database, then reading it via a
-	 * dataset bounding box covering the entire planet and verifying the output
-	 * is as expected.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void testDatasetBoundingBox() throws IOException {
-		File authFile;
-		File inputFile;
-		File outputFile;
-		
-		// Generate input files.
-		authFile = getAuthFile();
-		inputFile = dataUtils.createDataFile("v0_6/db-snapshot.osm");
-		outputFile = dataUtils.newFile();
-		
-		// Remove all existing data from the database.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--truncate-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Load the database with a dataset.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-0.6",
-				inputFile.getPath(),
-				"--write-pgsql-0.6",
-				"authFile=" + authFile.getPath()
-			}
-		);
-		
-		// Dump the database to an osm file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-pgsql-0.6",
-				"authFile=" + authFile.getPath(),
-				"--dataset-bounding-box-0.6",
-				"completeWays=true",
-				"--tag-sort-0.6",
-				"--write-xml-0.6",
-				outputFile.getPath()
-			}
-		);
-		
-		// Validate that the output file matches the input file.
-		dataUtils.compareFiles(inputFile, outputFile);
-	}
-}
diff --git a/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm b/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm
deleted file mode 100644
index d0486aa..0000000
--- a/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset-expected.osm
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1-revised"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-    <member type="node" ref="7" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc b/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc
deleted file mode 100644
index e5bbb0d..0000000
--- a/pgsnapshot/src/test/resources/data/template/v0_6/db-changeset.osc
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osmChange version="0.6" generator="Osmosis %VERSION%">
-  <modify>
-    <node id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12" lat="-1" lon="-2">
-      <tag k="created_by" v="Me1-revised"/>
-    </node>
-  </modify>
-  <delete>
-    <way id="3" version="13" timestamp="2008-01-03T09:10:11Z" changeset="92"/>
-  </delete>
-  <create>
-    <node id="7" version="1" timestamp="2008-01-03T18:19:20Z" changeset="92" lat="-13" lon="-14">
-      <tag k="created_by" v="Me6"/>
-    </node>
-  </create>
-  <modify>
-    <!-- Add a new way node and change the user name. -->
-    <way id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-      <nd ref="1"/>
-      <nd ref="2"/>
-      <nd ref="3"/>
-      <nd ref="4"/>
-      <tag k="created_by" v="Me1"/>
-    </way>
-    <!-- This modify uses the same version number which will test db ability to cope with changeset replay. -->
-    <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-      <nd ref="2"/>
-      <nd ref="3"/>
-      <nd ref="4"/>
-      <tag k="created_by" v="Me1"/>
-    </way>
-    <!-- New version of the relation pointing to a different node. -->
-    <relation id="1" version="11" timestamp="2008-01-03T03:04:05Z" uid="10" user="user10b" changeset="12">
-      <member type="node" ref="7" role="noderole"/>
-      <member type="way" ref="1" role="wayrole1"/>
-      <member type="way" ref="2" role="wayrole2"/>
-      <tag k="type" v="myrelation"/>
-    </relation>
-  </modify>
-</osmChange>
diff --git a/pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm b/pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm
deleted file mode 100644
index e37f94e..0000000
--- a/pgsnapshot/src/test/resources/data/template/v0_6/db-dataset-expected.osm
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11" lat="-1" lon="-2">
-    <tag k="change" v="new tag"/>
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="7" version="16" timestamp="2008-01-02T18:19:20Z" changeset="93" lat="-11" lon="-12">
-    <tag k="change" v="new node"/>
-    <tag k="created_by" v="Me7"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10b" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm b/pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm
deleted file mode 100644
index 0792662..0000000
--- a/pgsnapshot/src/test/resources/data/template/v0_6/db-snapshot.osm
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11" lat="-1" lon="-2">
-    <tag k="created_by" v="Me1"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" changeset="31" lat="-5" lon="-6">
-    <tag k="created_by" v="Me3"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" changeset="41" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <node id="5" version="14" timestamp="2008-01-02T12:13:14Z" changeset="91" lat="-9" lon="-10">
-    <tag k="created_by" v="Me5"/>
-  </node>
-  <node id="6" version="15" timestamp="2008-01-02T15:16:17Z" changeset="91" lat="-11" lon="-12">
-    <tag k="created_by" v="Me6"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" changeset="21">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="3" version="12" timestamp="2008-01-02T09:10:11Z" changeset="91">
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <nd ref="5"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" changeset="11">
-    <member type="node" ref="6" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/replication/.checkstyle b/replication/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/replication/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/replication/.classpath b/replication/.classpath
deleted file mode 100644
index 3aec94e..0000000
--- a/replication/.classpath
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Set"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/replication/.gitignore b/replication/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/replication/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/replication/.project b/replication/.project
deleted file mode 100644
index f9f7964..0000000
--- a/replication/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Replication</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/replication/build.xml b/replication/build.xml
deleted file mode 100644
index 11de90f..0000000
--- a/replication/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Replication" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/replication/ivy.xml b/replication/ivy.xml
deleted file mode 100644
index 0047f99..0000000
--- a/replication/ivy.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-replication"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-set" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="compile->default" changing="true"/>
-        
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java
deleted file mode 100644
index 5b2ab0f..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/ReplicationPluginLoader.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.plugin.PluginLoader;
-import org.openstreetmap.osmosis.replication.v0_6.IntervalDownloaderFactory;
-import org.openstreetmap.osmosis.replication.v0_6.IntervalDownloaderInitializerFactory;
-import org.openstreetmap.osmosis.replication.v0_6.ReplicationDownloaderFactory;
-import org.openstreetmap.osmosis.replication.v0_6.ReplicationDownloaderInitializerFactory;
-import org.openstreetmap.osmosis.replication.v0_6.ReplicationFileMergerFactory;
-import org.openstreetmap.osmosis.replication.v0_6.ReplicationFileMergerInitializerFactory;
-import org.openstreetmap.osmosis.replication.v0_6.ReplicationLagReaderFactory;
-
-
-/**
- * The plugin loader for the replication tasks.
- * 
- * @author Brett Henderson
- */
-public class ReplicationPluginLoader implements PluginLoader {
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Map<String, TaskManagerFactory> loadTaskFactories() {
-		Map<String, TaskManagerFactory> factoryMap;
-		
-		factoryMap = new HashMap<String, TaskManagerFactory>();
-		
-		factoryMap.put("read-change-interval", new IntervalDownloaderFactory());
-		factoryMap.put("rci", new IntervalDownloaderFactory());
-		factoryMap.put("read-change-interval-init", new IntervalDownloaderInitializerFactory());
-		factoryMap.put("rcii", new IntervalDownloaderInitializerFactory());
-		factoryMap.put("read-replication-interval", new ReplicationDownloaderFactory());
-		factoryMap.put("rri", new ReplicationDownloaderFactory());
-		factoryMap.put("read-replication-interval-init", new ReplicationDownloaderInitializerFactory());
-		factoryMap.put("rrii", new ReplicationDownloaderInitializerFactory());
-		factoryMap.put("merge-replication-files", new ReplicationFileMergerFactory());
-		factoryMap.put("mrf", new ReplicationFileMergerFactory());
-		factoryMap.put("merge-replication-files-init", new ReplicationFileMergerInitializerFactory());
-		factoryMap.put("mrfi", new ReplicationFileMergerInitializerFactory());
-		factoryMap.put("read-replication-lag", new ReplicationLagReaderFactory());
-		factoryMap.put("rrl", new ReplicationLagReaderFactory());
-		
-		factoryMap.put("read-change-interval-0.6", new IntervalDownloaderFactory());
-		factoryMap.put("read-change-interval-init-0.6", new IntervalDownloaderInitializerFactory());
-		factoryMap.put("read-replication-interval-0.6", new ReplicationDownloaderFactory());
-		factoryMap.put("read-replication-interval-init-0.6", new ReplicationDownloaderInitializerFactory());
-		factoryMap.put("merge-replication-files-0.6", new ReplicationFileMergerFactory());
-		factoryMap.put("merge-replication-files-init-0.6", new ReplicationFileMergerInitializerFactory());
-		factoryMap.put("read-replication-lag-0.6", new ReplicationLagReaderFactory());
-		
-		return factoryMap;
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStatePersistor.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStatePersistor.java
deleted file mode 100644
index c4e5451..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/FileReplicationStatePersistor.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.common;
-
-import java.io.File;
-import java.util.Properties;
-
-import org.openstreetmap.osmosis.core.util.PropertiesPersister;
-
-
-/**
- * A file-based persister for replication state.
- */
-public class FileReplicationStatePersistor implements ReplicationStatePersister {
-	
-	private PropertiesPersister persister;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param stateFile
-	 *            The location of the file containing the persisted data.
-	 */
-	public FileReplicationStatePersistor(File stateFile) {
-		persister = new PropertiesPersister(stateFile);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public ReplicationState loadState() {
-		return new ReplicationState(persister.load());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void saveState(ReplicationState state) {
-		Properties properties;
-		
-		properties = new Properties();
-		state.store(properties);
-		
-		persister.store(properties);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean stateExists() {
-		return persister.exists();
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java
deleted file mode 100644
index a20fa6d..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationState.java
+++ /dev/null
@@ -1,179 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.common;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Properties;
-
-import org.openstreetmap.osmosis.core.store.StoreClassRegister;
-import org.openstreetmap.osmosis.core.store.StoreReader;
-import org.openstreetmap.osmosis.core.store.StoreWriter;
-import org.openstreetmap.osmosis.core.store.Storeable;
-import org.openstreetmap.osmosis.core.time.DateFormatter;
-import org.openstreetmap.osmosis.core.time.DateParser;
-
-
-/**
- * Contains the state to be remembered between replication invocations. This state ensures that no
- * data is missed during replication, and none is repeated.
- */
-public class ReplicationState implements Storeable {
-	private Date timestamp;
-	private long sequenceNumber;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param txnMax
-	 *            The maximum transaction id in the database.
-	 * @param txnMaxQueried
-	 *            The maximum transaction id currently replicated from the database.
-	 * @param txnActive
-	 *            The currently active transaction ids.
-	 * @param txnReady
-	 *            The previously active transaction ids that can now be queried.
-	 * @param timestamp
-	 *            The maximum timestamp of data currently read from the database.
-	 * @param sequenceNumber
-	 *            The replication sequence number.
-	 */
-	public ReplicationState(long txnMax, long txnMaxQueried, List<Long> txnActive, List<Long> txnReady,
-			Date timestamp, long sequenceNumber) {
-		this.timestamp = timestamp;
-		this.sequenceNumber = sequenceNumber;
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param reader
-	 *            The store to read state from.
-	 * @param scr
-	 *            Maintains the mapping between classes and their identifiers
-	 *            within the store.
-	 */
-	public ReplicationState(StoreReader reader, StoreClassRegister scr) {
-		timestamp = new Date(reader.readLong());
-		sequenceNumber = reader.readLong();
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param properties
-	 *            The properties to load state from.
-	 */
-	public ReplicationState(Properties properties) {
-		timestamp = new DateParser().parse(properties.getProperty("timestamp"));
-		sequenceNumber = Long.parseLong(properties.getProperty("sequenceNumber"));
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void store(StoreWriter writer, StoreClassRegister storeClassRegister) {
-		writer.writeLong(timestamp.getTime());
-		writer.writeLong(sequenceNumber);
-	}
-
-
-	/**
-	 * Writes all state into the provided properties object.
-	 * 
-	 * @param properties
-	 *            The properties to be updated.
-	 */
-	public void store(Properties properties) {
-		properties.setProperty("timestamp", new DateFormatter().format(timestamp));
-		properties.setProperty("sequenceNumber", Long.toString(sequenceNumber));
-	}
-
-
-	/**
-	 * Gets the maximum timestamp of data currently read from the database.
-	 * 
-	 * @return The timestamp.
-	 */
-	public Date getTimestamp() {
-		return timestamp;
-	}
-
-
-	/**
-	 * Sets the maximum timestamp of data currently read from the database.
-	 * 
-	 * @param timestamp
-	 *            The timestamp.
-	 */
-	public void setTimestamp(Date timestamp) {
-		this.timestamp = timestamp;
-	}
-	
-	
-	/**
-	 * Gets the replication sequence number.
-	 * 
-	 * @return The sequence number.
-	 */
-	public long getSequenceNumber() {
-		return sequenceNumber;
-	}
-
-
-	/**
-	 * Sets the replication sequence number.
-	 * 
-	 * @param sequenceNumber
-	 *            The sequence number.
-	 */
-	public void setSequenceNumber(long sequenceNumber) {
-		this.sequenceNumber = sequenceNumber;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		boolean result;
-		
-		if (obj instanceof ReplicationState) {
-			ReplicationState compareState = (ReplicationState) obj;
-			
-			if (timestamp.equals(compareState.timestamp)
-					&& sequenceNumber == compareState.sequenceNumber) {
-				result = true;
-			} else {
-				result = false;
-			}
-		} else {
-			result = false;
-		}
-		
-		return result;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		return (int) sequenceNumber + (int) timestamp.getTime();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String toString() {
-		return "ReplicationState(timestamp=" + timestamp + ", sequenceNumber=" + sequenceNumber + ")";
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStatePersister.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStatePersister.java
deleted file mode 100644
index 9707668..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ReplicationStatePersister.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.common;
-
-/**
- * Implementations of this interface provide persistence for replication state objects. This state
- * should be persisted after completion of the data replication, and preferably within the same
- * transaction to avoid duplicates in the face of failures.
- */
-public interface ReplicationStatePersister {
-	/**
-	 * Persists the state.
-	 * 
-	 * @param state
-	 *            The state to be persisted.
-	 */
-	void saveState(ReplicationState state);
-	
-	
-	/**
-	 * Loads the existing state.
-	 * 
-	 * @return The state to be loaded.
-	 */
-	ReplicationState loadState();
-	
-	
-	/**
-	 * Checks if state currently exists. If no state exists it will need to be initialized.
-	 * 
-	 * @return True if state exists, false otherwise.
-	 */
-	boolean stateExists();
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java
deleted file mode 100644
index 9607f05..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/common/ServerStateReader.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.common;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * Retrieves replication state files from the server hosting replication data.
- */
-public class ServerStateReader {
-	private static final Logger LOG = Logger.getLogger(ServerStateReader.class.getName());
-	private static final String SERVER_STATE_FILE = "state.txt";
-	private static final String SEQUENCE_STATE_FILE_SUFFIX = ".state.txt";
-	
-	
-	private ReplicationSequenceFormatter sequenceFormatter;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public ServerStateReader() {
-		sequenceFormatter = new ReplicationSequenceFormatter(9, 3);
-	}
-	
-	
-	/**
-	 * Retrieves the latest state from the server.
-	 * 
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @return The state.
-	 */
-	public ReplicationState getServerState(URL baseUrl) {
-		return getServerState(baseUrl, SERVER_STATE_FILE);
-	}
-	
-	
-	/**
-	 * Retrieves the specified state from the server.
-	 * 
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @param sequenceNumber
-	 *            The sequence number of the state to be retrieved from the server.
-	 * @return The state.
-	 */
-	public ReplicationState getServerState(URL baseUrl, long sequenceNumber) {
-		return getServerState(baseUrl, sequenceFormatter.getFormattedName(sequenceNumber, SEQUENCE_STATE_FILE_SUFFIX));
-	}
-
-
-	/**
-	 * Retrieves the specified state from the server.
-	 * 
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @param stateFile
-	 *            The state file to be retrieved.
-	 * @return The state.
-	 */
-	private ReplicationState getServerState(URL baseUrl, String stateFile) {
-		URL stateUrl;
-		InputStream stateStream = null;
-		
-		try {
-			stateUrl = new URL(baseUrl, stateFile);
-		} catch (MalformedURLException e) {
-			throw new OsmosisRuntimeException("The server timestamp URL could not be created.", e);
-		}
-		
-		try {
-			BufferedReader reader;
-			Properties stateProperties;
-			ReplicationState state;
-			
-			URLConnection connection = stateUrl.openConnection();
-			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
-			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
-			stateStream = connection.getInputStream();
-			
-			reader = new BufferedReader(new InputStreamReader(stateStream));
-			stateProperties = new Properties();
-			stateProperties.load(reader);
-			
-			state = new ReplicationState(stateProperties);
-			
-			stateStream.close();
-			stateStream = null;
-			
-			return state;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read the state from the server.", e);
-		} finally {
-			try {
-				if (stateStream != null) {
-					stateStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to close state stream.", e);
-			}
-		}
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java
deleted file mode 100644
index ae6d407..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/BaseReplicationDownloader.java
+++ /dev/null
@@ -1,357 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.v0_6;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Date;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.common.RunnableTask;
-import org.openstreetmap.osmosis.core.util.FileBasedLock;
-import org.openstreetmap.osmosis.core.util.PropertiesPersister;
-import org.openstreetmap.osmosis.replication.common.ReplicationSequenceFormatter;
-import org.openstreetmap.osmosis.replication.common.ReplicationState;
-import org.openstreetmap.osmosis.replication.common.ServerStateReader;
-import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
-
-
-/**
- * This class downloads a set of replication files from a HTTP server and tracks the progress of
- * which files have already been processed. The actual processing of changeset files is performed by
- * sub-classes. This class forms the basis of a replication mechanism.
- * 
- * @author Brett Henderson
- */
-public abstract class BaseReplicationDownloader implements RunnableTask {
-	
-	private static final Logger LOG = Logger.getLogger(BaseReplicationDownloader.class.getName());
-	private static final String LOCK_FILE = "download.lock";
-	private static final String CONFIG_FILE = "configuration.txt";
-	private static final String LOCAL_STATE_FILE = "state.txt";
-	
-	
-	private File workingDirectory;
-	private ReplicationSequenceFormatter sequenceFormatter;
-	private ServerStateReader serverStateReader;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param workingDirectory
-	 *            The directory containing configuration and tracking files.
-	 */
-	public BaseReplicationDownloader(File workingDirectory) {
-		this.workingDirectory = workingDirectory;
-		
-		sequenceFormatter = new ReplicationSequenceFormatter(9, 3);
-		serverStateReader = new ServerStateReader();
-	}
-	
-	
-	/**
-	 * Provides sub-classes with access to the working directory.
-	 * 
-	 * @return The working directory for the task.
-	 */
-	protected File getWorkingDirectory() {
-		return workingDirectory;
-	}
-	
-	
-	/**
-	 * Downloads the file from the server with the specified name and writes it
-	 * to a local temporary file.
-	 * 
-	 * @param fileName
-	 *            The name of the file to download.
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @return The temporary file containing the downloaded data.
-	 */
-	private File downloadReplicationFile(String fileName, URL baseUrl) {
-		URL changesetUrl;
-		InputStream inputStream = null;
-		OutputStream outputStream = null;
-		
-		try {
-			changesetUrl = new URL(baseUrl, fileName);
-		} catch (MalformedURLException e) {
-			throw new OsmosisRuntimeException("The server file URL could not be created.", e);
-		}
-		
-		try {
-			BufferedInputStream source;
-			BufferedOutputStream sink;
-			File outputFile;
-			byte[] buffer;
-			
-			// Open an input stream for the changeset file on the server.
-			URLConnection connection = changesetUrl.openConnection();
-			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
-			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
-			inputStream = connection.getInputStream();
-			source = new BufferedInputStream(inputStream, 65536);
-			
-			// Create a temporary file to write the data to.
-			outputFile = File.createTempFile("change", null);
-			
-			// Open a output stream for the destination file.
-			outputStream = new FileOutputStream(outputFile);
-			sink = new BufferedOutputStream(outputStream, 65536);
-			
-			// Download the file.
-			buffer = new byte[65536];
-			for (int bytesRead = source.read(buffer); bytesRead > 0; bytesRead = source.read(buffer)) {
-				sink.write(buffer, 0, bytesRead);
-			}
-			sink.flush();
-			
-			// Clean up all file handles.
-			inputStream.close();
-			inputStream = null;
-			outputStream.close();
-			outputStream = null;
-			
-			return outputFile;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read the changeset file " + fileName + " from the server.", e);
-		} finally {
-			try {
-				if (inputStream != null) {
-					inputStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to changeset download stream.", e);
-			}
-			try {
-				if (outputStream != null) {
-					outputStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to changeset output stream.", e);
-			}
-		}
-	}
-	
-	
-	private void processReplicationFile(File replicationFile, ReplicationState replicationState) {
-		try {
-			XmlChangeReader xmlReader;
-			
-			// Send the contents of the replication file to the sink but suppress the complete
-			// and release methods.
-			xmlReader = new XmlChangeReader(replicationFile, true, CompressionMethod.GZip);
-			
-			// Delegate to the sub-class to process the xml.
-			processChangeset(xmlReader, replicationState);
-			
-		} finally {
-			if (!replicationFile.delete()) {
-				LOG.warning("Unable to delete file " + replicationFile.getName());
-			}
-		}
-	}
-
-
-	/**
-	 * Determines the maximum timestamp of data to be downloaded during this invocation. This may be
-	 * overriden by sub-classes, but the sub-classes must call this implemention first and then
-	 * limit the maximum timestamp further if needed. A sub-class may never increase the maximum
-	 * timestamp beyond that calculated by this method.
-	 * 
-	 * @param configuration
-	 *            The configuration.
-	 * @param serverTimestamp
-	 *            The timestamp of the latest data on the server.
-	 * @param localTimestamp
-	 *            The timestamp of the most recently downloaded data.
-	 * @return The maximum timestamp for this invocation.
-	 */
-	protected Date calculateMaximumTimestamp(ReplicationDownloaderConfiguration configuration, Date serverTimestamp,
-			Date localTimestamp) {
-		Date maximumTimestamp;
-		
-		maximumTimestamp = serverTimestamp;
-		
-		// Limit the duration according to the maximum defined in the configuration.
-		if (configuration.getMaxInterval() > 0) {
-			if ((serverTimestamp.getTime() - localTimestamp.getTime())
-				> configuration.getMaxInterval()) {
-				maximumTimestamp = new Date(localTimestamp.getTime() + configuration.getMaxInterval());
-			}
-		}
-		
-		return maximumTimestamp;
-	}
-	
-	
-	private ReplicationState download(ReplicationDownloaderConfiguration configuration, ReplicationState serverState,
-			ReplicationState initialLocalState) {
-		URL baseUrl;
-		ReplicationState localState;
-		Date maximumDownloadTimestamp;
-		
-		localState = initialLocalState;
-		
-		// Determine the location of download files.
-		baseUrl = configuration.getBaseUrl();
-		
-		// Determine the maximum timestamp that can be downloaded.
-		maximumDownloadTimestamp =
-			calculateMaximumTimestamp(configuration, serverState.getTimestamp(), localState.getTimestamp());
-		LOG.fine("The maximum timestamp to be downloaded is " + maximumDownloadTimestamp + ".");
-		
-		// Download all files and send their contents to the sink.
-		while (localState.getSequenceNumber() < serverState.getSequenceNumber()) {
-			File replicationFile;
-			long sequenceNumber;
-			ReplicationState fileReplicationState;
-			
-			// Ensure that our current state is within the allowable timestamp range.
-			if (localState.getTimestamp().compareTo(maximumDownloadTimestamp) >= 0) {
-				break;
-			}
-			
-			// Calculate the next sequence number.
-			sequenceNumber = localState.getSequenceNumber() + 1;
-			LOG.finer("Processing replication sequence " + sequenceNumber + ".");
-			
-			// Get the state associated with the next file.
-			fileReplicationState = serverStateReader.getServerState(baseUrl, sequenceNumber);
-			
-			// Download the next replication file to a temporary file.
-			replicationFile =
-				downloadReplicationFile(sequenceFormatter.getFormattedName(sequenceNumber, ".osc.gz"), baseUrl);
-			
-			// Process the file and send its contents to the sink.
-			processReplicationFile(replicationFile, fileReplicationState);
-			
-			// Update the local state to reflect the file state just processed.
-			localState = fileReplicationState;
-		}
-		
-		return localState;
-	}
-	
-	
-	private void runImpl() {
-		try {
-			ReplicationDownloaderConfiguration configuration;
-			ReplicationState serverState;
-			ReplicationState localState;
-			PropertiesPersister localStatePersistor;
-			Properties localStateProperties;
-			
-			// Instantiate utility objects.
-			configuration = new ReplicationDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
-			
-			// Obtain the server state.
-			LOG.fine("Reading current server state.");
-			serverState = serverStateReader.getServerState(configuration.getBaseUrl());
-			
-			// Build the local state persister which is used for both loading and storing local state.
-			localStatePersistor = new PropertiesPersister(new File(workingDirectory, LOCAL_STATE_FILE));
-			
-			// If local state isn't available we need to copy server state to be the initial local state
-			// then exit.
-			if (localStatePersistor.exists()) {
-				localStateProperties = localStatePersistor.load();
-				localState = new ReplicationState(localStateProperties);
-				
-				// Download and process the replication files.
-				localState = download(configuration, serverState, localState);
-				
-			} else {
-				localState = serverState;
-				
-				processInitialize(localState);
-			}
-			
-			// Commit downstream changes.
-			processComplete();
-			
-			// Persist the local state.
-			localStateProperties = new Properties();
-			localState.store(localStateProperties);
-			localStatePersistor.store(localStateProperties);
-			
-		} finally {
-			processRelease();
-		}
-	}
-	
-	
-	/**
-	 * Invoked once during the first execution run to allow initialisation based on the initial
-	 * replication state downloaded from the server.
-	 * 
-	 * @param initialState
-	 *            The first server state.
-	 */
-	protected abstract void processInitialize(ReplicationState initialState);
-	
-	
-	/**
-	 * Processes the changeset.
-	 * 
-	 * @param xmlReader
-	 *            The changeset reader initialised to point to the changeset file.
-	 * @param replicationState
-	 *            The replication state associated with the changeset file.
-	 */
-	protected abstract void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState);
-
-
-	/**
-	 * This is implemented by sub-classes and is called when all changesets have been processed.
-	 * This should perform any completion tasks such as committing changes to a database.
-	 */
-	protected abstract void processComplete();
-
-
-	/**
-	 * This is implemented by sub-classes and is called and the completion of all processing
-	 * regardless of whether it was successful or not. This should perform any cleanup tasks such as
-	 * closing files or releasing database connections.
-	 */
-	protected abstract void processRelease();
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		FileBasedLock fileLock;
-		
-		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
-		
-		try {
-			fileLock.lock();
-			
-			runImpl();
-			
-			fileLock.unlock();
-			
-		} finally {
-			fileLock.release();
-		}
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java
deleted file mode 100644
index 0372511..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/IntervalDownloader.java
+++ /dev/null
@@ -1,410 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.v0_6;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskRunner;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
-import org.openstreetmap.osmosis.core.time.DateParser;
-import org.openstreetmap.osmosis.core.util.FileBasedLock;
-import org.openstreetmap.osmosis.replication.common.TimestampTracker;
-import org.openstreetmap.osmosis.replication.v0_6.impl.ChangesetFileNameFormatter;
-import org.openstreetmap.osmosis.replication.v0_6.impl.IntervalDownloaderConfiguration;
-import org.openstreetmap.osmosis.set.v0_6.ChangeMerger;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
-
-
-/**
- * Downloads a set of change files from a HTTP server, and merges them into a
- * single output stream. It tracks the intervals covered by the current files
- * and stores the current timestamp between invocations forming the basis of a
- * replication mechanism.
- * 
- * @author Brett Henderson
- */
-public class IntervalDownloader implements RunnableChangeSource {
-	
-	private static final Logger LOG = Logger.getLogger(IntervalDownloader.class.getName());
-	
-	
-	private static final String LOCK_FILE = "download.lock";
-	private static final String CONFIG_FILE = "configuration.txt";
-	private static final String TSTAMP_FILE = "timestamp.txt";
-	private static final String TSTAMP_NEW_FILE = "timestamp-new.txt";
-	private static final String SERVER_TSTAMP_FILE = "timestamp.txt";
-	
-	
-	private ChangeSink changeSink;
-	private String taskId;
-	private File workingDirectory;
-	private DateParser dateParser;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param taskId
-	 *            The identifier for the task, this is required because the
-	 *            names of threads created by this task will use this name as a
-	 *            prefix.
-	 * @param workingDirectory
-	 *            The directory containing configuration and tracking files.
-	 */
-	public IntervalDownloader(String taskId, File workingDirectory) {
-		this.taskId = taskId;
-		this.workingDirectory = workingDirectory;
-		
-		dateParser = new DateParser();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * Retrieves the latest timestamp from the server.
-	 * 
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @return The timestamp.
-	 */
-	private Date getServerTimestamp(URL baseUrl) {
-		URL timestampUrl;
-		InputStream timestampStream = null;
-		
-		try {
-			timestampUrl = new URL(baseUrl, SERVER_TSTAMP_FILE);
-		} catch (MalformedURLException e) {
-			throw new OsmosisRuntimeException("The server timestamp URL could not be created.", e);
-		}
-		
-		try {
-			BufferedReader reader;
-			Date result;
-			
-			URLConnection connection = timestampUrl.openConnection();
-			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
-			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
-			timestampStream = connection.getInputStream();
-			
-			reader = new BufferedReader(new InputStreamReader(timestampStream));
-			
-			result = dateParser.parse(reader.readLine());
-			
-			timestampStream.close();
-			timestampStream = null;
-			
-			return result;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read the timestamp from the server.", e);
-		} finally {
-			try {
-				if (timestampStream != null) {
-					timestampStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to close timestamp stream.", e);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Downloads the file from the server with the specified name and writes it
-	 * to a local temporary file.
-	 * 
-	 * @param fileName
-	 *            The name of the file to download.
-	 * @param baseUrl
-	 *            The url of the directory containing change files.
-	 * @return The temporary file containing the downloaded data.
-	 */
-	private File downloadChangesetFile(String fileName, URL baseUrl) {
-		URL changesetUrl;
-		InputStream inputStream = null;
-		OutputStream outputStream = null;
-		
-		try {
-			changesetUrl = new URL(baseUrl, fileName);
-		} catch (MalformedURLException e) {
-			throw new OsmosisRuntimeException("The server file URL could not be created.", e);
-		}
-		
-		try {
-			BufferedInputStream source;
-			BufferedOutputStream sink;
-			File outputFile;
-			byte[] buffer;
-			
-			// Open an input stream for the changeset file on the server.
-			URLConnection connection = changesetUrl.openConnection();
-			connection.setReadTimeout(15 * 60 * 1000); // timeout 15 minutes
-			connection.setConnectTimeout(15 * 60 * 1000); // timeout 15 minutes
-			inputStream = connection.getInputStream();
-			source = new BufferedInputStream(inputStream, 65536);
-			
-			// Create a temporary file to write the data to.
-			outputFile = File.createTempFile("change", null);
-			
-			// Open a output stream for the destination file.
-			outputStream = new FileOutputStream(outputFile);
-			sink = new BufferedOutputStream(outputStream, 65536);
-			
-			// Download the file.
-			buffer = new byte[65536];
-			for (int bytesRead = source.read(buffer); bytesRead > 0; bytesRead = source.read(buffer)) {
-				sink.write(buffer, 0, bytesRead);
-			}
-			sink.flush();
-			
-			// Clean up all file handles.
-			inputStream.close();
-			inputStream = null;
-			outputStream.close();
-			outputStream = null;
-			
-			return outputFile;
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read the changeset file " + fileName + " from the server.", e);
-		} finally {
-			try {
-				if (inputStream != null) {
-					inputStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to changeset download stream.", e);
-			}
-			try {
-				if (outputStream != null) {
-					outputStream.close();
-				}
-			} catch (IOException e) {
-				// We are already in an error condition so log and continue.
-				LOG.log(Level.WARNING, "Unable to changeset output stream.", e);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Downloads the changeset files from the server and writes their contents
-	 * to the output task.
-	 */
-	private void download() {
-		IntervalDownloaderConfiguration configuration;
-		TimestampTracker timestampTracker;
-		ChangesetFileNameFormatter fileNameFormatter;
-		Date currentTime;
-		Date maximumTime;
-		URL baseUrl;
-		int maxDownloadCount;
-		int downloadCount;
-		ArrayList<File> tmpFileList;
-		ArrayList<RunnableChangeSource> tasks;
-		ArrayList<TaskRunner> taskRunners;
-		boolean tasksSuccessful;
-		
-		// Instantiate utility objects.
-		configuration = new IntervalDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
-		timestampTracker = new TimestampTracker(
-			new File(workingDirectory, TSTAMP_FILE),
-			new File(workingDirectory, TSTAMP_NEW_FILE)
-		);
-		fileNameFormatter = new ChangesetFileNameFormatter(
-			configuration.getChangeFileBeginFormat(),
-			configuration.getChangeFileEndFormat()
-		);
-		
-		// Create the base url.
-		try {
-			baseUrl = new URL(configuration.getBaseUrl());
-		} catch (MalformedURLException e) {
-			throw new OsmosisRuntimeException(
-					"Unable to convert URL string (" + configuration.getBaseUrl() + ") into a URL.", e);
-		}
-		
-		tmpFileList = new ArrayList<File>();
-		
-		// Load the current time from the timestamp tracking file.
-		currentTime = timestampTracker.getTime();
-		
-		// Load the latest timestamp from the server.
-		maximumTime = getServerTimestamp(baseUrl);
-		
-		// Process until all files have been retrieved from the server.
-		maxDownloadCount = configuration.getMaxDownloadCount();
-		downloadCount = 0;
-		while ((maxDownloadCount == 0 || downloadCount < maxDownloadCount) && currentTime.before(maximumTime)) {
-			Date nextTime;
-			String downloadFileName;
-			
-			// Calculate the end of the next time interval.
-			nextTime = new Date(currentTime.getTime() + configuration.getIntervalLength());
-			
-			// Generate the filename to be retrieved from the server.
-			downloadFileName = fileNameFormatter.generateFileName(currentTime, nextTime);
-			
-			// Download the changeset from the server.
-			tmpFileList.add(downloadChangesetFile(downloadFileName, baseUrl));
-			
-			// Move the current time to the next interval.
-			currentTime = nextTime;
-			
-			// Increment the current download count.
-			downloadCount++;
-		}
-		
-		// Generate a set of tasks for loading the change files and merge them
-		// into a single change stream.
-		tasks = new ArrayList<RunnableChangeSource>();
-		for (File tmpFile : tmpFileList) {
-			XmlChangeReader changeReader;
-			
-			// Generate a change reader task for the current task.
-			changeReader = new XmlChangeReader(
-				tmpFile,
-				true,
-				CompressionMethod.GZip
-			);
-			
-			// If tasks already exist, a change merge task must be used to merge
-			// existing output with this task output, otherwise this task can be
-			// added to the list directly.
-			if (tasks.size() > 0) {
-				ChangeMerger changeMerger;
-				
-				// Create a new change merger merging the last task output with the current task.
-				changeMerger = new ChangeMerger(ConflictResolutionMethod.LatestSource, 10);
-				
-				// Connect the inputs of this merger to the most recent change
-				// output and the new change output.
-				tasks.get(tasks.size() - 1).setChangeSink(changeMerger.getChangeSink(0));
-				changeReader.setChangeSink(changeMerger.getChangeSink(1));
-				
-				tasks.add(changeReader);
-				tasks.add(changeMerger);
-				
-			} else {
-				tasks.add(changeReader);
-			}
-		}
-		
-		// We only need to execute sub-threads if tasks exist, otherwise we must
-		// notify the sink that we have completed.
-		if (tasks.size() > 0) {
-			// Connect the last task to the change sink.
-			tasks.get(tasks.size() - 1).setChangeSink(changeSink);
-			
-			// Create task runners for each of the tasks to provide thread
-			// management.
-			taskRunners = new ArrayList<TaskRunner>(tasks.size());
-			for (int i = 0; i < tasks.size(); i++) {
-				taskRunners.add(new TaskRunner(tasks.get(i), "Thread-" + taskId + "-worker" + i));
-			}
-			
-			// Launch all of the tasks.
-			for (int i = 0; i < taskRunners.size(); i++) {
-				TaskRunner taskRunner;
-				
-				taskRunner = taskRunners.get(i);
-				
-				LOG.fine("Launching changeset worker + " + i + " in a new thread.");
-				
-				taskRunner.start();
-			}
-			
-			// Wait for all the tasks to complete.
-			tasksSuccessful = true;
-			for (int i = 0; i < taskRunners.size(); i++) {
-				TaskRunner taskRunner;
-				
-				taskRunner = taskRunners.get(i);
-				
-				LOG.fine("Waiting for changeset worker " + i + " to complete.");
-				
-				try {
-					taskRunner.join();
-				} catch (InterruptedException e) {
-					// We are already in an error condition so log and continue.
-					LOG.log(Level.WARNING, "The wait for task completion was interrupted.", e);
-				}
-				
-				if (!taskRunner.isSuccessful()) {
-					LOG.log(Level.SEVERE, "Changeset worker " + i + " failed", taskRunner.getException());
-					
-					tasksSuccessful = false;
-				}
-			}
-			
-		} else {
-			changeSink.complete();
-			tasksSuccessful = true;
-		}
-		
-		// Remove the temporary files.
-		for (File tmpFile : tmpFileList) {
-			if (!tmpFile.delete()) {
-				LOG.warning("Unable to delete file " + tmpFile.getName());
-			}
-		}
-		
-		if (!tasksSuccessful) {
-			throw new OsmosisRuntimeException("One or more changeset workers failed.");
-		}
-		
-		// Update the timestamp tracker.
-		timestampTracker.setTime(currentTime);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		FileBasedLock fileLock;
-		
-		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE));
-		
-		try {
-			fileLock.lock();
-			
-			download();
-			
-			fileLock.unlock();
-			
-		} finally {
-			changeSink.release();
-			fileLock.release();
-		}
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java
deleted file mode 100644
index cda7081..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationDownloader.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorter;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
-import org.openstreetmap.osmosis.replication.common.ReplicationState;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
-
-
-/**
- * Downloads a set of replication files from a HTTP server, and merges them into a
- * single output stream. It tracks the intervals covered by the current files
- * and stores the current timestamp between invocations forming the basis of a
- * replication mechanism.
- * 
- * @author Brett Henderson
- */
-public class ReplicationDownloader extends BaseReplicationDownloader implements RunnableChangeSource {
-	
-	private ChangeSorter changeSorter;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param workingDirectory
-	 *            The directory containing configuration and tracking files.
-	 */
-	public ReplicationDownloader(File workingDirectory) {
-		super(workingDirectory);
-		
-		// We will sort all contents prior to sending to the sink. This adds overhead that may not
-		// always be required, but provides consistent behaviour.
-		changeSorter = new ChangeSorter(new ChangeForStreamableApplierComparator());
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		changeSorter.setChangeSink(changeSink);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processInitialize(ReplicationState initialState) {
-		// Nothing to do.
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState) {
-		final ChangeSink localChangeSink = changeSorter;
-		
-		xmlReader.setChangeSink(new ChangeSink() {
-			private ChangeSink suppressedChangeSink = localChangeSink;
-			
-			@Override
-			public void process(ChangeContainer change) {
-				suppressedChangeSink.process(change);
-			}
-			@Override
-			public void complete() {
-				// Suppress the call.
-			}
-			@Override
-			public void release() {
-				// Suppress the call.
-			} });
-		
-		xmlReader.run();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processComplete() {
-		changeSorter.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processRelease() {
-		changeSorter.release();
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java
deleted file mode 100644
index 8411be4..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationFileMerger.java
+++ /dev/null
@@ -1,301 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.v0_6;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Date;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeForStreamableApplierComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.ChangeSorter;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.replication.common.FileReplicationStatePersistor;
-import org.openstreetmap.osmosis.replication.common.ReplicationFileSequenceFormatter;
-import org.openstreetmap.osmosis.replication.common.ReplicationState;
-import org.openstreetmap.osmosis.replication.common.ReplicationStatePersister;
-import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
-import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationFileMergerConfiguration;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeReader;
-import org.openstreetmap.osmosis.xml.v0_6.XmlChangeWriter;
-
-
-/**
- * Consumes the files in a replication directory and combines them into larger replication files
- * grouped by a time interval. This allows replication files created at regular intervals to be
- * combined into larger files for more efficient consumption where latency is less of an issue.
- */
-public class ReplicationFileMerger extends BaseReplicationDownloader {
-	private static final String DATA_DIRECTORY = "data";
-	private static final String CONFIG_FILE = "configuration.txt";
-	private static final String DATA_STATE_FILE = "state.txt";
-	
-	
-	private boolean sinkActive;
-	private ChangeSink changeSink;
-	private ReplicationState currentDataState;
-	private ReplicationStatePersister dataStatePersister;
-	private ReplicationFileSequenceFormatter replicationFileSequenceFormatter;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param workingDirectory
-	 *            The directory containing configuration and tracking files.
-	 */
-	public ReplicationFileMerger(File workingDirectory) {
-		super(workingDirectory);
-		
-		File dataDirectory;
-		
-		dataDirectory = new File(getWorkingDirectory(), DATA_DIRECTORY);
-		
-		dataStatePersister = new FileReplicationStatePersistor(new File(dataDirectory, DATA_STATE_FILE));
-		
-		replicationFileSequenceFormatter = new ReplicationFileSequenceFormatter(dataDirectory);
-		
-		sinkActive = false;
-	}
-	
-	
-	private Date alignDateToIntervalBoundary(Date requestedDate, long intervalLength) {
-		long remainder;
-		
-		remainder = requestedDate.getTime() % intervalLength;
-		
-		if (remainder > 0) {
-			return new Date(requestedDate.getTime() - remainder);
-		} else {
-			return requestedDate;
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected Date calculateMaximumTimestamp(ReplicationDownloaderConfiguration configuration, Date serverTimestamp,
-			Date localTimestamp) {
-		Date maximumTimestamp;
-		long intervalLength;
-		
-		// Read the current persisted state.
-		currentDataState = dataStatePersister.loadState();
-		
-		// Get the default maximum timestamp according to base calculations.
-		maximumTimestamp = super.calculateMaximumTimestamp(configuration, serverTimestamp, localTimestamp);
-		
-		// Align the maximum timestamp to an interval boundary.
-		intervalLength = getConfiguration().getIntervalLength();
-		if (intervalLength > 0) {
-			maximumTimestamp = alignDateToIntervalBoundary(maximumTimestamp, intervalLength);
-			
-			// For the first sequence file, we make sure we make sure that the maximum timestamp is
-			// ahead of the data timestamp. If it isn't, we move the maximum timestamp backwards by
-			// one interval to address the case where the local timestamp is behind the data
-			// timestamp causing some data to be downloaded and processed.
-			if (currentDataState.getSequenceNumber() == 0) {
-				if (maximumTimestamp.compareTo(currentDataState.getTimestamp()) <= 0) {
-					maximumTimestamp = new Date(maximumTimestamp.getTime() - intervalLength);
-				}
-			}
-		}
-		
-		return maximumTimestamp;
-	}
-	
-	
-	private ChangeSink buildResultWriter(long sequenceNumber) {
-		File resultFile;
-		XmlChangeWriter xmlChangeWriter;
-		ChangeSorter changeSorter;
-		
-		resultFile = replicationFileSequenceFormatter.getFormattedName(currentDataState.getSequenceNumber(), ".osc.gz");
-		
-		xmlChangeWriter = new XmlChangeWriter(
-				resultFile,
-				CompressionMethod.GZip);
-		
-		changeSorter = new ChangeSorter(new ChangeForStreamableApplierComparator());
-		changeSorter.setChangeSink(xmlChangeWriter);
-		
-		return changeSorter;
-	}
-	
-	
-	private void persistSequencedCurrentState() {
-		FileReplicationStatePersistor statePersistor;
-		File stateFile;
-		
-		stateFile = replicationFileSequenceFormatter.getFormattedName(currentDataState.getSequenceNumber(),
-				".state.txt");
-		
-		statePersistor = new FileReplicationStatePersistor(stateFile);
-		
-		statePersistor.saveState(currentDataState);
-	}
-	
-	
-	private void writeChangeset(XmlChangeReader xmlReader) {
-		final ChangeSink localChangeSink = changeSink;
-		
-		xmlReader.setChangeSink(new ChangeSink() {
-			private ChangeSink suppressedWriter = localChangeSink;
-			
-			@Override
-			public void process(ChangeContainer change) {
-				suppressedWriter.process(change);
-			}
-			@Override
-			public void complete() {
-				// Suppress the call.
-			}
-			@Override
-			public void release() {
-				// Suppress the call.
-			} });
-		
-		xmlReader.run();
-	}
-	
-	
-	private ReplicationFileMergerConfiguration getConfiguration() {
-		return new ReplicationFileMergerConfiguration(new File(getWorkingDirectory(), CONFIG_FILE));
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processInitialize(ReplicationState initialState) {
-		Date initialDate;
-		Date alignedDate;
-		long intervalLength;
-		
-		intervalLength = getConfiguration().getIntervalLength();
-		
-		initialDate = initialState.getTimestamp();
-		
-		// Align the date to an interval boundary.
-		alignedDate = alignDateToIntervalBoundary(initialDate, intervalLength);
-		
-		// If the date has been moved, then advance it to the next interval. We do this because
-		// during replication we never claim to have covered a time period that we haven't received
-		// data for. We may include extra data from a previous interval. By advancing the stated
-		// initial timestamp to the next interval our first replication will include some data from
-		// the previous interval.
-		if (alignedDate.compareTo(initialDate) < 0) {
-			alignedDate = new Date(alignedDate.getTime() + intervalLength);
-		}
-		
-		// Create an initial replication state object.
-		currentDataState = new ReplicationState(0, 0, new ArrayList<Long>(), new ArrayList<Long>(), alignedDate, 0);
-		
-		// Write out the initial "0" state file.
-		persistSequencedCurrentState();
-		
-		// Save the main state file.
-		dataStatePersister.saveState(currentDataState);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processChangeset(XmlChangeReader xmlReader, ReplicationState replicationState) {
-		int intervalLength;
-		ReplicationFileMergerConfiguration configuration;
-		
-		configuration = getConfiguration();
-		
-		// Get the configured interval length.
-		intervalLength = configuration.getIntervalLength();
-		
-		// If this is the first time through, initialise a writer for the next sequence number.
-		if (!sinkActive) {
-			// Increment the current sequence number.
-			currentDataState.setSequenceNumber(currentDataState.getSequenceNumber() + 1);
-			
-			// Initialise an output file for the new sequence number.
-			changeSink = buildResultWriter(currentDataState.getSequenceNumber());
-		}
-		
-		if (intervalLength > 0) {
-			// If this is the first time through, align the timestamp at the next boundary.
-			if (!sinkActive) {
-				Date intervalEnd;
-				
-				intervalEnd = new Date(currentDataState.getTimestamp().getTime() + intervalLength);
-				intervalEnd = alignDateToIntervalBoundary(intervalEnd, intervalLength);
-				currentDataState.setTimestamp(intervalEnd);
-			}
-			
-			// If the replication state has moved us past the current interval end point we need to
-			// open a new interval. This may occur many times if the current replication state moves
-			// us past several intervals.
-			while ((replicationState.getTimestamp().getTime() - currentDataState.getTimestamp().getTime())
-					>= intervalLength) {
-				
-				// If we have an open changeset writer, close it and save the current state.
-				changeSink.complete();
-				changeSink.release();
-				
-				persistSequencedCurrentState();
-				dataStatePersister.saveState(currentDataState);
-				
-				// Update the state to match the next interval.
-				currentDataState.setSequenceNumber(currentDataState.getSequenceNumber() + 1);
-				currentDataState.setTimestamp(
-						new Date(currentDataState.getTimestamp().getTime() + configuration.getIntervalLength()));
-				
-				// Begin a new interval.
-				changeSink = buildResultWriter(currentDataState.getSequenceNumber());
-			}
-			
-		} else {
-			// There is no maximum interval set, so simply update the current state based on the
-			// current replication state.
-			currentDataState.setTimestamp(replicationState.getTimestamp());
-		}
-		
-		// Write the changeset to the writer.
-		writeChangeset(xmlReader);
-		
-		// We are guaranteed to have an active writer at this point.
-		sinkActive = true;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processComplete() {
-		if (sinkActive) {
-			changeSink.complete();
-			persistSequencedCurrentState();
-			dataStatePersister.saveState(currentDataState);
-			
-			changeSink.release();
-			changeSink = null;
-			
-			sinkActive = false;
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void processRelease() {
-		if (sinkActive) {
-			changeSink.release();
-			sinkActive = false;
-		}
-	}
-}
diff --git a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java b/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java
deleted file mode 100644
index beef998..0000000
--- a/replication/src/main/java/org/openstreetmap/osmosis/replication/v0_6/ReplicationLagReader.java
+++ /dev/null
@@ -1,162 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.replication.v0_6;
-
-import java.io.File;
-import java.util.Properties;
-import java.util.logging.Logger;
-import java.text.MessageFormat;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.common.RunnableTask;
-import org.openstreetmap.osmosis.core.util.FileBasedLock;
-import org.openstreetmap.osmosis.core.util.PropertiesPersister;
-import org.openstreetmap.osmosis.replication.common.ReplicationState;
-import org.openstreetmap.osmosis.replication.common.ServerStateReader;
-import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;
-
-/**
- * Compares the timestamp of a local replication directory and the timestamp on the 
- * HTTP server that is configured to provide the replication files. It calculates 
- * the number of seconds the local replication directory is behind the HTTP server
- * and prints it to stdout.
- * 
- * @author Peter Koerner
- */
-public class ReplicationLagReader implements RunnableTask {
-	private static final Logger LOG = Logger.getLogger(ReplicationLagReader.class.getName());
-	private static final String LOCK_FILE_NAME = "download.lock";
-	private static final String CONFIG_FILE = "configuration.txt";
-	private static final String LOCAL_STATE_FILE = "state.txt";
-	
-	private boolean humanReadable;
-	private File workingDirectory;
-	private ServerStateReader serverStateReader;
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param workingDirectory
-	 *            The directory containing configuration and tracking files.
-	 * @param humanReadable
-	 *            Print the replication lag in a Hours, Minutes and Seconds
-	 *            instead of the raw number of seconds
-	 */
-	public ReplicationLagReader(File workingDirectory, boolean humanReadable) {
-		this.workingDirectory = workingDirectory;
-		this.humanReadable = humanReadable;
-		
-		serverStateReader = new ServerStateReader();
-	}
-	
-	
-	/**
-	 * Calculate the replication lag and print it to stdout
-	 */
-	private void getLag() {
-		ReplicationDownloaderConfiguration configuration;
-		ReplicationState serverState;
-		ReplicationState localState;
-		PropertiesPersister localStatePersistor;
-		Properties localStateProperties;
-		
-		// Instantiate utility objects.
-		configuration = new ReplicationDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
-		
-		// Obtain the server state.
-		LOG.fine("Reading current server state.");
-		serverState = serverStateReader.getServerState(configuration.getBaseUrl());
-		
-		// Build the local state persister which is used for both loading and storing local state.
-		localStatePersistor = new PropertiesPersister(new File(workingDirectory, LOCAL_STATE_FILE));
-		
-		// If local state isn't available we need to fail because no lag can be calculated.
-		if (!localStatePersistor.exists()) {
-			throw new OsmosisRuntimeException("Can't read local state.");
-		}
-		
-		// fetch the local state from the file
-		localStateProperties = localStatePersistor.load();
-		localState = new ReplicationState(localStateProperties);
-		
-		// extract the time of the local and the remote state files
-		long local = localState.getTimestamp().getTime();
-		long server = serverState.getTimestamp().getTime();
-		
-		// we assume the server being before the local state while calculating the difference
-		long lag = (server - local) / 1000;
-		
-		// check if a human readable version is requested
-		if (this.humanReadable) {
-			
-			if (lag > 86400) {
-				
-				// more than a day
-				Object[] args = {
-					new Long(lag / 86400), 
-					new Long((lag % 86400) / 3600)
-				};
-				System.out.println(
-					new MessageFormat("{0} day(s) and {1} hour(s)").format(args)
-				);
-				
-			} else if (lag > 3600) {
-				
-				// morte than an hour
-				Object[] args = {
-					new Long(lag / 3600), 
-					new Long((lag % 3600) / 60)
-				};
-				System.out.println(
-					new MessageFormat("{0} hour(s) and {1} minute(s)").format(args)
-				);
-				
-			} else if (lag > 60) {
-				
-				// more than a minute
-				Object[] args = {
-					new Long(lag / 60), 
-					new Long(lag % 60)
-				};
-				System.out.println(
-					new MessageFormat("{0} minute(s) and {1} second(s)").format(args)
-				);
-				
-			} else {
-				
-				// just some seconds
-				System.out.println(
-					new MessageFormat("{0} second(s)").format(lag)
-				);
-				
-			}
-			
-		} else {
-			
-			// print out the raw number of seconds
-			System.out.println(lag);
-			
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		FileBasedLock fileLock;
-		
-		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE_NAME));
-		
-		try {
-			fileLock.lock();
-			
-			getLag();
-			
-			fileLock.unlock();
-			
-		} finally {
-			fileLock.release();
-		}
-	}
-}
diff --git a/set/.checkstyle b/set/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/set/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/set/.classpath b/set/.classpath
deleted file mode 100644
index 14f8225..0000000
--- a/set/.classpath
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Xml"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/set/.gitignore b/set/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/set/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/set/.project b/set/.project
deleted file mode 100644
index 38de264..0000000
--- a/set/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Set</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/set/build.xml b/set/build.xml
deleted file mode 100644
index 9f0f554..0000000
--- a/set/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Set" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/set/ivy.xml b/set/ivy.xml
deleted file mode 100644
index 703d900..0000000
--- a/set/ivy.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-set"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java b/set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java
deleted file mode 100644
index 6c7853a..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/SetPluginLoader.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.plugin.PluginLoader;
-import org.openstreetmap.osmosis.set.v0_6.ChangeAppenderFactory;
-import org.openstreetmap.osmosis.set.v0_6.ChangeApplierFactory;
-import org.openstreetmap.osmosis.set.v0_6.ChangeDeriverFactory;
-import org.openstreetmap.osmosis.set.v0_6.ChangeMergerFactory;
-import org.openstreetmap.osmosis.set.v0_6.ChangeSimplifierFactory;
-import org.openstreetmap.osmosis.set.v0_6.EntityMergerFactory;
-import org.openstreetmap.osmosis.set.v0_6.FlattenFilterFactory;
-
-
-/**
- * The plugin loader for the Set manipulation tasks.
- * 
- * @author Brett Henderson
- */
-public class SetPluginLoader implements PluginLoader {
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Map<String, TaskManagerFactory> loadTaskFactories() {
-		Map<String, TaskManagerFactory> factoryMap;
-		
-		factoryMap = new HashMap<String, TaskManagerFactory>();
-		
-		factoryMap.put("apply-change", new ChangeApplierFactory());
-		factoryMap.put("ac", new ChangeApplierFactory());
-		factoryMap.put("derive-change", new ChangeDeriverFactory());
-		factoryMap.put("dc", new ChangeDeriverFactory());
-		factoryMap.put("flatten", new FlattenFilterFactory());
-		factoryMap.put("f", new FlattenFilterFactory());
-		factoryMap.put("merge", new EntityMergerFactory());
-		factoryMap.put("m", new EntityMergerFactory());
-		factoryMap.put("merge-change", new ChangeMergerFactory());
-		factoryMap.put("mc", new ChangeMergerFactory());
-		factoryMap.put("append-change", new ChangeAppenderFactory());
-		factoryMap.put("apc", new ChangeAppenderFactory());
-		factoryMap.put("simplify-change", new ChangeSimplifierFactory());
-		factoryMap.put("simc", new ChangeSimplifierFactory());
-		
-		factoryMap.put("apply-change-0.6", new ChangeApplierFactory());
-		factoryMap.put("derive-change-0.6", new ChangeDeriverFactory());
-		factoryMap.put("flatten-0.6", new FlattenFilterFactory());
-		factoryMap.put("merge-0.6", new EntityMergerFactory());
-		factoryMap.put("merge-change-0.6", new ChangeMergerFactory());
-		factoryMap.put("append-change-0.6", new ChangeAppenderFactory());
-		factoryMap.put("simplify-change-0.6", new ChangeSimplifierFactory());
-		
-		return factoryMap;
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java
deleted file mode 100644
index 2505d67..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeAppender.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.MultiChangeSinkRunnableChangeSource;
-
-/**
- * Combines multiple change sources into a single data set. It is done by writing the contents of
- * each of the input sources to the sink in order.
- * 
- * @author Brett Henderson
- */
-public class ChangeAppender implements MultiChangeSinkRunnableChangeSource {
-	
-	private List<DataPostbox<ChangeContainer>> sources;
-	private ChangeSink changeSink;
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sourceCount
-	 *            The number of sources to be appended.
-	 * @param inputBufferCapacity
-	 *            The capacity of the buffer to use for each source, in objects.
-	 */
-	public ChangeAppender(int sourceCount, int inputBufferCapacity) {
-		sources = new ArrayList<DataPostbox<ChangeContainer>>(sourceCount);
-		
-		for (int i = 0; i < sourceCount; i++) {
-			sources.add(new DataPostbox<ChangeContainer>(inputBufferCapacity));
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public ChangeSink getChangeSink(int instance) {
-		if (instance < 0 || instance >= sources.size()) {
-			throw new OsmosisRuntimeException("Sink instance " + instance + " is not valid.");
-		}
-		
-		return new DataPostboxChangeSink(sources.get(instance));
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int getChangeSinkCount() {
-		return sources.size();
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			// Write the data from each source to the sink in turn.
-			for (DataPostbox<ChangeContainer> source : sources) {
-				while (source.hasNext()) {
-					changeSink.process(source.getNext());
-				}
-			}
-			
-			changeSink.complete();
-			
-			completed = true;
-		
-		} finally {
-			if (!completed) {
-				for (DataPostbox<ChangeContainer> source : sources) {
-					source.setOutputError();
-				}
-			}
-			
-			changeSink.release();
-		}
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java
deleted file mode 100644
index f4ff849..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeApplier.java
+++ /dev/null
@@ -1,235 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedDeltaChangePipeValidator;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkMultiChangeSinkRunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
-
-
-/**
- * Applies a change set to an input source and produces an updated data set.
- * 
- * @author Brett Henderson
- */
-public class ChangeApplier implements MultiSinkMultiChangeSinkRunnableSource {
-	
-	private Sink sink;
-	private DataPostbox<EntityContainer> basePostbox;
-	private SortedEntityPipeValidator sortedEntityValidator;
-	private DataPostbox<ChangeContainer> changePostbox;
-	private SortedDeltaChangePipeValidator sortedChangeValidator;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param inputBufferCapacity
-	 *            The size of the buffers to use for input sources.
-	 */
-	public ChangeApplier(int inputBufferCapacity) {
-		basePostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
-		sortedEntityValidator = new SortedEntityPipeValidator();
-		sortedEntityValidator.setSink(new DataPostboxSink(basePostbox));
-		changePostbox = new DataPostbox<ChangeContainer>(inputBufferCapacity);
-		sortedChangeValidator = new SortedDeltaChangePipeValidator();
-		sortedChangeValidator.setChangeSink(new DataPostboxChangeSink(changePostbox));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public Sink getSink(int instance) {
-		if (instance != 0) {
-			throw new OsmosisRuntimeException("Sink instance " + instance
-					+ " is not valid.");
-		}
-		
-		return sortedEntityValidator;
-	}
-
-
-	/**
-	 * This implementation always returns 1.
-	 * 
-	 * @return 1
-	 */
-	public int getSinkCount() {
-		return 1;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public ChangeSink getChangeSink(int instance) {
-		if (instance != 0) {
-			throw new OsmosisRuntimeException("Change sink instance " + instance
-					+ " is not valid.");
-		}
-		
-		return sortedChangeValidator;
-	}
-
-
-	/**
-	 * This implementation always returns 1.
-	 * 
-	 * @return 1
-	 */
-	public int getChangeSinkCount() {
-		return 1;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * Processes an entity that exists on the base source but not the change
-	 * source.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	private void processBaseOnlyEntity(EntityContainer entityContainer) {
-		// The base entity doesn't exist on the change source therefore we
-		// simply pass it through.
-		sink.process(entityContainer);
-	}
-	
-	
-	/**
-	 * Processes a change for an entity that exists on the change source but not
-	 * the base source.
-	 * 
-	 * @param changeContainer
-	 *            The change to be processed.
-	 */
-	private void processChangeOnlyEntity(ChangeContainer changeContainer) {
-		// This entity doesn't exist in the "base" source therefore
-		// we would normally expect a create.
-		// But to cover cases where the change is being re-applied or it is a
-		// previously deleted item which will show as a modify we need to be
-		// lenient with error checking.
-		// It is also possible that a delete will come through for a
-		// previously deleted item which can be ignored.
-		if (changeContainer.getAction().equals(ChangeAction.Create)
-				|| changeContainer.getAction().equals(ChangeAction.Modify)) {
-			
-			sink.process(changeContainer.getEntityContainer());
-		}
-	}
-	
-	
-	/**
-	 * Processes a change for an entity that exists on both the base source and
-	 * the change source.
-	 * 
-	 * @param changeContainer
-	 *            The change to be processed.
-	 */
-	private void processBothSourceEntity(EntityContainer entityContainer, ChangeContainer changeContainer) {
-		// The same entity exists in both sources therefore we are
-		// expecting a modify or delete. However a create is possible if the
-		// data is being re-applied so we need to be lenient.
-		if (changeContainer.getAction().equals(ChangeAction.Create)
-				|| changeContainer.getAction().equals(ChangeAction.Modify)) {
-			
-			sink.process(changeContainer.getEntityContainer());
-		}
-	}
-
-
-	/**
-	 * Processes the input sources and sends the updated data stream to the
-	 * sink.
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			EntityContainerComparator comparator;
-			EntityContainer base = null;
-			ChangeContainer change = null;
-			
-			// Create a comparator for comparing two entities by type and identifier.
-			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
-			
-			// We continue in the comparison loop while both sources still have data.
-			while ((base != null || basePostbox.hasNext()) && (change != null || changePostbox.hasNext())) {
-				int comparisonResult;
-				
-				// Get the next input data where required.
-				if (base == null) {
-					base = basePostbox.getNext();
-				}
-				if (change == null) {
-					change = changePostbox.getNext();
-				}
-				
-				// Compare the two sources.
-				comparisonResult = comparator.compare(base, change.getEntityContainer());
-				
-				if (comparisonResult < 0) {
-					processBaseOnlyEntity(base);
-					base = null;
-					
-				} else if (comparisonResult > 0) {
-					processChangeOnlyEntity(change);
-					change = null;
-					
-				} else {
-					processBothSourceEntity(base, change);
-					base = null;
-					change = null;
-				}
-			}
-			
-			// Any remaining "base" entities are unmodified.
-			while (base != null || basePostbox.hasNext()) {
-				if (base == null) {
-					base = basePostbox.getNext();
-				}
-				processBaseOnlyEntity(base);
-				base = null;
-			}
-			
-			// Any remaining "change" entities must be creates.
-			while (change != null || changePostbox.hasNext()) {
-				if (change == null) {
-					change = changePostbox.getNext();
-				}
-				processChangeOnlyEntity(change);
-				change = null;
-			}
-			
-			sink.complete();
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				basePostbox.setOutputError();
-				changePostbox.setOutputError();
-			}
-			
-			sink.release();
-		}
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java
deleted file mode 100644
index 1def6b0..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeDeriver.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.change.v0_6.impl.TimestampSetter;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableChangeSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
-
-
-/**
- * Compares two different data sources and produces a set of differences.
- * 
- * @author Brett Henderson
- */
-public class ChangeDeriver implements MultiSinkRunnableChangeSource {
-
-	private ChangeSink changeSink;
-	private DataPostbox<EntityContainer> fromPostbox;
-	private DataPostboxSink fromSink;
-	private DataPostbox<EntityContainer> toPostbox;
-	private DataPostboxSink toSink;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param inputBufferCapacity
-	 *            The size of the buffers to use for input sources.
-	 */
-	public ChangeDeriver(int inputBufferCapacity) {
-		fromPostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
-		fromSink = new DataPostboxSink(fromPostbox);
-		toPostbox = new DataPostbox<EntityContainer>(inputBufferCapacity);
-		toSink = new DataPostboxSink(toPostbox);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public Sink getSink(int instance) {
-		switch (instance) {
-		case 0:
-			return fromSink;
-		case 1:
-			return toSink;
-		default:
-			throw new OsmosisRuntimeException("Sink instance " + instance
-					+ " is not valid.");
-		}
-	}
-
-
-	/**
-	 * This implementation always returns 2.
-	 * 
-	 * @return 2
-	 */
-	public int getSinkCount() {
-		return 2;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * Processes the input sources and sends the changes to the change sink.
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			EntityContainerComparator comparator;
-			EntityContainer fromEntityContainer = null;
-			EntityContainer toEntityContainer = null;
-			TimestampSetter timestampSetter;
-			
-			// Create a comparator for comparing two entities by type and identifier.
-			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
-			
-			// Create an object for setting the current timestamp on entities being deleted.
-			timestampSetter = new TimestampSetter();
-			
-			// We continue in the comparison loop while both sources still have data.
-			while (
-					(fromEntityContainer != null || fromPostbox.hasNext())
-					&& (toEntityContainer != null || toPostbox.hasNext())) {
-				int comparisonResult;
-				
-				// Get the next input data where required.
-				if (fromEntityContainer == null) {
-					fromEntityContainer = fromPostbox.getNext();
-				}
-				if (toEntityContainer == null) {
-					toEntityContainer = toPostbox.getNext();
-				}
-				
-				// Compare the two sources.
-				comparisonResult = comparator.compare(fromEntityContainer, toEntityContainer);
-				
-				if (comparisonResult < 0) {
-					// The from entity doesn't exist on the to source therefore
-					// has been deleted. We don't know when the entity was
-					// deleted so set the delete time to the current time.
-					changeSink.process(
-							new ChangeContainer(
-									timestampSetter.updateTimestamp(fromEntityContainer),
-									ChangeAction.Delete));
-					fromEntityContainer = null;
-				} else if (comparisonResult > 0) {
-					// The to entity doesn't exist on the from source therefore has
-					// been created.
-					changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Create));
-					toEntityContainer = null;
-				} else {
-					// The entity exists on both sources, therefore we must
-					// compare
-					// the entities directly. If there is a difference, the
-					// entity has been modified.
-					if (!fromEntityContainer.getEntity().equals(toEntityContainer.getEntity())) {
-						changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Modify));
-					}
-					fromEntityContainer = null;
-					toEntityContainer = null;
-				}
-			}
-			
-			// Any remaining "from" entities are deletes.
-			while (fromEntityContainer != null || fromPostbox.hasNext()) {
-				if (fromEntityContainer == null) {
-					fromEntityContainer = fromPostbox.getNext();
-				}
-
-				// The from entity doesn't exist on the to source therefore
-				// has been deleted. We don't know when the entity was
-				// deleted so set the delete time to the current time.
-				changeSink.process(
-						new ChangeContainer(
-								timestampSetter.updateTimestamp(fromEntityContainer),
-								ChangeAction.Delete));
-				fromEntityContainer = null;
-			}
-			// Any remaining "to" entities are creates.
-			while (toEntityContainer != null || toPostbox.hasNext()) {
-				if (toEntityContainer == null) {
-					toEntityContainer = toPostbox.getNext();
-				}
-				changeSink.process(new ChangeContainer(toEntityContainer, ChangeAction.Create));
-				toEntityContainer = null;
-			}
-			
-			changeSink.complete();
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				fromPostbox.setOutputError();
-				toPostbox.setOutputError();
-			}
-			
-			changeSink.release();
-		}
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java
deleted file mode 100644
index d5e280f..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeMerger.java
+++ /dev/null
@@ -1,202 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdThenVersionComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedHistoryChangePipeValidator;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.MultiChangeSinkRunnableChangeSource;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxChangeSink;
-
-
-/**
- * Merges two change sources into a single data set. Conflicting elements are
- * resolved by using either the latest timestamp (default) or always selecting
- * the second source.
- * 
- * @author Brett Henderson
- */
-public class ChangeMerger implements MultiChangeSinkRunnableChangeSource {
-	
-	private ChangeSink changeSink;
-	private DataPostbox<ChangeContainer> postbox0;
-	private SortedHistoryChangePipeValidator sortedChangeValidator0;
-	private DataPostbox<ChangeContainer> postbox1;
-	private SortedHistoryChangePipeValidator sortedChangeValidator1;
-	private ConflictResolutionMethod conflictResolutionMethod;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param conflictResolutionMethod
-	 *            The method to used to resolve conflict when two sources
-	 *            contain the same entity.
-	 * @param inputBufferCapacity
-	 *            The size of the buffers to use for input sources.
-	 */
-	public ChangeMerger(ConflictResolutionMethod conflictResolutionMethod, int inputBufferCapacity) {
-		this.conflictResolutionMethod = conflictResolutionMethod;
-		
-		postbox0 = new DataPostbox<ChangeContainer>(inputBufferCapacity);
-		sortedChangeValidator0 = new SortedHistoryChangePipeValidator();
-		sortedChangeValidator0.setChangeSink(new DataPostboxChangeSink(postbox0));
-		
-		postbox1 = new DataPostbox<ChangeContainer>(inputBufferCapacity);
-		sortedChangeValidator1 = new SortedHistoryChangePipeValidator();
-		sortedChangeValidator1.setChangeSink(new DataPostboxChangeSink(postbox1));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public ChangeSink getChangeSink(int instance) {
-		// Determine which postbox should be written to.
-		switch (instance) {
-		case 0:
-			return sortedChangeValidator0;
-		case 1:
-			return sortedChangeValidator1;
-		default:
-			throw new OsmosisRuntimeException("Sink instance " + instance + " is not valid.");
-		}
-	}
-
-
-	/**
-	 * This implementation always returns 2.
-	 * 
-	 * @return 2
-	 */
-	public int getChangeSinkCount() {
-		return 2;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			EntityContainerComparator comparator;
-			ChangeContainer changeContainer0 = null;
-			ChangeContainer changeContainer1 = null;
-			
-			// Create a comparator for comparing two entities by type and identifier.
-			comparator = new EntityContainerComparator(new EntityByTypeThenIdThenVersionComparator());
-			
-			// We continue in the comparison loop while both sources still have data.
-			while (
-					(changeContainer0 != null || postbox0.hasNext())
-					&& (changeContainer1 != null || postbox1.hasNext())) {
-				long comparisonResult;
-				
-				// Get the next input data where required.
-				if (changeContainer0 == null) {
-					changeContainer0 = postbox0.getNext();
-				}
-				if (changeContainer1 == null) {
-					changeContainer1 = postbox1.getNext();
-				}
-				
-				// Compare the two entities.
-				comparisonResult =
-					comparator.compare(changeContainer0.getEntityContainer(), changeContainer1.getEntityContainer());
-				
-				if (comparisonResult < 0) {
-					// Entity 0 doesn't exist on the other source and can be
-					// sent straight through.
-					changeSink.process(changeContainer0);
-					changeContainer0 = null;
-				} else if (comparisonResult > 0) {
-					// Entity 1 doesn't exist on the other source and can be
-					// sent straight through.
-					changeSink.process(changeContainer1);
-					changeContainer1 = null;
-				} else {
-					// The entity exists on both sources so we must resolve the conflict.
-					if (conflictResolutionMethod.equals(ConflictResolutionMethod.Timestamp)) {
-						int timestampComparisonResult;
-						
-						timestampComparisonResult =
-							changeContainer0.getEntityContainer().getEntity().getTimestamp()
-							.compareTo(changeContainer1.getEntityContainer().getEntity().getTimestamp());
-						
-						if (timestampComparisonResult < 0) {
-							changeSink.process(changeContainer1);
-						} else if (timestampComparisonResult > 0) {
-							changeSink.process(changeContainer0);
-						} else {
-							// If both have identical timestamps, use the second source.
-							changeSink.process(changeContainer1);
-						}
-						
-					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.LatestSource)) {
-						changeSink.process(changeContainer1);
-					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.Version)) {
-						int version0 = changeContainer0.getEntityContainer().getEntity().getVersion();
-						int version1 = changeContainer1.getEntityContainer().getEntity().getVersion();
-						if (version0 < version1) {
-							changeSink.process(changeContainer1);
-						} else if (version0 > version1) {
-							changeSink.process(changeContainer0);
-						} else {
-							// If both have identical versions, use the second source.
-							changeSink.process(changeContainer1);
-						}
-
-					} else {
-						throw new OsmosisRuntimeException(
-								"Conflict resolution method " + conflictResolutionMethod + " is not recognized.");
-					}
-					
-					changeContainer0 = null;
-					changeContainer1 = null;
-				}
-			}
-			
-			// Any remaining entities on either source can be sent straight through.
-			while (changeContainer0 != null || postbox0.hasNext()) {
-				if (changeContainer0 == null) {
-					changeContainer0 = postbox0.getNext();
-				}
-				changeSink.process(changeContainer0);
-				changeContainer0 = null;
-			}
-			while (changeContainer1 != null || postbox1.hasNext()) {
-				if (changeContainer1 == null) {
-					changeContainer1 = postbox1.getNext();
-				}
-				changeSink.process(changeContainer1);
-				changeContainer1 = null;
-			}
-			
-			changeSink.complete();
-			
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				postbox0.setOutputError();
-				postbox1.setOutputError();
-			}
-			
-			changeSink.release();
-		}
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java
deleted file mode 100644
index 590ba47..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifier.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.set.v0_6.impl.ChangeSimplifierImpl;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedHistoryChangePipeValidator;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * Looks at a sorted change stream and condenses multiple changes for a single entity into a single
- * change.
- * 
- * @author Brett Henderson
- */
-public class ChangeSimplifier implements ChangeSinkChangeSource {
-
-	private SortedHistoryChangePipeValidator orderingValidator;
-	private ChangeSimplifierImpl changeSimplifier;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public ChangeSimplifier() {
-		orderingValidator = new SortedHistoryChangePipeValidator();
-		changeSimplifier = new ChangeSimplifierImpl();
-		
-		orderingValidator.setChangeSink(changeSimplifier);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(ChangeContainer change) {
-		orderingValidator.process(change);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		orderingValidator.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		orderingValidator.release();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		changeSimplifier.setChangeSink(changeSink);
-	}
-
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java
deleted file mode 100644
index cb60878..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/EntityMerger.java
+++ /dev/null
@@ -1,291 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityByTypeThenIdComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.EntityContainerComparator;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedEntityPipeValidator;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.MultiSinkRunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.set.v0_6.impl.DataPostboxSink;
-
-
-/**
- * Merges two sources into a single data set. Conflicting elements are resolved
- * by using either the latest timestamp (default) or always selecting the second
- * source.
- * 
- * @author Brett Henderson
- */
-public class EntityMerger implements MultiSinkRunnableSource {
-	
-	private static final Logger LOG = Logger.getLogger(EntityMerger.class.getName());
-
-	private Sink sink;
-	private DataPostbox<EntityContainer> postbox0;
-	private SortedEntityPipeValidator sortedEntityValidator0;
-	private DataPostbox<EntityContainer> postbox1;
-	private SortedEntityPipeValidator sortedEntityValidator1;
-	private ConflictResolutionMethod conflictResolutionMethod;
-	private BoundRemovedAction boundRemovedAction;
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param conflictResolutionMethod
-	 *            The method to used to resolve conflict when two sources
-	 *            contain the same entity.
-	 * @param inputBufferCapacity
-	 *            The size of the buffers to use for input sources.
-	 * @param boundRemovedAction
-	 *            The action to take if the merge operation removes 
-	 *            a bound entity.
-	 */
-	public EntityMerger(ConflictResolutionMethod conflictResolutionMethod, int inputBufferCapacity, 
-			BoundRemovedAction boundRemovedAction) {
-		
-		this.conflictResolutionMethod = conflictResolutionMethod;
-		
-		postbox0 = new DataPostbox<EntityContainer>(inputBufferCapacity);
-		sortedEntityValidator0 = new SortedEntityPipeValidator();
-		sortedEntityValidator0.setSink(new DataPostboxSink(postbox0));
-		
-		postbox1 = new DataPostbox<EntityContainer>(inputBufferCapacity);
-		sortedEntityValidator1 = new SortedEntityPipeValidator();
-		sortedEntityValidator1.setSink(new DataPostboxSink(postbox1));
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public Sink getSink(int instance) {
-		// Determine which postbox should be written to.
-		switch (instance) {
-		case 0:
-			return sortedEntityValidator0;
-		case 1:
-			return sortedEntityValidator1;
-		default:
-			throw new OsmosisRuntimeException("Sink instance " + instance
-					+ " is not valid.");
-		}
-	}
-
-
-	/**
-	 * This implementation always returns 2.
-	 * 
-	 * @return 2
-	 */
-	public int getSinkCount() {
-		return 2;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void run() {
-		boolean completed = false;
-		
-		try {
-			EntityContainerComparator comparator;
-			EntityContainer entityContainer0 = null;
-			EntityContainer entityContainer1 = null;
-			
-			// Create a comparator for comparing two entities by type and identifier.
-			comparator = new EntityContainerComparator(new EntityByTypeThenIdComparator());
-			
-			// BEGIN bound special handling
-			
-			// If there is a bound, it's going to be the first object 
-			// in a properly sorted stream
-			entityContainer0 = nextOrNull(postbox0);
-			entityContainer1 = nextOrNull(postbox1);
-					
-			// There's only need for special processing if there actually is some data
-			// on both streams - no data implies no bound
-			if (entityContainer0 != null && entityContainer1 != null) {
-				Bound bound0 = null;
-				Bound bound1 = null;
-				
-				// If there are any bounds upstream, eat them up
-				if (entityContainer0.getEntity().getType() == EntityType.Bound) {
-					bound0 = (Bound) entityContainer0.getEntity();
-					entityContainer0 = nextOrNull(postbox0);
-				}
-				if (entityContainer1.getEntity().getType() == EntityType.Bound) {
-					bound1 = (Bound) entityContainer1.getEntity();
-					entityContainer1 = nextOrNull(postbox1);
-				}
-
-				// Only post a bound downstream if both upstream sources had a bound.
-				// (Otherwise there's either nothing to post or the posted bound is going
-				// to be smaller than the actual data, which is bad)
-				if (bound0 != null && bound1 != null) {
-					sink.process(new BoundContainer(bound0.union(bound1)));
-				} else if ((bound0 != null && bound1 == null)
-						|| (bound0 == null && bound1 != null)) {
-					handleBoundRemoved(bound0 == null);
-				}
-			}
-			
-			// END bound special handling
-			
-			// We continue in the comparison loop while both sources still have data.
-			while (
-					(entityContainer0 != null || postbox0.hasNext())
-					&& (entityContainer1 != null || postbox1.hasNext())) {
-				long comparisonResult;
-				
-				// Get the next input data where required.
-				if (entityContainer0 == null) {
-					entityContainer0 = postbox0.getNext();
-				}
-				if (entityContainer1 == null) {
-					entityContainer1 = postbox1.getNext();
-				}
-				
-				// Compare the two entities.
-				comparisonResult = comparator.compare(entityContainer0, entityContainer1);
-				
-				if (comparisonResult < 0) {
-					// Entity 0 doesn't exist on the other source and can be
-					// sent straight through.
-					sink.process(entityContainer0);
-					entityContainer0 = null;
-				} else if (comparisonResult > 0) {
-					// Entity 1 doesn't exist on the other source and can be
-					// sent straight through.
-					sink.process(entityContainer1);
-					entityContainer1 = null;
-				} else {
-					// The entity exists on both sources so we must resolve the conflict.
-					if (conflictResolutionMethod.equals(ConflictResolutionMethod.Timestamp)) {
-						int timestampComparisonResult;
-						
-						timestampComparisonResult =
-							entityContainer0.getEntity().getTimestamp()
-								.compareTo(entityContainer1.getEntity().getTimestamp());
-						
-						if (timestampComparisonResult < 0) {
-							sink.process(entityContainer1);
-						} else if (timestampComparisonResult > 0) {
-							sink.process(entityContainer0);
-						} else {
-							// If both have identical timestamps, use the second source.
-							sink.process(entityContainer1);
-						}
-						
-					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.LatestSource)) {
-						sink.process(entityContainer1);
-					} else if (conflictResolutionMethod.equals(ConflictResolutionMethod.Version)) {
-						int version0 = entityContainer0.getEntity().getVersion();
-						int version1 = entityContainer1.getEntity().getVersion();
-						if (version0 < version1) {
-							sink.process(entityContainer1);
-						} else if (version0 > version1) {
-							sink.process(entityContainer0);
-						} else {
-							// If both have identical versions, use the second source.
-							sink.process(entityContainer1);
-						}
-
-					} else {
-						throw new OsmosisRuntimeException(
-								"Conflict resolution method " + conflictResolutionMethod + " is not recognized.");
-					}
-					
-					entityContainer0 = null;
-					entityContainer1 = null;
-				}
-			}
-			
-			// Any remaining entities on either source can be sent straight through.
-			while (entityContainer0 != null || postbox0.hasNext()) {
-				if (entityContainer0 == null) {
-					entityContainer0 = postbox0.getNext();
-				}
-				sink.process(entityContainer0);
-				entityContainer0 = null;
-			}
-			while (entityContainer1 != null || postbox1.hasNext()) {
-				if (entityContainer1 == null) {
-					entityContainer1 = postbox1.getNext();
-				}
-				sink.process(entityContainer1);
-				entityContainer1 = null;
-			}
-			
-			sink.complete();
-			
-			completed = true;
-			
-		} finally {
-			if (!completed) {
-				postbox0.setOutputError();
-				postbox1.setOutputError();
-			}
-			
-			sink.release();
-		}
-	}
-		
-	private void handleBoundRemoved(boolean source0BoundMissing) {
-		
-		if (boundRemovedAction == BoundRemovedAction.Ignore) {
-			// Nothing to do
-			return;
-		}
-		
-		// Message for log or exception
-		String missingSourceID, otherSourceID;
-		
-		if (source0BoundMissing) {
-			missingSourceID = "0";
-			otherSourceID = "1";
-		} else {
-			missingSourceID = "1";
-			otherSourceID = "0";
-		}
-		
-		String message = String.format(
-				"Source %s of the merge task has an explicit bound set, but source %s has not. "
-				+ "Therefore the explicit bound has been removed from the merged stream.", 
-				missingSourceID, otherSourceID);
-		
-		// Now actually log or fail.
-		if (boundRemovedAction == BoundRemovedAction.Warn) {
-			LOG.warning(message);
-		} else if (boundRemovedAction == BoundRemovedAction.Fail) {
-			throw new OsmosisRuntimeException(message);
-		}
-	}
-
-
-	private static EntityContainer nextOrNull(DataPostbox<EntityContainer> postbox) {
-
-		if (postbox.hasNext()) {
-			return postbox.getNext();
-		}
-		
-		return null;
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java
deleted file mode 100644
index 0470253..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/FlattenFilter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.sort.v0_6.SortedDuplicateEntityPipeValidator;
-
-
-/**
- * Flatten / simplify a sorted entity stream. (similar to --simplify-change)
- */
-public class FlattenFilter extends SortedDuplicateEntityPipeValidator {
-	private Sink sink;
-	
-	private Sink flattener = new Sink() {
-		private EntityContainer previousContainer;
-		
-		/**
-		 * Process a node, way or relation.
-		 * 
-		 * @param currentContainer
-		 *            The entity container to be processed.
-		 */
-		@Override
-		public void process(EntityContainer currentContainer) {
-			if (previousContainer == null) {
-				previousContainer = currentContainer;
-				return;
-			}
-
-			Entity current = currentContainer.getEntity();
-			Entity previous = previousContainer.getEntity();
-
-			if (current.getId() != previous.getId() || !current.getType().equals(previous.getType())) {
-				sink.process(previousContainer);
-				previousContainer = currentContainer;
-				return;
-			}
-
-			if (current.getVersion() > previous.getVersion()) {
-				previousContainer = currentContainer;
-			}
-		}
-
-
-		@Override
-		public void complete() {
-			/*
-			 * If we've stored entities temporarily, we now need to forward the
-			 * stored ones to the output.
-			 */
-			if (previousContainer != null) {
-				sink.process(previousContainer);
-			}
-
-			sink.complete();
-		}
-
-
-		@Override
-		public void release() {
-			sink.release();
-		}
-	};
-
-
-	/**
-	 * Creates a new instance.
-	 */
-	public FlattenFilter() {
-		super.setSink(flattener);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java
deleted file mode 100644
index f44fa70..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/ChangeSimplifierImpl.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
-
-
-/**
- * Looks at a sorted change stream and condenses multiple changes for a single entity into a single
- * change.
- * 
- * @author Brett Henderson
- */
-public class ChangeSimplifierImpl implements ChangeSinkChangeSource {
-
-	private List<ChangeContainer> currentChanges;
-	private ChangeSink changeSink;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public ChangeSimplifierImpl() {
-		currentChanges = new ArrayList<ChangeContainer>();
-	}
-	
-	
-	private void flushCurrentChanges() {
-		ChangeContainer changeBegin;
-		ChangeContainer changeEnd;
-		ChangeAction actionBegin;
-		ChangeAction actionEnd;
-		ChangeAction actionResult;
-		
-		changeBegin = currentChanges.get(0);
-		changeEnd = currentChanges.get(currentChanges.size() - 1);
-		
-		actionBegin = changeBegin.getAction();
-		actionEnd = changeEnd.getAction();
-		
-		// If the final action is a delete we'll send a delete action regardless of whether the
-		// first action was a create just in case the create should have been a modify.
-		// If the first action is a create, then the result is always a create (except for delete
-		// case above).
-		// Everything else is treated as a modify.
-		if (actionEnd.equals(ChangeAction.Delete)) {
-			actionResult = ChangeAction.Delete;
-		} else if (actionBegin.equals(ChangeAction.Create)) {
-			actionResult = ChangeAction.Create;
-		} else {
-			actionResult = ChangeAction.Modify;
-		}
-		
-		changeSink.process(new ChangeContainer(changeEnd.getEntityContainer(), actionResult));
-		
-		currentChanges.clear();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(ChangeContainer change) {
-		// If the current change is for a different entity to those in our current changes list,
-		// then we must process the current changes.
-		if (currentChanges.size() > 0) {
-			long currentId;
-			
-			currentId = currentChanges.get(0).getEntityContainer().getEntity().getId();
-			
-			if (currentId != change.getEntityContainer().getEntity().getId()) {
-				flushCurrentChanges();
-			}
-		}
-		
-		currentChanges.add(change);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		if (!currentChanges.isEmpty()) {
-			flushCurrentChanges();
-		}
-		
-		changeSink.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		changeSink.release();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java
deleted file mode 100644
index 31a6d1d..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxChangeSink.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-
-
-/**
- * A change sink that writes all of its data to a postbox to be read by another thread.
- * 
- * @author Brett Henderson
- */
-public class DataPostboxChangeSink implements ChangeSink {
-	private DataPostbox<ChangeContainer> postbox;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param postbox
-	 *            The postbox to write all incoming data into.
-	 */
-	public DataPostboxChangeSink(DataPostbox<ChangeContainer> postbox) {
-		this.postbox = postbox;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(ChangeContainer change) {
-		postbox.put(change);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		postbox.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		postbox.release();
-	}
-}
diff --git a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java b/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java
deleted file mode 100644
index 5073a5e..0000000
--- a/set/src/main/java/org/openstreetmap/osmosis/set/v0_6/impl/DataPostboxSink.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6.impl;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.store.DataPostbox;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-
-/**
- * A sink that writes all of its data to a postbox to be read by another thread.
- * 
- * @author Brett Henderson
- */
-public class DataPostboxSink implements Sink {
-	private DataPostbox<EntityContainer> postbox;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param postbox
-	 *            The postbox to write all incoming data into.
-	 */
-	public DataPostboxSink(DataPostbox<EntityContainer> postbox) {
-		this.postbox = postbox;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(EntityContainer entity) {
-		postbox.put(entity);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		postbox.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		postbox.release();
-	}
-}
diff --git a/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java b/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java
deleted file mode 100644
index 553abb2..0000000
--- a/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/ChangeSimplifierTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.Osmosis;
-import org.openstreetmap.osmosis.testutil.AbstractDataTest;
-
-
-/**
- * Tests the change simplifier task.
- */
-public class ChangeSimplifierTest extends AbstractDataTest {
-	
-	/**
-	 * Tests that a set of changes is simplified correctly.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void test() throws IOException {
-		File sourceFile;
-		File expectedOutputFile;
-		File actualOutputFile;
-		
-		// Generate files.
-		sourceFile = dataUtils.createDataFile("v0_6/simplify-change-in.osc");
-		expectedOutputFile = dataUtils.createDataFile("v0_6/simplify-change-out.osc");
-		actualOutputFile = dataUtils.newFile();
-		
-		// Append the two source files into the destination file.
-		Osmosis.run(
-			new String [] {
-				"-q",
-				"--read-xml-change-0.6",
-				sourceFile.getPath(),
-				"--simplify-change-0.6",
-				"--write-xml-change-0.6",
-				actualOutputFile.getPath()
-			}
-		);
-		
-		// Validate that the output file matches the expected result.
-		dataUtils.compareFiles(expectedOutputFile, actualOutputFile);
-	}
-}
diff --git a/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java b/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java
deleted file mode 100644
index 3a5f857..0000000
--- a/set/src/test/java/org/openstreetmap/osmosis/set/v0_6/MergeBoundTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.set.v0_6;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.merge.common.ConflictResolutionMethod;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-
-/**
- * Tests bounding box processing in merge tasks.
- * 
- * @author Igor Podolskiy
- */
-public class MergeBoundTest {
-
-	/**
-	 * A simple dummy ID generator for the helper source class.
-	 */
-	private static AtomicInteger idGenerator = new AtomicInteger(1000);
-	
-	/**
-	 * Tests the proper working of the merge task if neither 
-	 * source has a declared bound.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testNeitherHasBound() throws Exception {
-		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), false);
-		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), false);
-
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
-
-		Assert.assertEquals(2, mergedList.size());
-		for (EntityContainer entityContainer : mergedList) {
-			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
-		}
-	}
-	
-	/**
-	 * Tests whether merge will delete the declared bound if only source 0 
-	 * has a declared bound.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testSource0HasBound() throws Exception {
-		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), true);
-		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), false);
-
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-
-		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
-		Assert.assertEquals(2, mergedList.size());
-		for (EntityContainer entityContainer : mergedList) {
-			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
-		}
-	}
-	
-	/**
-	 * Tests whether merge will delete the declared bound if only source 1 
-	 * has a declared bound.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testSource1HasBound() throws Exception {
-		RunnableSource source0 = new BoundSource(new Bound(1, 2, 4, 3, "source0"), false);
-		RunnableSource source1 = new BoundSource(new Bound(5, 6, 8, 7, "source1"), true);
-
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
-		
-		Assert.assertEquals(2, mergedList.size());
-		for (EntityContainer entityContainer : mergedList) {
-			Assert.assertEquals(EntityType.Node, entityContainer.getEntity().getType());
-		}
-	}
-	
-	/**
-	 * Test the proper computation of the union bound iff both sources
-	 * have bounds.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testBothHaveBounds() throws Exception {
-		Bound bound0 = new Bound(1, 2, 4, 3, "source1");
-		RunnableSource source0 = new BoundSource(bound0, true);
-
-		Bound bound1 = new Bound(5, 6, 8, 7, "source2");
-		RunnableSource source1 = new BoundSource(bound1, true);
-
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
-		Assert.assertEquals(3, mergedList.size());
-		Assert.assertEquals(EntityType.Bound, mergedList.get(0).getEntity().getType());
-		
-		// Check the bound
-		Bound bound01 = (Bound) mergedList.get(0).getEntity();
-		Assert.assertEquals(bound0.union(bound1), bound01);
-
-		for (int i = 1; i < mergedList.size(); i++) {
-			Assert.assertEquals(EntityType.Node, mergedList.get(i).getEntity().getType());
-		}
-	}
-	
-	/**
-	 * Tests the proper working of the merge task if both sources are
-	 * empty.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testBothEmpty() throws Exception {
-		RunnableSource source0 = new EmptySource();
-		RunnableSource source1 = new EmptySource();
-
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-		Assert.assertTrue("Expected empty result set but got some data", merged.getLastEntityContainer() == null);
-	}
-	
-	/**
-	 * Tests the proper working of the merge task if exactly one source is
-	 * empty with respect to the declared bound.
-	 * 
-	 * @throws Exception if something goes wrong
-	 */
-	@Test
-	public void testOneSourceEmpty() throws Exception {
-		RunnableSource source0 = new EmptySource();
-
-		Bound bound1 = new Bound(5, 6, 8, 7, "source2");
-		RunnableSource source1 = new BoundSource(bound1, true);
-		
-		EntityMerger merger = new EntityMerger(ConflictResolutionMethod.LatestSource, 1,
-				BoundRemovedAction.Ignore);
-		
-		SinkEntityInspector merged = merge(merger, source0, source1);
-		List<EntityContainer> mergedList = createList(merged.getProcessedEntities());
-		
-		Assert.assertEquals(2, mergedList.size());
-		Assert.assertEquals(bound1, mergedList.get(0).getEntity());
-		Assert.assertEquals(EntityType.Node, mergedList.get(1).getEntity().getType());
-	}
-
-	private static <T> List<T> createList(Iterable<T> t) {
-		List<T> list = new ArrayList<T>();
-		for (T elem : t) {
-			list.add(elem);
-		}
-		return list;
-	}
-	
-
-	/**
-	 * Helper method to execute a simple merge of two sources.
-	 * 
-	 * @throws Exception if something goes wrong.
-	 */
-	private SinkEntityInspector merge(EntityMerger merger, 
-			RunnableSource source1, RunnableSource source2) throws Exception {
-		Thread t1 = new Thread(source1);
-		Thread t2 = new Thread(source2);
-		
-		SinkEntityInspector inspector = new SinkEntityInspector();
-		source1.setSink(merger.getSink(0));
-		source2.setSink(merger.getSink(1));
-		merger.setSink(inspector);
-		
-		Thread mThread = new Thread(merger);
-		mThread.start();
-		t1.start();
-		t2.start();
-		mThread.join();
-		t1.join();
-		t2.join();
-		return inspector;
-	}
-
-	/**
-	 * A trivial empty source.
-	 */
-	private static class EmptySource implements RunnableSource {
-
-		private Sink sink;
-		
-		@Override
-		public void setSink(Sink sink) {
-			this.sink = sink;
-		}
-
-		@Override
-		public void run() {
-			sink.complete();
-		}
-	}
-
-	/**
-	 * A simple source which provides a single node in the center of a given
-	 * bounding box, and optionally, a declared bounding box.
-	 */
-	private static class BoundSource implements RunnableSource {
-
-		private Sink sink;
-		private Bound bound;
-		private boolean publishBound;
-
-		public BoundSource(Bound bound, boolean publishBound) {
-			if (bound == null) {
-				throw new IllegalArgumentException("bound must not be null");
-			}
-			this.publishBound = publishBound;
-			this.bound = bound;
-		}
-
-		@Override
-		public void setSink(Sink sink) {
-			this.sink = sink;
-		}
-
-
-		@Override
-		public void run() {
-			if (publishBound) {
-				sink.process(new BoundContainer(bound));
-			}
-			sink.process(new NodeContainer(createNode()));
-			sink.complete();
-		}
-		
-		private Node createNode() {
-			double lon = (bound.getRight() - bound.getLeft()) / 2;
-			double lat = (bound.getTop() - bound.getBottom()) / 2;
-			return new Node(
-					new CommonEntityData(idGenerator.incrementAndGet(), 1, new Date(), OsmUser.NONE, 1),
-					lat, lon);
-		}
-	}
-}
diff --git a/set/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java b/set/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
deleted file mode 100644
index 737d24e..0000000
--- a/set/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.test.task.v0_6;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-/**
- * Mock object for inspecting the resulting entities after passing through a pipeline task.
- * 
- * @author Karl Newman
- */
-public class SinkEntityInspector implements Sink {
-
-	private List<EntityContainer> processedEntities;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SinkEntityInspector() {
-		processedEntities = new LinkedList<EntityContainer>();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Catch all passed entities and save them for later inspection.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		processedEntities.add(entityContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Shortcut method if you only care about the most recent EntityContainer.
-	 * 
-	 * @return the lastEntityContainer
-	 */
-	public EntityContainer getLastEntityContainer() {
-		if (processedEntities.isEmpty()) {
-			return null;
-		} else {
-			return processedEntities.get(processedEntities.size() - 1);
-		}
-	}
-
-
-	/**
-	 * Retrieve an Iterable of all the processed EntityContainers.
-	 * 
-	 * @return the processedEntities
-	 */
-	public Iterable<EntityContainer> getProcessedEntities() {
-		return Collections.unmodifiableList(processedEntities);
-	}
-
-}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..ae02cdf
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,20 @@
+include 'osmosis-apidb'
+include 'osmosis-areafilter'
+include 'build-support'
+include 'osmosis-core'
+include 'osmosis-dataset'
+include 'osmosis-extract'
+include 'osmosis-hstore-jdbc'
+include 'package'
+include 'osmosis-osm-binary'
+include 'osmosis-pbf'
+include 'osmosis-pbf2'
+include 'osmosis-pgsimple'
+include 'osmosis-pgsnapshot'
+include 'osmosis-replication'
+include 'osmosis-replication-http'
+include 'osmosis-set'
+include 'osmosis-tagfilter'
+include 'osmosis-tagtransform'
+include 'osmosis-testutil'
+include 'osmosis-xml'
diff --git a/tagfilter/.checkstyle b/tagfilter/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/tagfilter/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/tagfilter/.classpath b/tagfilter/.classpath
deleted file mode 100644
index 5028abb..0000000
--- a/tagfilter/.classpath
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/tagfilter/.gitignore b/tagfilter/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/tagfilter/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/tagfilter/.project b/tagfilter/.project
deleted file mode 100644
index 0d973c2..0000000
--- a/tagfilter/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-TagFilter</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/tagfilter/build.xml b/tagfilter/build.xml
deleted file mode 100644
index 7725bff..0000000
--- a/tagfilter/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.TagFilter" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/tagfilter/ivy.xml b/tagfilter/ivy.xml
deleted file mode 100644
index b62b757..0000000
--- a/tagfilter/ivy.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-tagfilter"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-xml" rev="${project.version}" conf="test->default" changing="true"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java
deleted file mode 100644
index 556264f..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyFilter.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.HashSet;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A class filtering everything but allowed nodes.
- *
- * @author Aurelien Jacobs
- */
-public class NodeKeyFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private HashSet<String> allowedKeys;
-
-	/**
-	 * Creates a new instance.
-	 *
-	 * @param keyList
-	 *            Comma-separated list of allowed key,
-	 *            e.g. "place,amenity"
-	 */
-	public NodeKeyFilter(String keyList) {
-
-		allowedKeys = new HashSet<String>();
-		String[] keys = keyList.split(",");
-		for (int i = 0; i < keys.length; i++) {
-			allowedKeys.add(keys[i]);
-		}
-
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		Node node = container.getEntity();
-
-		boolean matchesFilter = false;
-		for (Tag tag : node.getTags()) {
-			if (allowedKeys.contains(tag.getKey())) {
-				matchesFilter = true;
-				break;
-			}
-		}
-
-		if (matchesFilter) {
-			sink.process(container);
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java
deleted file mode 100644
index 179b6f2..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilter.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.HashSet;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A class filtering everything but allowed nodes.
- *
- * @author Aurelien Jacobs
- */
-public class NodeKeyValueFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private HashSet<String> allowedKeyValues;
-
-	/**
-	 * Creates a new instance.
-	 *
-	 * @param keyValueList
-	 *            Comma-separated list of allowed key-value combinations,
-	 *            e.g. "place.city,place.town"
-	 */
-	public NodeKeyValueFilter(String keyValueList) {
-
-		allowedKeyValues = new HashSet<String>();
-		String[] keyValues = keyValueList.split(",");
-		for (int i = 0; i < keyValues.length; i++) {
-			allowedKeyValues.add(keyValues[i]);
-		}
-
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		Node node = container.getEntity();
-
-		boolean matchesFilter = false;
-		for (Tag tag : node.getTags()) {
-			String keyValue = tag.getKey() + "." + tag.getValue();
-			if (allowedKeyValues.contains(keyValue)) {
-				matchesFilter = true;
-				break;
-			}
-		}
-
-		if (matchesFilter) {
-			sink.process(container);
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		// Do nothing.
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java
deleted file mode 100644
index 0ddb79c..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/NodeKeyValueFilterFactory.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
-
-
-/**
- * Extends the basic task manager factory functionality with used-node filter task
- * specific common methods.
- *
- * @author Brett Henderson
- * @author Christoph Sommer
- */
-public class NodeKeyValueFilterFactory extends TaskManagerFactory {
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		String keyValueList = getStringArgument(taskConfig, "keyValueList");
-		return new SinkSourceManager(
-			taskConfig.getId(),
-			new NodeKeyValueFilter(keyValueList),
-			taskConfig.getPipeArgs()
-		);
-	}
-
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java
deleted file mode 100644
index 346f128..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilter.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.Set;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-/**
- * A simple class to filter node, way, and relation entities by their tag keys and/or values.
- * 
- * @author Andrew Byrd
- */
-public class TagFilter implements SinkSource {
-    private Sink sink;
-    private Set<String> tagKeys;
-    private Map<String, Set<String>> tagKeyValues;
-    private Class<? extends EntityContainer> filterClass;
-    private boolean reject;
-    private boolean matchesEverything;
-    private static final Logger LOG = Logger.getLogger(TagFilter.class.getName());
-    
-
-    /**
-     * Creates a new instance. 
-     *
-     * @param filterMode
-     *          A 2-field dash-separated string specifying:
-     *          1. Whether the filter accepts or rejects entities
-     *          2. The entity type upon which it operates
-     *
-     * @param tagKeys
-     *          A Set of tag key Strings. The filter will match these tags irrespective of tag values.
-     *
-     * @param tagKeyValues
-     *          A map of tag key Strings to Sets of tag key values. These key-value pairs are checked 
-     *          against each entity's tags to determine whether or not it matches the filter.
-     */
-    public TagFilter(String filterMode, Set<String> tagKeys, Map<String, Set<String>> tagKeyValues) {        
-        String[] filterModeSplit = filterMode.toLowerCase().split("-");
-        if (filterModeSplit.length != 2) { 
-            throw new OsmosisRuntimeException(
-            "The TagFilter task's default parameter must consist of an action and an entity type separated by '-'."); 
-        }
-
-        String action = filterModeSplit[0];
-        if (action.equals("accept")) { 
-            reject = false; 
-        } else if (action.equals("reject")) { 
-            reject = true;  
-        } else { 
-            throw new OsmosisRuntimeException(
-            "The TagFilter action must be either 'accept' or 'reject'. '" + action + "' is not a supported mode."); 
-        }
-
-        String entity = filterModeSplit[1];
-        if  (entity.endsWith("s")) { 
-            entity = entity.substring(0, entity.length() - 1); 
-        }
-        if  (entity.equals("node")) { 
-            filterClass = NodeContainer.class; 
-        } else if (entity.equals("way")) { 
-            filterClass = WayContainer.class;      
-        } else if (entity.equals("relation")) { 
-            filterClass = RelationContainer.class; 
-        } else { 
-            throw new OsmosisRuntimeException(
-            "The TagFilter entity type must be one of 'node', 'way', or 'relation'. '" + entity 
-            + "' is not a supported entity type."); 
-        }
-        
-        matchesEverything = (tagKeys.size() == 0 && tagKeyValues.size() == 0);
-        this.tagKeys = tagKeys;
-        this.tagKeyValues = tagKeyValues;
-        
-        String logString = "New TagFilter ";
-        if (reject) {
-            logString += "rejects ";
-        } else {
-            logString += "accepts ";
-        }    
-        logString += filterClass;
-        if (matchesEverything) {
-            logString += " (no tag-based filtering).";
-        } else {
-            logString += " having tag keys " + tagKeys + " or tag key-value pairs " + tagKeyValues + ".";
-        }
-        LOG.finer(logString);
-    }
-
-
-    /**
-     * Checks whether the Entity in a container has tags that match this filter.
-     *
-     * @param container
-     *      The container holding the entity whose tags shall be examined.
-     */
-    private boolean matches(EntityContainer container) {
-        boolean matched = false;
-        for (Tag tag : container.getEntity().getTags()) {
-            String key = tag.getKey();
-            if (tagKeys.contains(key)) {
-                matched = true;
-                break; 
-            }
-            Set<String> values = tagKeyValues.get(key);
-            if ((values != null) && values.contains(tag.getValue())) {
-                matched = true;
-                break; 
-            } 
-        }
-        return matched;    
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public void process(EntityContainer container) {
-        if (filterClass.isInstance(container)) {
-            if (reject ^ (matchesEverything || matches(container))) {
-                sink.process(container);
-            }
-        } else {
-            sink.process(container);
-        }
-    }
-        
-
-    /**
-     * {@inheritDoc}
-     */
-    public void complete() {
-        sink.complete();
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public void release() {
-        sink.release();
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setSink(Sink sink) {
-        this.sink = sink;
-    }
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java
deleted file mode 100644
index 605064c..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagRemover.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.HashSet;
-import java.util.Iterator;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Filters a set of tags from all entities. This allows unwanted tags to be
- * removed from the data.
- * 
- * @author Jochen Topf
- * @author Brett Henderson
- */
-public class TagRemover implements SinkSource {
-	private Sink sink;
-	private HashSet<String> keysToDrop;
-	private String[] keyPrefixesToDrop;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param keyList
-	 *            Comma separated list of keys of tags to be removed.
-	 * @param keyPrefixList
-	 *            Comma separated list of key prefixes of tags to be removed.
-	 */
-	public TagRemover(String keyList, String keyPrefixList) {
-		keysToDrop = new HashSet<String>();
-		String[] keys = keyList.split(",");
-		for (int i = 0; i < keys.length; i++) {
-			keysToDrop.add(keys[i]);
-		}
-		keyPrefixesToDrop = keyPrefixList.split(",");
-		if (keyPrefixesToDrop[0] == "") {
-			keyPrefixesToDrop = new String[] {};
-		}
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		EntityContainer writeableContainer;
-		Entity entity;
-		
-		writeableContainer = entityContainer.getWriteableInstance();
-		entity = writeableContainer.getEntity();
-		
-		for (Iterator<Tag> i = entity.getTags().iterator(); i.hasNext();) {
-			Tag tag;
-			
-			tag = i.next();
-			
-			if (keysToDrop.contains(tag.getKey())) {
-				i.remove();
-			} else {
-				for (String prefix : keyPrefixesToDrop) {
-					if (tag.getKey().startsWith(prefix)) {
-						i.remove();
-					   	break;
-					}
-				}
-			}
-		}
-		
-		sink.process(writeableContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java
deleted file mode 100644
index b2cb22f..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilter.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.filter.common.IdTracker;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Restricts output of nodes to those that are used in ways and relations.
- * 
- * @author Brett Henderson
- * @author Karl Newman
- * @author Christoph Sommer 
- * @author Bartosz Fabianowski
- */
-public class UsedNodeFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private SimpleObjectStore<NodeContainer> allNodes;
-	private SimpleObjectStore<WayContainer> allWays;
-	private SimpleObjectStore<RelationContainer> allRelations;
-	private IdTracker requiredNodes;
-	
-	
-	/**
-	 * Creates a new instance.
-	 *
-	 * @param idTrackerType
-	 *            Defines the id tracker implementation to use.
-	 */
-	public UsedNodeFilter(IdTrackerType idTrackerType) {
-		allNodes = new SimpleObjectStore<NodeContainer>(
-				new SingleClassObjectSerializationFactory(NodeContainer.class), "afnd", true);
-		allWays = new SimpleObjectStore<WayContainer>(
-				new SingleClassObjectSerializationFactory(WayContainer.class), "afwy", true);
-		allRelations = new SimpleObjectStore<RelationContainer>(
-				new SingleClassObjectSerializationFactory(RelationContainer.class), "afrl", true);
-
-		requiredNodes = IdTrackerFactory.createInstance(idTrackerType);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		allNodes.add(container);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		Way way;
-
-		// mark all nodes as required		
-		way = container.getEntity();
-		for (WayNode nodeReference : way.getWayNodes()) {
-			long nodeId = nodeReference.getNodeId();
-			requiredNodes.set(nodeId);
-		}
-
-		allWays.add(container);
-
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		Relation relation;
-
-		// mark all nodes as required
-		relation = container.getEntity();
-		for (RelationMember memberReference : relation.getMembers()) {
-			if (memberReference.getMemberType() == EntityType.Node) {
-				long nodeId = memberReference.getMemberId();
-				requiredNodes.set(nodeId);
-			}
-		}
-
-		allRelations.add(container);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-
-		// send on all required nodes
-		ReleasableIterator<NodeContainer> nodeIterator = allNodes.iterate();
-		while (nodeIterator.hasNext()) {
-			NodeContainer nodeContainer = nodeIterator.next();
-			long nodeId = nodeContainer.getEntity().getId();
-			if (!requiredNodes.get(nodeId)) {
-				continue;
-			}
-			sink.process(nodeContainer);
-		}
-		nodeIterator.release();
-		nodeIterator = null;
-
-		// send on all ways
-		ReleasableIterator<WayContainer> wayIterator = allWays.iterate();
-		while (wayIterator.hasNext()) {
-			sink.process(wayIterator.next());
-		}
-		wayIterator.release();
-		wayIterator = null;
-
-		// send on all relations
-		ReleasableIterator<RelationContainer> relationIterator = allRelations.iterate();
-		while (relationIterator.hasNext()) {
-			sink.process(relationIterator.next());
-		}
-		relationIterator.release();
-		relationIterator = null;
-
-		// done
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		if (allNodes != null) {
-			allNodes.release();
-		}
-		if (allWays != null) {
-			allWays.release();			
-		}
-		if (allRelations != null) {
-			allRelations.release();
-		}
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java
deleted file mode 100644
index ea7e042..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedNodeFilterFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
-
-
-/**
- * Extends the basic task manager factory functionality with used-node filter task
- * specific common methods.
- * 
- * @author Brett Henderson
- * @author Christoph Sommer
- */
-public class UsedNodeFilterFactory extends TaskManagerFactory {
-	private static final String ARG_ID_TRACKER_TYPE = "idTrackerType";
-	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.IdList;
-	
-	
-	/**
-	 * Utility method that returns the IdTrackerType to use for a given taskConfig.
-	 * 
-	 * @param taskConfig
-	 *            Contains all information required to instantiate and configure
-	 *            the task.
-	 * @return The entity identifier tracker type.
-	 */
-	protected IdTrackerType getIdTrackerType(
-			TaskConfiguration taskConfig) {
-		if (doesArgumentExist(taskConfig, ARG_ID_TRACKER_TYPE)) {
-			String idTrackerType;
-			
-			idTrackerType = getStringArgument(taskConfig, ARG_ID_TRACKER_TYPE);
-			
-			try {
-				return IdTrackerType.valueOf(idTrackerType);
-			} catch (IllegalArgumentException e) {
-				throw new OsmosisRuntimeException(
-					"Argument " + ARG_ID_TRACKER_TYPE + " for task " + taskConfig.getId()
-					+ " must contain a valid id tracker type.", e);
-			}
-			
-		} else {
-			return DEFAULT_ID_TRACKER_TYPE;
-		}
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-
-		IdTrackerType idTrackerType = getIdTrackerType(taskConfig);
-
-		return new SinkSourceManager(
-			taskConfig.getId(),
-			new UsedNodeFilter(idTrackerType),
-			taskConfig.getPipeArgs()
-		);
-	}
-
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java
deleted file mode 100644
index 1f31887..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilter.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.filter.common.IdTracker;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerFactory;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
-import org.openstreetmap.osmosis.core.store.SimpleObjectStore;
-import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * Restricts output of ways to those that are used in relations.
- * 
- * @author Brett Henderson
- * @author Karl Newman
- * @author Christoph Sommer 
- * @author Bartosz Fabianowski
- */
-public class UsedWayFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private SimpleObjectStore<NodeContainer> allNodes;
-	private SimpleObjectStore<WayContainer> allWays;
-	private SimpleObjectStore<RelationContainer> allRelations;
-	private IdTracker requiredWays;
-	
-	
-	/**
-	 * Creates a new instance.
-	 *
-	 * @param idTrackerType
-	 *            Defines the id tracker implementation to use.
-	 */
-	public UsedWayFilter(IdTrackerType idTrackerType) {
-		allNodes = new SimpleObjectStore<NodeContainer>(
-				new SingleClassObjectSerializationFactory(NodeContainer.class), "afnd", true);
-		allWays = new SimpleObjectStore<WayContainer>(
-				new SingleClassObjectSerializationFactory(WayContainer.class), "afwy", true);
-		allRelations = new SimpleObjectStore<RelationContainer>(
-				new SingleClassObjectSerializationFactory(RelationContainer.class), "afrl", true);
-
-		requiredWays = IdTrackerFactory.createInstance(idTrackerType);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		allNodes.add(container);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		allWays.add(container);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		Relation relation;
-
-		// mark all nodes as required
-		relation = container.getEntity();
-		for (RelationMember memberReference : relation.getMembers()) {
-			if (memberReference.getMemberType() == EntityType.Way) {
-				long wayId = memberReference.getMemberId();
-				requiredWays.set(wayId);
-			}
-		}
-
-		allRelations.add(container);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-    // send on all nodes
-    ReleasableIterator<NodeContainer> nodeIterator = allNodes.iterate();
-    while (nodeIterator.hasNext()) {
-      sink.process(nodeIterator.next());
-    }
-    nodeIterator.release();
-    nodeIterator = null;
-
-		// send on all required ways
-		ReleasableIterator<WayContainer> wayIterator = allWays.iterate();
-		while (wayIterator.hasNext()) {
-			WayContainer wayContainer = wayIterator.next();
-			long wayId = wayContainer.getEntity().getId();
-			if (!requiredWays.get(wayId)) {
-				continue;
-			}
-			sink.process(wayContainer);
-		}
-		wayIterator.release();
-		wayIterator = null;
-
-		// send on all relations
-		ReleasableIterator<RelationContainer> relationIterator = allRelations.iterate();
-		while (relationIterator.hasNext()) {
-			sink.process(relationIterator.next());
-		}
-		relationIterator.release();
-		relationIterator = null;
-
-		// done
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		if (allNodes != null) {
-			allNodes.release();
-		}
-		if (allWays != null) {
-			allWays.release();			
-		}
-		if (allRelations != null) {
-			allRelations.release();
-		}
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java
deleted file mode 100644
index f357d96..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/UsedWayFilterFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.filter.common.IdTrackerType;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
-
-/**
- * Extends the basic task manager factory functionality with used-way filter task
- * specific common methods.
- * 
- * @author Brett Henderson
- * @author Christoph Sommer
- * @author Bartosz Fabianowski
- */
-public class UsedWayFilterFactory extends TaskManagerFactory {
-	private static final String ARG_ID_TRACKER_TYPE = "idTrackerType";
-	private static final IdTrackerType DEFAULT_ID_TRACKER_TYPE = IdTrackerType.IdList;
-	
-	
-	/**
-	 * Utility method that returns the IdTrackerType to use for a given taskConfig.
-	 * 
-	 * @param taskConfig
-	 *            Contains all information required to instantiate and configure
-	 *            the task.
-	 * @return The entity identifier tracker type.
-	 */
-	protected IdTrackerType getIdTrackerType(
-			TaskConfiguration taskConfig) {
-		if (doesArgumentExist(taskConfig, ARG_ID_TRACKER_TYPE)) {
-			String idTrackerType;
-			
-			idTrackerType = getStringArgument(taskConfig, ARG_ID_TRACKER_TYPE);
-			
-			try {
-				return IdTrackerType.valueOf(idTrackerType);
-			} catch (IllegalArgumentException e) {
-				throw new OsmosisRuntimeException(
-					"Argument " + ARG_ID_TRACKER_TYPE + " for task " + taskConfig.getId()
-					+ " must contain a valid id tracker type.", e);
-			}
-			
-		} else {
-			return DEFAULT_ID_TRACKER_TYPE;
-		}
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-
-		IdTrackerType idTrackerType = getIdTrackerType(taskConfig);
-
-		return new SinkSourceManager(
-			taskConfig.getId(),
-			new UsedWayFilter(idTrackerType),
-			taskConfig.getPipeArgs()
-		);
-	}
-
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java
deleted file mode 100644
index 3014db4..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyFilter.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.HashSet;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A simple class to filter way entities by their tag keys.
- * Based on work by Brett Henderson, Karl Newman, Christoph Sommer, Aurelien Jacobs
- * 
- * @author Andrew Byrd
- */
-public class WayKeyFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private HashSet<String> allowedKeys;
-
-	/**
-	 * Creates a new instance.
-	 *
-	 * @param keyList
-	 *            Comma-separated list of allowed keys,
-	 *            e.g. "highway,place"
-	 */
-	public WayKeyFilter(String keyList) {
-
-		allowedKeys = new HashSet<String>();
-		String[] keys = keyList.split(",");
-		for (int i = 0; i < keys.length; i++) {
-			allowedKeys.add(keys[i]);
-		}
-
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		sink.process(container);
-	}
-	
-    
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		Way way = container.getEntity();
-
-		boolean matchesFilter = false;
-		for (Tag tag : way.getTags()) {
-			if (allowedKeys.contains(tag.getKey())) {
-				matchesFilter = true;
-				break;
-			}
-		}
-
-		if (matchesFilter) {
-			sink.process(container);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		sink.process(container);
-	}
-    
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java
deleted file mode 100644
index d330d90..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilter.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import java.util.HashSet;
-
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.task.v0_6.SinkSource;
-
-
-/**
- * A simple class to filter way entities by their tags.
- * 
- * @author Brett Henderson
- * @author Karl Newman
- * @author Christoph Sommer 
- */
-public class WayKeyValueFilter implements SinkSource, EntityProcessor {
-	private Sink sink;
-	private HashSet<String> allowedKeyValues;
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param keyValueList
-	 *            Comma-separated list of allowed key-value combinations,
-	 *            e.g. "highway.motorway,highway.motorway_link" 
-	 */
-	public WayKeyValueFilter(String keyValueList) {
-
-		allowedKeyValues = new HashSet<String>();
-		String[] keyValues = keyValueList.split(",");
-		for (int i = 0; i < keyValues.length; i++) {
-			allowedKeyValues.add(keyValues[i]);
-		}
-
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		// Ask the entity container to invoke the appropriate processing method
-		// for the entity type.
-		entityContainer.process(this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(BoundContainer boundContainer) {
-		// By default, pass it on unchanged
-		sink.process(boundContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(NodeContainer container) {
-		sink.process(container);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(WayContainer container) {
-		Way way = container.getEntity();
-
-		boolean matchesFilter = false;
-		for (Tag tag : way.getTags()) {
-			String keyValue = tag.getKey() + "." + tag.getValue();
-			if (allowedKeyValues.contains(keyValue)) {
-				matchesFilter = true;
-				break;
-			}
-		}
-
-		if (matchesFilter) {
-			sink.process(container);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(RelationContainer container) {
-		sink.process(container);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void complete() {
-		sink.complete();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void release() {
-		sink.release();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-}
diff --git a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java b/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java
deleted file mode 100644
index f6f921a..0000000
--- a/tagfilter/src/main/java/org/openstreetmap/osmosis/tagfilter/v0_6/WayKeyValueFilterFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManagerFactory;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkSourceManager;
-
-
-/**
- * Extends the basic task manager factory functionality with used-node filter task
- * specific common methods.
- * 
- * @author Brett Henderson
- * @author Christoph Sommer
- */
-public class WayKeyValueFilterFactory extends TaskManagerFactory {
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		String keyValueList = getStringArgument(
-				taskConfig, "keyValueList", "highway.motorway,highway.motorway_link,highway.trunk,highway.trunk_link");
-		return new SinkSourceManager(
-			taskConfig.getId(),
-			new WayKeyValueFilter(keyValueList),
-			taskConfig.getPipeArgs()
-		);
-	}
-
-}
diff --git a/tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java b/tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java
deleted file mode 100644
index 27c56fd..0000000
--- a/tagfilter/src/test/java/org/openstreetmap/osmosis/tagfilter/v0_6/TagFilterTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.tagfilter.v0_6;
-
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-/**
- * Tests the TagFilter implementation.
- * 
- * @author Andrew Byrd
- * Based on tests written by Karl Newman
- */
-public class TagFilterTest {
-
-	private SinkEntityInspector entityInspector;
-	private TagFilter tagFilter;
-
-	private Node amenityNode;
-	private NodeContainer amenityNodeContainer;
-
-	private Node taglessNode;
-	private NodeContainer taglessNodeContainer;
-
-	private Way motorwayWay;
-	private WayContainer motorwayWayContainer;
-
-	private Way residentialWay;
-	private WayContainer residentialWayContainer;
-
-	private Relation testRelation;
-	private RelationContainer testRelationContainer;
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		OsmUser user;
-		List<Tag> tags;
-		
-		user = new OsmUser(12, "OsmosisTest");
-			
-		tags = Arrays.asList(new Tag("amenity", "bank"), new Tag("Akey", "Avalue"));
-		amenityNode = new Node(new CommonEntityData(1101, 0, new Date(), user, 0, tags), 1, 2);
-		amenityNodeContainer = new NodeContainer(amenityNode);
-
-		tags = new ArrayList<Tag>();
-		taglessNode = new Node(new CommonEntityData(1102, 0, new Date(), user, 0, tags), 3, 4);
-		taglessNodeContainer = new NodeContainer(taglessNode);
-
-		tags = Arrays.asList(new Tag("highway", "motorway"), new Tag("Bkey", "Bvalue"));
-		motorwayWay = new Way(new CommonEntityData(2201, 0, new Date(), user, 0, tags), new ArrayList<WayNode>());
-		motorwayWayContainer = new WayContainer(motorwayWay);
-
-		tags = Arrays.asList(new Tag("highway", "residential"), new Tag("Ckey", "Cvalue"));
-		residentialWay =
-			new Way(new CommonEntityData(2202, 0, new Date(), user, 0, tags), new ArrayList<WayNode>());
-		residentialWayContainer = new WayContainer(residentialWay);
-
-		tags = Arrays.asList(new Tag("Dkey", "Dvalue"));
-		testRelation =
-			new Relation(new CommonEntityData(3301, 0, new Date(), user, 0, tags), new ArrayList<RelationMember>());
-		testRelationContainer = new RelationContainer(testRelation);
-	}
-
-
-	/**
-	 * Performs post-test activities.
-	 */
-	@After
-	public void tearDown() {
-		// nothing to do here.
-	}
-
-
-	/**
-	 * Test passing a node which matches the filter.
-	 */
-	@Test
-	public final void testAcceptNode() {
-		Set<String> keys = new HashSet<String>(Arrays.asList("amenity"));
-		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
-		keyValues.put("key", new HashSet<String>(Arrays.asList("valone", "valtwo")));
-		tagFilter = new TagFilter("accept-nodes", keys, keyValues);
-		entityInspector = new SinkEntityInspector();
-		tagFilter.setSink(entityInspector);
-
-		tagFilter.process(amenityNodeContainer);
-		tagFilter.process(taglessNodeContainer);
-		tagFilter.process(residentialWayContainer);
-		tagFilter.complete();
-
-		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
-		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
-		tagFilter.release();
-	}
-
-
-	/**
-	 * Test rejecting a way which matches the filter.
-	 */
-	@Test
-	public final void testRejectWay() {
-		Set<String> keys = new HashSet<String>();
-		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
-		keyValues.put("highway", new HashSet<String>(Arrays.asList("motorway", "motorway_link")));
-		tagFilter = new TagFilter("reject-ways", keys, keyValues);
-		entityInspector = new SinkEntityInspector();
-		tagFilter.setSink(entityInspector);
-
-		tagFilter.process(amenityNodeContainer);
-		tagFilter.process(residentialWayContainer);
-		tagFilter.process(motorwayWayContainer);
-		tagFilter.complete();
-
-		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
-		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
-		tagFilter.release();
-	}
-
-
-	/**
-	 * Test rejecting a relation without tag-based filtering.
-	 */
-	@Test
-	public final void testRejectRelation() {
-		Set<String> keys = new HashSet<String>();
-		Map<String, Set<String>> keyValues = new HashMap<String, Set<String>>();
-		tagFilter = new TagFilter("reject-relations", keys, keyValues);
-		entityInspector = new SinkEntityInspector();
-		tagFilter.setSink(entityInspector);
-
-		tagFilter.process(amenityNodeContainer);
-		tagFilter.process(residentialWayContainer);
-		tagFilter.process(testRelationContainer);
-		tagFilter.complete();
-
-		List<EntityContainer> expectedResult = Arrays.asList(amenityNodeContainer, residentialWayContainer);
-		assertTrue(entityInspector.getProcessedEntities().equals(expectedResult));
-		tagFilter.release();
-	}
-
-}
diff --git a/tagfilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java b/tagfilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
deleted file mode 100644
index 737d24e..0000000
--- a/tagfilter/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.test.task.v0_6;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-/**
- * Mock object for inspecting the resulting entities after passing through a pipeline task.
- * 
- * @author Karl Newman
- */
-public class SinkEntityInspector implements Sink {
-
-	private List<EntityContainer> processedEntities;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SinkEntityInspector() {
-		processedEntities = new LinkedList<EntityContainer>();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Catch all passed entities and save them for later inspection.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		processedEntities.add(entityContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Shortcut method if you only care about the most recent EntityContainer.
-	 * 
-	 * @return the lastEntityContainer
-	 */
-	public EntityContainer getLastEntityContainer() {
-		if (processedEntities.isEmpty()) {
-			return null;
-		} else {
-			return processedEntities.get(processedEntities.size() - 1);
-		}
-	}
-
-
-	/**
-	 * Retrieve an Iterable of all the processed EntityContainers.
-	 * 
-	 * @return the processedEntities
-	 */
-	public Iterable<EntityContainer> getProcessedEntities() {
-		return Collections.unmodifiableList(processedEntities);
-	}
-
-}
diff --git a/tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm b/tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm
deleted file mode 100644
index b232c5f..0000000
--- a/tagfilter/src/test/resources/data/template/v0_6/tag-remove-expected.osm
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
-    <tag k="other_tag_1" v="other tag before"/>
-    <tag k="other_tag_2" v="other tag after"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" lat="-3" lon="-4"/>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" lat="-5" lon="-6">
-    <tag k="other_tag_1b" v="other tag before"/>
-    <tag k="other_tag_2b" v="other tag after"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" lat="-7" lon="-8"/>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="other_tag_1c" v="other tag before"/>
-    <tag k="other_tag_1c" v="other tag after"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
-    <member type="node" ref="3" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="other_tag_1d" v="other tag before"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm b/tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm
deleted file mode 100644
index c8a0a4c..0000000
--- a/tagfilter/src/test/resources/data/template/v0_6/tag-remove-snapshot.osm
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<osm version="0.6" generator="Osmosis %VERSION%">
-  <bound box="-90.00000,-180.00000,90.00000,180.00000" origin="Osmosis %VERSION%"/>
-  <node id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10" lat="-1" lon="-2">
-    <tag k="other_tag_1" v="other tag before"/>
-    <tag k="created_by" v="Me1"/>
-    <tag k="other_tag_2" v="other tag after"/>
-  </node>
-  <node id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20" lat="-3" lon="-4">
-    <tag k="created_by" v="Me2"/>
-  </node>
-  <node id="3" version="12" timestamp="2008-01-02T06:07:08Z" uid="30" user="user30" lat="-5" lon="-6">
-    <tag k="other_tag_1b" v="other tag before"/>
-    <tag k="created_by" v="Me3"/>
-    <tag k="other_tag_2b" v="other tag after"/>
-  </node>
-  <node id="4" version="13" timestamp="2008-01-02T09:10:11Z" uid="40" user="user40" lat="-7" lon="-8">
-    <tag k="created_by" v="Me4"/>
-  </node>
-  <way id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
-    <nd ref="1"/>
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <tag k="created_by" v="Me1"/>
-  </way>
-  <way id="2" version="11" timestamp="2008-01-02T03:04:05Z" uid="20" user="user20">
-    <nd ref="2"/>
-    <nd ref="3"/>
-    <nd ref="4"/>
-    <tag k="other_tag_1c" v="other tag before"/>
-    <tag k="created_by" v="Me1"/>
-    <tag k="other_tag_1c" v="other tag after"/>
-  </way>
-  <relation id="1" version="10" timestamp="2008-01-02T03:04:05Z" uid="10" user="user10">
-    <member type="node" ref="3" role="noderole"/>
-    <member type="way" ref="1" role="wayrole1"/>
-    <member type="way" ref="2" role="wayrole2"/>
-    <tag k="other_tag_1d" v="other tag before"/>
-    <tag k="type" v="myrelation"/>
-  </relation>
-</osm>
diff --git a/testutil/.checkstyle b/testutil/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/testutil/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/testutil/.classpath b/testutil/.classpath
deleted file mode 100644
index 2ea38d9..0000000
--- a/testutil/.classpath
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/testutil/.gitignore b/testutil/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/testutil/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/testutil/.project b/testutil/.project
deleted file mode 100644
index 8d6c82e..0000000
--- a/testutil/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-TestUtil</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/testutil/build.xml b/testutil/build.xml
deleted file mode 100644
index f044d24..0000000
--- a/testutil/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.TestUtil" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/testutil/ivy.xml b/testutil/ivy.xml
deleted file mode 100644
index d8a8868..0000000
--- a/testutil/ivy.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-testutil"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	<dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="compile->default"/>
-    	
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/xml/.checkstyle b/xml/.checkstyle
deleted file mode 100644
index 4720ecd..0000000
--- a/xml/.checkstyle
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
-  <local-check-config name="Osmosis Checks" location="/Osmosis-BuildSupport/checkstyle.xml" type="project" description="">
-    <additional-data name="protect-config-file" value="false"/>
-  </local-check-config>
-  <fileset name="all" enabled="true" check-config-name="Osmosis Checks" local="true">
-    <file-match-pattern match-pattern="." include-pattern="true"/>
-  </fileset>
-</fileset-config>
diff --git a/xml/.classpath b/xml/.classpath
deleted file mode 100644
index e44941b..0000000
--- a/xml/.classpath
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-Core"/>
-	<classpathentry kind="lib" path="lib/test/antlr-2.7.7.jar"/>
-	<classpathentry kind="lib" path="lib/test/checkstyle-5.4.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-beanutils-core-1.8.3.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-cli-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-codec-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-compress-1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/google-collections-1.0.jar"/>
-	<classpathentry kind="lib" path="lib/test/hamcrest-core-1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/jpf-1.5.jar"/>
-	<classpathentry kind="lib" path="lib/test/junit-4.10.jar"/>
-	<classpathentry kind="lib" path="lib/test/stax2-api-3.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/test/woodstox-core-lgpl-4.1.2.jar"/>
-	<classpathentry kind="lib" path="lib/test/xercesImpl-2.9.1.jar"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/Osmosis-TestUtil"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
diff --git a/xml/.gitignore b/xml/.gitignore
deleted file mode 100644
index cb906e1..0000000
--- a/xml/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-/build
-/distrib
-/eclipse
-/lib
-/report
diff --git a/xml/.project b/xml/.project
deleted file mode 100644
index 7bab72f..0000000
--- a/xml/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Osmosis-Xml</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-		<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
-	</natures>
-</projectDescription>
diff --git a/xml/build.xml b/xml/build.xml
deleted file mode 100644
index 0b5f429..0000000
--- a/xml/build.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<project name="Osmosis.Xml" default="all" basedir=".">
-	
-	<!-- Include common java build. -->
-	<property name="build-support.dir" location="../build-support"/>
-	<import file="${build-support.dir}/script/build-java.xml"/>
-</project>
diff --git a/xml/ivy.xml b/xml/ivy.xml
deleted file mode 100644
index 0a0c261..0000000
--- a/xml/ivy.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<ivy-module version="2.0"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
-	
-    <info organisation="org.openstreetmap.osmosis" module="osmosis-xml"/>
-    
-    <configurations>
-		<conf name="default" visibility="public" description="runtime dependencies and master artifact can be used with this conf" extends="runtime,master"/>
-		<conf name="master" visibility="public" description="contains only the artifact published by this module itself, with no transitive dependencies"/>
-		<conf name="compile" visibility="public" description="this is the default scope, used if none is specified. Compile dependencies are available in all classpaths."/>
-		<conf name="provided" visibility="public" description="this is much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive."/>
-		<conf name="runtime" visibility="public" description="this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath." extends="compile"/>
-		<conf name="test" visibility="private" description="this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases." extends="runtime"/>
-		<conf name="system" visibility="public" description="this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository."/>
-		<conf name="sources" visibility="public" description="this configuration contains the source artifact of this module, if any."/>
-		<conf name="javadoc" visibility="public" description="this configuration contains the javadoc artifact of this module, if any."/>
-		<conf name="optional" visibility="public" description="contains all optional dependencies"/>
-		<conf name="distribution" visibility="public" description="contains distribution packages"/>
-    </configurations>
-    
-    <publications>
-    	<artifact name="${project.name}" type="jar" ext="jar" conf="master"/>
-    </publications>
-    
-    <dependencies>
-    	<dependency org="org.openstreetmap.osmosis" name="osmosis-core" rev="${project.version}" conf="compile->default" changing="true"/>
-    	
-    	<dependency org="commons-codec" name="commons-codec" rev="${dependency.version.commons-codec}" conf="compile->default"/>
-        
-        <dependency org="org.openstreetmap.osmosis" name="osmosis-testutil" rev="${project.version}" conf="test->default" changing="true"/>
-        <dependency org="junit" name="junit" rev="${dependency.version.junit}" conf="test->default"/>
-    	<dependency org="com.puppycrawl.tools" name="checkstyle" rev="${dependency.version.checkstyle}" conf="test->default"/>
-    </dependencies>
-</ivy-module>
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java
deleted file mode 100644
index a75f519..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/common/BaseXmlWriter.java
+++ /dev/null
@@ -1,218 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.common;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * An OSM data sink for storing all data to an xml file.
- * 
- * @author Brett Henderson
- */
-public abstract class BaseXmlWriter {
-	
-	private static Logger log = Logger.getLogger(BaseXmlWriter.class.getName());
-	
-	
-	private boolean writerProvided;
-	private File file;
-	private boolean initialized;
-	private BufferedWriter writer;
-	private CompressionMethod compressionMethod;
-	
-	
-	/**
-	 * Creates a new instance to write to the provided writer.
-	 * 
-	 * @param writer The writer to receive data.  This writer will not be closed on completion.
-	 */
-	public BaseXmlWriter(BufferedWriter writer) {
-		this.writer = writer;
-		
-		writerProvided = true;
-	}
-	
-	
-	/**
-	 * Creates a new instance to write to the specified file.
-	 * 
-	 * @param file
-	 *            The file to write.
-	 * @param compressionMethod
-	 *            Specifies the compression method to employ.
-	 */
-	public BaseXmlWriter(File file, CompressionMethod compressionMethod) {
-		this.file = file;
-		this.compressionMethod = compressionMethod;
-		
-		writerProvided = false;
-	}
-	
-	
-	/**
-	 * Sets the writer on the element writer used for this implementation.
-	 * 
-	 * @param resultWriter
-	 *            The writer receiving xml data.
-	 */
-	protected abstract void setWriterOnElementWriter(BufferedWriter resultWriter);
-	
-	
-	/**
-	 * Calls the begin method of the element writer used for this implementation.
-	 */
-	protected abstract void beginElementWriter();
-	
-	
-	/**
-	 * Calls the end method of the element writer used for this implementation.
-	 */
-	protected abstract void endElementWriter();
-	
-	
-	/**
-	 * Writes data to the output file.
-	 * 
-	 * @param data
-	 *            The data to be written.
-	 */
-	private void write(String data) {
-		try {
-			writer.write(data);
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to write data.", e);
-		}
-	}
-	
-	
-	/**
-	 * Writes a new line in the output file.
-	 */
-	private void writeNewLine() {
-		try {
-			writer.newLine();
-			
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to write data.", e);
-		}
-	}
-	
-	
-	/**
-	 * Initialises the output file for writing. This must be called by
-	 * sub-classes before any writing is performed. This method may be called
-	 * multiple times without adverse affect allowing sub-classes to invoke it
-	 * every time they perform processing.
-	 */
-	protected void initialize() {
-		if (!initialized) {
-			if (!writerProvided) {
-				OutputStream outStream = null;
-				
-				try {
-					OutputStreamWriter outStreamWriter;
-					
-					// make "-" an alias for /dev/stdout
-					if (file.getName().equals("-")) {
-						outStream = System.out;
-					} else {
-						outStream = new FileOutputStream(file);
-					}
-					
-					outStream =
-						new CompressionActivator(compressionMethod).createCompressionOutputStream(outStream);
-					
-					outStreamWriter = new OutputStreamWriter(outStream, "UTF-8");
-					
-					writer = new BufferedWriter(outStreamWriter);
-					
-					outStream = null;
-					
-				} catch (IOException e) {
-					throw new OsmosisRuntimeException("Unable to open file for writing.", e);
-				} finally {
-					if (outStream != null) {
-						try {
-							outStream.close();
-						} catch (Exception e) {
-							log.log(Level.SEVERE, "Unable to close output stream.", e);
-						}
-						outStream = null;
-					}
-				}
-			}
-			
-			setWriterOnElementWriter(writer);
-			
-			initialized = true;
-			
-			write("<?xml version='1.0' encoding='UTF-8'?>");
-			writeNewLine();
-			
-			beginElementWriter();
-		}
-	}
-	
-	
-	/**
-	 * Flushes all changes to file.
-	 */
-	public void complete() {
-		// We need to call this here so that we create empty files if no records
-		// are available.
-		initialize();
-		
-		try {
-			endElementWriter();
-			
-			if (!writerProvided) {
-				try {
-					if (writer != null) {
-						writer.close();
-					}
-					
-				} catch (IOException e) {
-					throw new OsmosisRuntimeException("Unable to complete writing to the xml stream.", e);
-				} finally {
-					writer = null;
-				}
-			}
-		} finally {
-			initialized = false;
-		}
-	}
-	
-	
-	/**
-	 * Cleans up any open file handles.
-	 */
-	public void release() {
-		try {
-			if (!writerProvided) {
-				try {
-					try {
-						if (writer != null) {
-							writer.close();
-						}
-					} catch (IOException e) {
-						log.log(Level.SEVERE, "Unable to close writer.", e);
-					}
-				} finally {
-					writer = null;
-				}
-			}
-		} finally {
-			initialized = false;
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java
deleted file mode 100644
index 9446061..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/FastXmlReader.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamReader;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.CompressionActivator;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.impl.FastXmlParser;
-
-
-/**
- * An OSM data source reading from an xml file. The entire contents of the file
- * are read.
- * 
- * @author Jiri Clement
- * @author Brett Henderson
- */
-public class FastXmlReader implements RunnableSource {
-		
-		private static Logger log = Logger.getLogger(FastXmlReader.class.getName());
-		
-		private Sink sink;
-		private final File file;
-		private final boolean enableDateParsing;
-		private final CompressionMethod compressionMethod;
-		
-		
-		/**
-		 * Creates a new instance.
-		 * 
-		 * @param file
-		 *            The file to read.
-		 * @param enableDateParsing
-		 *            If true, dates will be parsed from xml data, else the current
-		 *            date will be used thus saving parsing time.
-		 * @param compressionMethod
-		 *            Specifies the compression method to employ.
-		 */
-		public FastXmlReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
-			this.file = file;
-			this.enableDateParsing = enableDateParsing;
-			this.compressionMethod = compressionMethod;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void setSink(Sink sink) {
-			this.sink = sink;
-		}
-		
-				
-		
-		/**
-		 * Reads all data from the file and send it to the sink.
-		 */
-		public void run() {
-			InputStream inputStream = null;
-			FastXmlParser parser = null;
-			
-			try {
-				
-				// make "-" an alias for /dev/stdin
-				if (file.getName().equals("-")) {
-					inputStream = System.in;
-				} else {
-					inputStream = new FileInputStream(file);
-				}
-				
-				
-				inputStream =
-					new CompressionActivator(compressionMethod).
-						createCompressionInputStream(inputStream);
-				
-		        XMLInputFactory factory = XMLInputFactory.newInstance();
-		        factory.setProperty(XMLInputFactory.IS_COALESCING, false);
-		        factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
-		        factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
-		        XMLStreamReader xpp = factory.createXMLStreamReader(inputStream);
-				
-				parser = new FastXmlParser(sink, xpp, enableDateParsing);
-				parser.readOsm();
-				
-				sink.complete();
-				
-			} catch (Exception e) {
-				throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
-			} finally {
-				sink.release();
-				
-				if (inputStream != null) {
-					try {
-						inputStream.close();
-					} catch (IOException e) {
-						log.log(Level.SEVERE, "Unable to close input stream.", e);
-					}
-					inputStream = null;
-				}
-			}
-		}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java
deleted file mode 100644
index 120a87b..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReader.java
+++ /dev/null
@@ -1,136 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
-import org.openstreetmap.osmosis.xml.common.CompressionActivator;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeHandler;
-
-
-/**
- * A change source reading from an xml file. The entire contents of the file
- * are read.
- * 
- * @author Brett Henderson
- */
-public class XmlChangeReader implements RunnableChangeSource {
-	
-	private static Logger log = Logger.getLogger(XmlReader.class.getName());
-	
-	private ChangeSink changeSink;
-	private File file;
-	private boolean enableDateParsing;
-	private CompressionMethod compressionMethod;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to read.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current
-	 *            date will be used thus saving parsing time.
-	 * @param compressionMethod
-	 *            Specifies the compression method to employ.
-	 */
-	public XmlChangeReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
-		this.file = file;
-		this.enableDateParsing = enableDateParsing;
-		this.compressionMethod = compressionMethod;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setChangeSink(ChangeSink changeSink) {
-		this.changeSink = changeSink;
-	}
-	
-	
-	/**
-	 * Creates a new SAX parser.
-	 * 
-	 * @return The newly created SAX parser.
-	 */
-	private SAXParser createParser() {
-		try {
-			return SAXParserFactory.newInstance().newSAXParser();
-			
-		} catch (ParserConfigurationException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		}
-	}
-	
-	
-	/**
-	 * Reads all data from the file and send it to the sink.
-	 */
-	public void run() {
-		InputStream inputStream = null;
-		
-		try {
-			SAXParser parser;
-			
-			// make "-" an alias for /dev/stdin
-			if (file.getName().equals("-")) {
-				inputStream = System.in;
-			} else {
-				inputStream = new FileInputStream(file);
-			}
-			
-			inputStream =
-				new CompressionActivator(compressionMethod).
-					createCompressionInputStream(inputStream);
-			
-			parser = createParser();
-			
-			parser.parse(inputStream, new OsmChangeHandler(changeSink, enableDateParsing));
-			
-			changeSink.complete();
-			
-		} catch (SAXParseException e) {
-			throw new OsmosisRuntimeException(
-				"Unable to parse xml file " + file
-				+ ".  publicId=(" + e.getPublicId()
-				+ "), systemId=(" + e.getSystemId()
-				+ "), lineNumber=" + e.getLineNumber()
-				+ ", columnNumber=" + e.getColumnNumber() + ".",
-				e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Unable to parse XML.", e);
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
-		} finally {
-			changeSink.release();
-			
-			if (inputStream != null) {
-				try {
-					inputStream.close();
-				} catch (IOException e) {
-					log.log(Level.SEVERE, "Unable to close input stream.", e);
-				}
-				inputStream = null;
-			}
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java
deleted file mode 100644
index 7809e99..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeUploader.java
+++ /dev/null
@@ -1,308 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.commons.codec.binary.Base64;
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeWriter;
-
-
-/**
- * An OSM change sink for uploading all data to an OpenStreetMap server.
- *
- * @author Marcus Wolschon Marcus at Wolscon.biz
- */
-public class XmlChangeUploader implements ChangeSink {
-
-    /**
-     * Our logger for debug and error -output.
-     */
-    private static final Logger LOG = Logger.getLogger(
-            XmlChangeUploader.class.getName());
-
-    /**
-     * Default-value for {@link DownloadingDataSet#APIBASEURSETTING}.
-     */
-    private static final String DEFAULTAPIBASEURL =
-             "http://api.openstreetmap.org/api/0.6";
-
-    /**
-     * The baseURL defaults to the value of DEFAULTAPIBASEURL.
-     */
-    private String myBaseURL;
-
-    /**
-     * the user-name to use.
-     */
-    private String myUserName;
-
-    /**
-     * the password to use.
-     */
-    private String myPassword;
-
-    /**
-     * Comment to add to the Changeset.
-     */
-    private String myComment;
-
-    /**
-     * Used to generate the XML-content of an osc-file.
-     */
-    private OsmChangeWriter myChangeWriter;
-
-    /**
-     * The ID of the changeset we opened on the server.
-     */
-    private int myChangesetNumber = -1;
-
-    /**
-     * We cache the changeset here to replace the
-     * changeset-id later.
-     */
-    private StringWriter myChangesetBuffer = new StringWriter();
-
-    /**
-     * Creates a new instance.
-     * The baseURL defaults to the production API.
-     * @param aBaseURL may be null
-     * @param aUserName the user-name to use
-     * @param aPassword the password to use
-     * @param aComment the comment to set in the changeset
-     */
-    public XmlChangeUploader(final String aBaseURL,
-                             final String aUserName,
-                             final String aPassword,
-                             final String aComment) {
-
-        if (aBaseURL == null) {
-            this.myBaseURL = DEFAULTAPIBASEURL;
-        } else {
-            this.myBaseURL = aBaseURL;
-        }
-        if (aUserName == null) {
-            throw new IllegalArgumentException("null username given");
-        }
-        this.myUserName = aUserName;
-        if (aPassword == null) {
-            throw new IllegalArgumentException("null password given");
-        }
-        this.myPassword = aPassword;
-        if (aComment == null) {
-            this.myComment = "";
-        } else {
-            this.myComment = aComment;
-        }
-        this.myPassword = aPassword;
-
-        this.myChangeWriter = new OsmChangeWriter("osmChange", 0);
-    }
-
-    /**
-     * Open the changeset if it is not yet open.
-     * @throws IOException if we cannot contact the server.
-     */
-    protected final void initialize() throws IOException {
-        if (myChangesetNumber == -1) {
-            URL url = new URL(this.myBaseURL + "/changeset/create");
-            System.err.println("DEBUG: URL= " + url.toString());
-            HttpURLConnection httpCon = (HttpURLConnection)
-                                      url.openConnection();
-
-            // we do not use Authenticator.setDefault()
-            // here to stay thread-safe.
-            httpCon.setRequestProperty("Authorization", "Basic "
-                    + Base64.encodeBase64String(
-                            (this.myUserName + ":"
-                           + this.myPassword).getBytes("UTF8")));
-
-            httpCon.setDoOutput(true);
-            httpCon.setRequestMethod("PUT");
-            OutputStreamWriter out = new OutputStreamWriter(
-                httpCon.getOutputStream());
-            out.write("<osm version=\"0.6\" generator=\"Osmosis "
-                    + OsmosisConstants.VERSION + "\">\n"
-                    + "\t<changeset>\n");
-            out.write("\t\t<tag k=\"created_by\" v=\"Osmosis\"/>\n");
-            out.write("\t\t<tag k=\"comment\" v=\""
-                           + this.myComment + "\"/>\n");
-            out.write("\t</changeset>\n</osm>");
-            out.close();
-
-            int responseCode = httpCon.getResponseCode();
-            if (responseCode != HttpURLConnection.HTTP_OK) {
-                InputStreamReader reader = new InputStreamReader(
-                        httpCon.getInputStream());
-                LOG.severe(readAll(reader).toString());
-                throw new IllegalStateException("Http-Status-code is not"
-                        + " 200 OK but " + responseCode
-                        + " \"" + httpCon.getResponseMessage()
-                        + "\" Error=" + httpCon.getHeaderField("Error"));
-            }
-            Reader in = new InputStreamReader(httpCon.getInputStream());
-            char[] buffer = new char[Byte.MAX_VALUE];
-            int len = in.read(buffer);
-            int changeset = Integer.parseInt(new String(buffer, 0, len));
-
-            LOG.info("opened changeset with ID: " + changeset);
-            this.myChangesetNumber = changeset;
-
-            this.myChangeWriter.setWriter(this.myChangesetBuffer);
-            this.myChangeWriter.begin();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public final void process(final ChangeContainer changeContainer) {
-        try {
-            initialize();
-
-            myChangeWriter.process(changeContainer);
-        } catch (IOException e) {
-            throw new OsmosisRuntimeException(
-                    "Cannot open changeset on server", e);
-        }
-    }
-
-    /**
-     * close the changeset on the server.
-     */
-    public final void complete() {
-        try {
-            myChangeWriter.end();
-            LOG.fine("complete() called");
-            uploadChangeBuffer();
-            closeChangeset();
-        } catch (Exception e) {
-            throw new OsmosisRuntimeException(
-                    "cannot upload or close changeset.", e);
-        }
-    }
-
-    /**
-     * Upload the buffered changes to the server,
-     * replacing the changeset-number.
-     * @throws IOException if we cannot contact the server
-     */
-    private void uploadChangeBuffer() throws IOException {
-        URL url = new URL(this.myBaseURL + "/changeset/"
-                        + this.myChangesetNumber + "/upload");
-        HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
-        httpCon.setDoOutput(true);
-
-        // we do not use Authenticator.setDefault() here to stay thread-safe.
-        httpCon.setRequestProperty("Authorization", "Basic "
-                + Base64.encodeBase64String(
-                        (this.myUserName + ":"
-                       + this.myPassword).getBytes("UTF8")));
-
-        OutputStream out = httpCon.getOutputStream();
-        OutputStreamWriter writer = new OutputStreamWriter(out, "UTF8");
-        writer.flush();
-        String changeSet = this.myChangesetBuffer.getBuffer().toString();
-        System.out.println("changeset we got uploading:\n" + changeSet);
-        String modified = changeSet.replaceAll("changeset=\"[0-9]*\"",
-                                               "changeset=\""
-                                             + this.myChangesetNumber + "\"");
-        System.out.println("changeset we are uploading:\n" + modified);
-        writer.write(modified);
-        writer.close();
-        int responseCode = httpCon.getResponseCode();
-        LOG.fine("response-code to changeset: "
-                + responseCode);
-        if (responseCode != HttpURLConnection.HTTP_OK) {
-//            InputStreamReader reader = new InputStreamReader(
-//                    httpCon.getInputStream());
-//            LOG.severe("response:\n" + readAll(reader).toString());
-            throw new IllegalStateException("Http-Status-code is not"
-                    + " 200 OK but " + responseCode
-                    + " \"" + httpCon.getResponseMessage()
-                    + "\" Error=" + httpCon.getHeaderField("Error"));
-        }
-    }
-
-    /**
-     * Close the changeset on the server,
-     * commiting the change.
-     * @throws IOException if we cannot contact the server
-     */
-    private void closeChangeset() throws IOException {
-        URL url = new URL(this.myBaseURL + "/changeset/"
-                + this.myChangesetNumber + "/close");
-        System.err.println("DEBUG: URL= " + url.toString());
-        HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
-        httpCon.setDoOutput(true);
-        httpCon.setRequestMethod("PUT");
-
-        // we do not use Authenticator.setDefault() here to stay thread-safe.
-        httpCon.setRequestProperty("Authorization", "Basic "
-                + Base64.encodeBase64String(
-                        (this.myUserName + ":"
-                       + this.myPassword).getBytes("UTF8")));
-
-        httpCon.setRequestProperty(
-                "Content-Type", "application/x-www-form-urlencoded");
-        httpCon.connect();
-        int responseCode = httpCon.getResponseCode();
-        LOG.info("response-code to closing of changeset: "
-                + responseCode);
-        this.myChangesetNumber = -1;
-        if (responseCode != HttpURLConnection.HTTP_OK) {
-//            InputStreamReader reader = new InputStreamReader(
-//                       httpCon.getInputStream());
-//            LOG.severe(readAll(reader).toString());
-            throw new IllegalStateException("Http-Status-code is not"
-                    + " 200 OK but " + responseCode
-                    + " \"" + httpCon.getResponseMessage()
-                    + "\" Error=" + httpCon.getHeaderField("Error"));
-        }
-    }
-
-    /**
-     * Read this reader into a String.
-     * @param aReader where to read from
-     * @return the content
-     * @throws IOException if we cannot read
-     */
-    private StringBuilder readAll(final Reader aReader) throws IOException {
-        char[] buffer = new char[Byte.MAX_VALUE];
-        int reat = -1;
-        StringBuilder sb = new StringBuilder();
-        while ((reat = aReader.read(buffer)) >= 0) {
-            sb.append(buffer, 0, reat);
-        }
-        aReader.close();
-        return sb;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public final void release() {
-        if (this.myChangesetNumber != -1) {
-            try {
-                LOG.fine("release() called");
-                closeChangeset();
-            } catch (Exception e) {
-                LOG.log(Level.SEVERE, "Cannot close changeset.", e);
-            }
-        }
-    }
-
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java
deleted file mode 100644
index 3a5944b..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeWriter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.BufferedWriter;
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.xml.common.BaseXmlWriter;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmChangeWriter;
-
-
-/**
- * An OSM change sink for storing all data to an xml file.
- *
- * @author Brett Henderson
- */
-public class XmlChangeWriter extends BaseXmlWriter implements ChangeSink {
-
-	private OsmChangeWriter osmChangeWriter;
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to write.
-	 * @param compressionMethod
-	 *            Specifies the compression method to employ.
-	 */
-	public XmlChangeWriter(File file, CompressionMethod compressionMethod) {
-    	super(file, compressionMethod);
-
-        osmChangeWriter = new OsmChangeWriter("osmChange", 0);
-    }
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(ChangeContainer changeContainer) {
-		initialize();
-		
-		osmChangeWriter.process(changeContainer);
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void beginElementWriter() {
-		osmChangeWriter.begin();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void endElementWriter() {
-		osmChangeWriter.end();
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void setWriterOnElementWriter(BufferedWriter writer) {
-		osmChangeWriter.setWriter(writer);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java
deleted file mode 100644
index 64e754a..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlDownloader.java
+++ /dev/null
@@ -1,270 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.core.util.MultiMemberGZIPInputStream;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmHandler;
-import org.openstreetmap.osmosis.xml.v0_6.impl.XmlConstants;
-
-
-/**
- * An OSM data source reading from an osm-xml file from the
- * OpenStreetMap-server.
- *
- * @author <a href="mailto:Marcus at Wolschon.biz">Marcus Wolschon</a>
- */
-public class XmlDownloader implements RunnableSource {
-
-    /**
-     * The http-response-code for OK.
-     */
-    private static final int RESPONSECODE_OK = 200;
-
-
-    /**
-     * My logger for debug- and error-output.
-     */
-    private static Logger log = Logger.getLogger(XmlDownloader.class.getName());
-
-
-    /**
-     * The timeout we use for the  HttpURLConnection.
-     */
-    private static final int TIMEOUT = 15000;
-
-
-    /**
-     * Where to deliver the loaded data.
-     */
-    private Sink mySink;
-
-    /**
-     * Left longitude of the bounding box.
-     */
-    private double myLeft;
-
-    /**
-     * Right longitude of the bounding box.
-     */
-    private double myRight;
-
-    /**
-     * Top latitude of the bounding box.
-     */
-    private double myTop;
-
-    /**
-     * Bottom latitude of the bounding box.
-     */
-    private double myBottom;
-
-    /**
-     * The base url of the server.
-     * Defaults to. "http://www.openstreetmap.org/api/0.5".
-     */
-    private String myBaseUrl = XmlConstants.DEFAULT_URL;
-
-    /**
-     * The http connection used to retrieve data.
-     */
-    private HttpURLConnection myActiveConnection;
-
-    /**
-     * The stream providing response data.
-     */
-    private InputStream responseStream;
-
-    /**
-     * Creates a new instance with the specified geographical coordinates.
-     *
-     * @param left
-     *            The longitude marking the left edge of the bounding box.
-     * @param right
-     *            The longitude marking the right edge of the bounding box.
-     * @param top
-     *            The latitude marking the top edge of the bounding box.
-     * @param bottom
-     *            The latitude marking the bottom edge of the bounding box.
-     * @param baseUrl
-     *            (optional) The base url of the server (eg.
-     *            http://www.openstreetmap.org/api/0.5).
-     */
-    public XmlDownloader(final double left,
-                         final double right,
-                         final double top,
-                         final double bottom,
-                         final String baseUrl) {
-        this.myLeft = Math.min(left, right);
-        this.myRight = Math.max(left, right);
-        this.myTop = Math.max(top, bottom);
-        this.myBottom = Math.min(top, bottom);
-        if (baseUrl != null) {
-            this.myBaseUrl = baseUrl;
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setSink(final Sink aSink) {
-        this.mySink = aSink;
-    }
-
-    /**
-     * Cleans up any resources remaining after completion.
-     */
-    private void cleanup() {
-        if (myActiveConnection != null) {
-            try {
-                myActiveConnection.disconnect();
-            } catch (Exception e) {
-                log.log(Level.SEVERE, "Unable to disconnect.", e);
-            }
-            myActiveConnection = null;
-        }
-
-        if (responseStream != null) {
-            try {
-
-                responseStream.close();
-            } catch (IOException e) {
-                log.log(Level.SEVERE, "Unable to close response stream.", e);
-            }
-            responseStream = null;
-        }
-    }
-
-
-    /**
-     * Creates a new SAX parser.
-     *
-     * @return The newly created SAX parser.
-     */
-    private SAXParser createParser() {
-        try {
-            return SAXParserFactory.newInstance().newSAXParser();
-
-        } catch (ParserConfigurationException e) {
-            throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-        } catch (SAXException e) {
-            throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-        }
-    }
-
-
-    /**
-     * Reads all data from the server and send it to the {@link Sink}.
-     */
-    public void run() {
-        try {
-            SAXParser parser = createParser();
-            InputStream inputStream =
-            	getInputStream(myBaseUrl + "/map?bbox=" + myLeft + "," + myBottom + "," + myRight + "," + myTop);
-
-            // First send the Bound down the pipeline
-            mySink.process(new BoundContainer(new Bound(myRight, myLeft, myTop, myBottom, myBaseUrl)));
-
-            try {
-                parser.parse(inputStream, new OsmHandler(mySink, true));
-            } finally {
-                inputStream.close();
-                inputStream = null;
-            }
-
-            mySink.complete();
-
-        } catch (SAXParseException e) {
-            throw new OsmosisRuntimeException(
-                    "Unable to parse xml"
-                    + ".  publicId=(" + e.getPublicId()
-                    + "), systemId=(" + e.getSystemId()
-                    + "), lineNumber=" + e.getLineNumber()
-                    + ", columnNumber=" + e.getColumnNumber() + ".",
-                    e);
-        } catch (SAXException e) {
-            throw new OsmosisRuntimeException("Unable to parse XML.", e);
-        } catch (IOException e) {
-            throw new OsmosisRuntimeException("Unable to read XML.", e);
-        } finally {
-            mySink.release();
-
-            cleanup();
-        }
-    }
-
-    /**
-     * Open a connection to the given url and return a reader on the input
-     * stream from that connection.
-     *
-     * @param pUrlStr
-     *            The exact url to connect to.
-     * @return An reader reading the input stream (servers answer) or
-     *         <code>null</code>.
-     * @throws IOException
-     *             on io-errors
-     */
-    private InputStream getInputStream(final String pUrlStr) throws IOException {
-        URL url;
-        int responseCode;
-        String encoding;
-
-        url = new URL(pUrlStr);
-        myActiveConnection = (HttpURLConnection) url.openConnection();
-
-        myActiveConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
-
-        responseCode = myActiveConnection.getResponseCode();
-
-        if (responseCode != RESPONSECODE_OK) {
-            String message;
-            String apiErrorMessage;
-
-            apiErrorMessage = myActiveConnection.getHeaderField("Error");
-
-            if (apiErrorMessage != null) {
-                message = "Received API HTTP response code " + responseCode
-                + " with message \"" + apiErrorMessage
-                + "\" for URL \"" + pUrlStr + "\".";
-            } else {
-                message = "Received API HTTP response code " + responseCode
-                + " for URL \"" + pUrlStr + "\".";
-            }
-
-            throw new OsmosisRuntimeException(message);
-        }
-
-        myActiveConnection.setConnectTimeout(TIMEOUT);
-
-        encoding = myActiveConnection.getContentEncoding();
-
-        responseStream = myActiveConnection.getInputStream();
-        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
-            responseStream = new MultiMemberGZIPInputStream(responseStream);
-        } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
-            responseStream = new InflaterInputStream(responseStream, new Inflater(true));
-        }
-
-        return responseStream;
-    }
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java
deleted file mode 100644
index 029383f..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlReader.java
+++ /dev/null
@@ -1,137 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.v0_6.RunnableSource;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.CompressionActivator;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmHandler;
-
-
-/**
- * An OSM data source reading from an xml file. The entire contents of the file
- * are read.
- * 
- * @author Brett Henderson
- */
-public class XmlReader implements RunnableSource {
-	
-	private static Logger log = Logger.getLogger(XmlReader.class.getName());
-	
-	private Sink sink;
-	private File file;
-	private boolean enableDateParsing;
-	private CompressionMethod compressionMethod;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to read.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current
-	 *            date will be used thus saving parsing time.
-	 * @param compressionMethod
-	 *            Specifies the compression method to employ.
-	 */
-	public XmlReader(File file, boolean enableDateParsing, CompressionMethod compressionMethod) {
-		this.file = file;
-		this.enableDateParsing = enableDateParsing;
-		this.compressionMethod = compressionMethod;
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void setSink(Sink sink) {
-		this.sink = sink;
-	}
-	
-	
-	/**
-	 * Creates a new SAX parser.
-	 * 
-	 * @return The newly created SAX parser.
-	 */
-	private SAXParser createParser() {
-		try {
-			return SAXParserFactory.newInstance().newSAXParser();
-			
-		} catch (ParserConfigurationException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		}
-	}
-	
-	
-	/**
-	 * Reads all data from the file and send it to the sink.
-	 */
-	public void run() {
-		InputStream inputStream = null;
-		
-		try {
-			SAXParser parser;
-			
-			// make "-" an alias for /dev/stdin
-			if (file.getName().equals("-")) {
-				inputStream = System.in;
-			} else {
-				inputStream = new FileInputStream(file);
-			}
-			
-			
-			inputStream =
-				new CompressionActivator(compressionMethod).
-					createCompressionInputStream(inputStream);
-			
-			parser = createParser();
-			
-			parser.parse(inputStream, new OsmHandler(sink, enableDateParsing));
-			
-			sink.complete();
-			
-		} catch (SAXParseException e) {
-			throw new OsmosisRuntimeException(
-				"Unable to parse xml file " + file
-				+ ".  publicId=(" + e.getPublicId()
-				+ "), systemId=(" + e.getSystemId()
-				+ "), lineNumber=" + e.getLineNumber()
-				+ ", columnNumber=" + e.getColumnNumber() + ".",
-				e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Unable to parse XML.", e);
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("Unable to read XML file " + file + ".", e);
-		} finally {
-			sink.release();
-			
-			if (inputStream != null) {
-				try {
-					inputStream.close();
-				} catch (IOException e) {
-					log.log(Level.SEVERE, "Unable to close input stream.", e);
-				}
-				inputStream = null;
-			}
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java
deleted file mode 100644
index 057777a..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriter.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.BufferedWriter;
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.BaseXmlWriter;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.v0_6.impl.OsmWriter;
-
-
-/**
- * An OSM data sink for storing all data to an xml file.
- * 
- * @author Brett Henderson
- */
-public class XmlWriter extends BaseXmlWriter implements Sink {
-	
-	private OsmWriter osmWriter;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param writer
-	 *            The writer to send all data to.
-	 */
-	public XmlWriter(BufferedWriter writer) {
-		super(writer);
-		
-		osmWriter = new OsmWriter("osm", 0, true);
-	}
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param file
-	 *            The file to write.
-	 * @param compressionMethod
-	 *            Specifies the compression method to employ.
-	 */
-	public XmlWriter(File file, CompressionMethod compressionMethod) {
-		super(file, compressionMethod);
-		
-		osmWriter = new OsmWriter("osm", 0, true);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void process(EntityContainer entityContainer) {
-		initialize();
-		
-		osmWriter.process(entityContainer);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void beginElementWriter() {
-		osmWriter.begin();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void endElementWriter() {
-		osmWriter.end();
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected void setWriterOnElementWriter(BufferedWriter writer) {
-		osmWriter.setWriter(writer);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java
deleted file mode 100644
index d90cc25..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/XmlWriterFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.File;
-
-import org.openstreetmap.osmosis.core.pipeline.common.TaskConfiguration;
-import org.openstreetmap.osmosis.core.pipeline.common.TaskManager;
-import org.openstreetmap.osmosis.core.pipeline.v0_6.SinkManager;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-import org.openstreetmap.osmosis.xml.common.XmlTaskManagerFactory;
-
-
-/**
- * The task manager factory for an xml writer.
- * 
- * @author Brett Henderson
- */
-public class XmlWriterFactory extends XmlTaskManagerFactory {
-	private static final String ARG_FILE_NAME = "file";
-	private static final String DEFAULT_FILE_NAME = "dump.osm";
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	protected TaskManager createTaskManagerImpl(TaskConfiguration taskConfig) {
-		String fileName;
-		File file;
-		XmlWriter task;
-		CompressionMethod compressionMethod;
-		
-		// Get the task arguments.
-		fileName = getStringArgument(
-			taskConfig,
-			ARG_FILE_NAME,
-			getDefaultStringArgument(taskConfig, DEFAULT_FILE_NAME)
-		);
-		compressionMethod = getCompressionMethodArgument(taskConfig, fileName);
-		
-		// Create a file object from the file name provided.
-		file = new File(fileName);
-		
-		// Build the task object.
-		task = new XmlWriter(file, compressionMethod);
-		
-		return new SinkManager(taskConfig.getId(), task, taskConfig.getPipeArgs());
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundElementProcessor.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundElementProcessor.java
deleted file mode 100644
index e7e7b0c..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundElementProcessor.java
+++ /dev/null
@@ -1,89 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import org.xml.sax.Attributes;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
-
-/**
- * Provides an element processor implementation for a node.
- * 
- * @author Karl Newman
- */
-public class BoundElementProcessor extends SourceElementProcessor {
-	private static final String ATTRIBUTE_NAME_BOX = "box";
-	private static final String ATTRIBUTE_NAME_ORIGIN = "origin";
-
-	private Bound bound;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param parentProcessor
-	 *            The parent of this element processor.
-	 * @param sink
-	 *            The sink for receiving processed data.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current date will be used
-	 *            thus saving parsing time.
-	 */
-	public BoundElementProcessor(BaseElementProcessor parentProcessor,
-	        Sink sink,
-	        boolean enableDateParsing) {
-		super(parentProcessor, sink, enableDateParsing);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void begin(Attributes attributes) {
-		String boxString;
-		String origin;
-		String[] boundStrings;
-		Double right;
-		Double left;
-		Double top;
-		Double bottom;
-		
-		boxString = attributes.getValue(ATTRIBUTE_NAME_BOX);
-		
-		if (boxString == null) {
-			throw new OsmosisRuntimeException("Missing required box attribute of bound element");
-		}
-		boundStrings = boxString.split(",");
-		if (boundStrings.length != 4) {
-			throw new OsmosisRuntimeException("Badly formed box attribute of bound element");
-		}
-		try {
-			bottom = Double.parseDouble(boundStrings[0]);
-			left = Double.parseDouble(boundStrings[1]);
-			top = Double.parseDouble(boundStrings[2]);
-			right = Double.parseDouble(boundStrings[3]);
-		} catch (NumberFormatException e) {
-			throw new OsmosisRuntimeException("Can't parse box attribute of bound element", e);
-		}
-		origin = attributes.getValue(ATTRIBUTE_NAME_ORIGIN);
-		if (origin == null || origin.equals("")) {
-			throw new OsmosisRuntimeException("Origin attribute of bound element is empty or missing.");
-		}
-		bound = new Bound(right, left, top, bottom, origin);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void end() {
-		getSink().process(new BoundContainer(bound));
-		bound = null;
-	}
-
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java
deleted file mode 100644
index affd0e5..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriter.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.util.Locale;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-/**
- * @author KNewman
- * 
- */
-public class BoundWriter extends ElementWriter {
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 */
-	public BoundWriter(String elementName, int indentLevel) {
-		super(elementName, indentLevel);
-	}
-
-
-	/**
-	 * Writes the bound.
-	 * 
-	 * @param bound
-	 *            The bound to be processed.
-	 */
-	public void process(Bound bound) {
-
-		// Only add the Bound if the origin string isn't empty
-		if (bound.getOrigin() != "") {
-			beginOpenElement();
-			// Write with the US locale (to force . instead of , as the decimal separator)
-			// Use only 5 decimal places (~1.2 meter resolution should be sufficient for Bound)
-			addAttribute("box", String.format(
-			        Locale.US,
-			        "%.5f,%.5f,%.5f,%.5f",
-			        bound.getBottom(),
-			        bound.getLeft(),
-			        bound.getTop(),
-			        bound.getRight()));
-			addAttribute("origin", bound.getOrigin());
-			endOpenElement(true);
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java
deleted file mode 100644
index a39069c..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/ChangeSourceElementProcessor.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import org.xml.sax.Attributes;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
-import org.openstreetmap.osmosis.xml.common.ElementProcessor;
-
-
-/**
- * Provides an element processor implementation for an osm change element.
- * 
- * @author Brett Henderson
- */
-public class ChangeSourceElementProcessor extends BaseElementProcessor {
-	private static final String ELEMENT_NAME_CREATE = "create";
-	private static final String ELEMENT_NAME_MODIFY = "modify";
-	private static final String ELEMENT_NAME_DELETE = "delete";
-	private static final String ATTRIBUTE_NAME_VERSION = "version";
-	
-	
-	private OsmElementProcessor createElementProcessor;
-	private OsmElementProcessor modifyElementProcessor;
-	private OsmElementProcessor deleteElementProcessor;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param parentProcessor
-	 *            The parent of this element processor.
-	 * @param changeSink
-	 *            The changeSink for receiving processed data.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current
-	 *            date will be used thus saving parsing time.
-	 */
-	public ChangeSourceElementProcessor(
-			BaseElementProcessor parentProcessor, ChangeSink changeSink, boolean enableDateParsing) {
-		super(parentProcessor, enableDateParsing);
-		
-		createElementProcessor =
-			new OsmElementProcessor(
-					this, new ChangeSinkAdapter(changeSink, ChangeAction.Create), enableDateParsing, false);
-		modifyElementProcessor =
-			new OsmElementProcessor(
-					this, new ChangeSinkAdapter(changeSink, ChangeAction.Modify), enableDateParsing, false);
-		deleteElementProcessor =
-			new OsmElementProcessor(
-					this, new ChangeSinkAdapter(changeSink, ChangeAction.Delete), enableDateParsing, false);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void begin(Attributes attributes) {
-		String fileVersion;
-		
-		fileVersion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
-		
-		if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
-			System.err.println(
-				"Warning, expected version " + XmlConstants.OSM_VERSION
-				+ " but received " + fileVersion + "."
-			);
-		}
-	}
-	
-	
-	/**
-	 * Retrieves the appropriate child element processor for the newly
-	 * encountered nested element.
-	 * 
-	 * @param uri
-	 *            The element uri.
-	 * @param localName
-	 *            The element localName.
-	 * @param qName
-	 *            The element qName.
-	 * @return The appropriate element processor for the nested element.
-	 */
-	@Override
-	public ElementProcessor getChild(String uri, String localName, String qName) {
-		if (ELEMENT_NAME_CREATE.equals(qName)) {
-			return createElementProcessor;
-		} else if (ELEMENT_NAME_MODIFY.equals(qName)) {
-			return modifyElementProcessor;
-		} else if (ELEMENT_NAME_DELETE.equals(qName)) {
-			return deleteElementProcessor;
-		}
-		
-		return super.getChild(uri, localName, qName);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void end() {
-		// This class produces no data and therefore doesn't need to do anything
-		// when the end of the element is reached.
-	}
-	
-	
-	private static class ChangeSinkAdapter implements Sink {
-		private ChangeSink changeSink;
-		private ChangeAction action;
-		
-		
-		/**
-		 * Creates a new instance.
-		 * 
-		 * @param changeSink
-		 *            The changeSink for receiving processed data.
-		 * @param action
-		 *            The action to apply to all data received.
-		 */
-		public ChangeSinkAdapter(ChangeSink changeSink, ChangeAction action) {
-			this.changeSink = changeSink;
-			this.action = action;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(EntityContainer entityContainer) {
-			changeSink.process(new ChangeContainer(entityContainer, action));
-		}
-
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void complete() {
-			changeSink.complete();
-		}
-
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void release() {
-			changeSink.release();
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java
deleted file mode 100644
index fec8933..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/FastXmlParser.java
+++ /dev/null
@@ -1,393 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.util.Calendar;
-import java.util.logging.Logger;
-
-import javax.xml.stream.XMLStreamConstants;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.common.SimpleTimestampContainer;
-import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
-import org.openstreetmap.osmosis.core.domain.common.TimestampFormat;
-import org.openstreetmap.osmosis.core.domain.common.UnparsedTimestampContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.XmlTimestampFormat;
-
-
-/**
- * Reads the contents of an osm file using a Stax parser.
- * 
- * @author Jiri Klement
- * @author Brett Henderson
- */
-public class FastXmlParser {
-	
-	private static final String ELEMENT_NAME_BOUND = "bound";
-	private static final String ELEMENT_NAME_NODE = "node";
-	private static final String ELEMENT_NAME_WAY = "way";
-	private static final String ELEMENT_NAME_RELATION = "relation";
-	private static final String ELEMENT_NAME_TAG = "tag";
-	private static final String ELEMENT_NAME_NODE_REFERENCE = "nd";
-	private static final String ELEMENT_NAME_MEMBER = "member";
-	private static final String ATTRIBUTE_NAME_ID = "id";
-	private static final String ATTRIBUTE_NAME_VERSION = "version";
-	private static final String ATTRIBUTE_NAME_TIMESTAMP = "timestamp";
-	private static final String ATTRIBUTE_NAME_USER_ID = "uid";
-	private static final String ATTRIBUTE_NAME_USER = "user";
-	private static final String ATTRIBUTE_NAME_CHANGESET_ID = "changeset";
-	private static final String ATTRIBUTE_NAME_LATITUDE = "lat";
-	private static final String ATTRIBUTE_NAME_LONGITUDE = "lon";
-	private static final String ATTRIBUTE_NAME_KEY = "k";
-	private static final String ATTRIBUTE_NAME_VALUE = "v";
-	private static final String ATTRIBUTE_NAME_REF = "ref";
-	private static final String ATTRIBUTE_NAME_TYPE = "type";
-	private static final String ATTRIBUTE_NAME_ROLE = "role";
-	private static final String ATTRIBUTE_NAME_BOX = "box";
-	private static final String ATTRIBUTE_NAME_ORIGIN = "origin";
-	
-	private static final Logger LOG = Logger.getLogger(FastXmlParser.class.getName());
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param sink
-	 *            The sink receiving all output data.
-	 * @param reader
-	 *            The input xml reader.
-	 * @param enableDateParsing
-	 *            If true, parsing of dates in the xml will be enabled,
-	 *            otherwise the current system time will be used.
-	 */
-	public FastXmlParser(Sink sink, XMLStreamReader reader, boolean enableDateParsing) {
-		this.sink = sink;
-		this.enableDateParsing = enableDateParsing;
-		this.reader = reader;
-		
-		if (enableDateParsing) {
-			timestampFormat = new XmlTimestampFormat();
-		} else {
-			Calendar calendar;
-			
-			calendar = Calendar.getInstance();
-			calendar.set(Calendar.MILLISECOND, 0);
-			dummyTimestampContainer = new SimpleTimestampContainer(calendar.getTime());
-		}
-		
-		memberTypeParser = new MemberTypeParser();
-	}
-	
-	private final XMLStreamReader reader;
-	private final Sink sink;
-	private final boolean enableDateParsing;
-	private final MemberTypeParser memberTypeParser;
-	private TimestampFormat timestampFormat;
-	private TimestampContainer dummyTimestampContainer;
-
-	
-	private TimestampContainer parseTimestamp(String data) {
-		if (enableDateParsing) {
-			return new UnparsedTimestampContainer(timestampFormat, data);
-		} else {
-			return dummyTimestampContainer;
-		}
-	}
-	
-	private void readUnknownElement() throws XMLStreamException {
-		int level = 0;
-		
-		do {
-			if (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
-				level++;
-			} else if (reader.getEventType() == XMLStreamConstants.END_ELEMENT) {
-				level--;
-			}
-			reader.nextTag();
-		} while (level > 0);
-	}
-
-
-	/**
-	 * Creates a user instance based on the current entity attributes. This includes identifying the
-	 * case where no user is available.
-	 * 
-	 * @return The appropriate user instance.
-	 */
-	private OsmUser readUser() {
-		String rawUserId;
-		String rawUserName;
-		
-		rawUserId = reader.getAttributeValue(null, ATTRIBUTE_NAME_USER_ID);
-		rawUserName = reader.getAttributeValue(null, ATTRIBUTE_NAME_USER);
-		
-		if (rawUserId != null) {
-			int userId;
-			String userName;
-			
-			userId = Integer.parseInt(rawUserId);
-			if (rawUserName == null) {
-				userName = "";
-			} else {
-				userName = rawUserName;
-			}
-			
-			return new OsmUser(userId, userName);
-			
-		} else {
-			return OsmUser.NONE;
-		}
-	}
-	
-	
-	/**
-	 * Parses a changeset id from the current entity.
-	 * 
-	 * @return The changeset id as a long. 0 is returned if no attribute is available.
-	 */
-	private long readChangesetId() {
-		String changesetIdAttribute;
-		
-		changesetIdAttribute = reader.getAttributeValue(null, ATTRIBUTE_NAME_CHANGESET_ID);
-		if (changesetIdAttribute != null) {
-			return Long.parseLong(changesetIdAttribute);
-		} else {
-			return 0;
-		}
-	}
-	
-	
-	private Bound readBound() throws Exception {
-		String boxString;
-		String origin;
-		String[] boundStrings;
-		Double right;
-		Double left;
-		Double top;
-		Double bottom;
-		
-		boxString = reader.getAttributeValue(null, ATTRIBUTE_NAME_BOX);
-		
-		if (boxString == null) {
-			throw new OsmosisRuntimeException("Missing required box attribute of bound element");
-		}
-		boundStrings = boxString.split(",");
-		if (boundStrings.length != 4) {
-			throw new OsmosisRuntimeException("Badly formed box attribute of bound element");
-		}
-		try {
-			bottom = Double.parseDouble(boundStrings[0]);
-			left = Double.parseDouble(boundStrings[1]);
-			top = Double.parseDouble(boundStrings[2]);
-			right = Double.parseDouble(boundStrings[3]);
-		} catch (NumberFormatException e) {
-			throw new OsmosisRuntimeException("Can't parse box attribute of bound element", e);
-		}
-		origin = reader.getAttributeValue(null, ATTRIBUTE_NAME_ORIGIN);
-		if (origin == null || origin.equals("")) {
-			throw new OsmosisRuntimeException("Origin attribute of bound element is empty or missing.");
-		}
-		Bound bound = new Bound(right, left, top, bottom, origin);
-		
-		reader.nextTag();
-		reader.nextTag();
-		
-		return bound;
-	}
-	
-	private Tag readTag() throws Exception {
-		Tag tag = new Tag(reader.getAttributeValue(null, ATTRIBUTE_NAME_KEY),
-				reader.getAttributeValue(null, ATTRIBUTE_NAME_VALUE));
-		reader.nextTag();
-		reader.nextTag();
-		return tag;
-	}
-	
-	private Node readNode() throws Exception {
-		long id;
-		int version;
-		TimestampContainer timestamp;
-		OsmUser user;
-		long changesetId;
-		double latitude;
-		double longitude;
-		Node node;
-		
-		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
-		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
-		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
-		changesetId = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_CHANGESET_ID));
-		user = readUser();
-		changesetId = readChangesetId();
-		latitude = Double.parseDouble(reader.getAttributeValue(null, ATTRIBUTE_NAME_LATITUDE));
-		longitude = Double.parseDouble(reader.getAttributeValue(null, ATTRIBUTE_NAME_LONGITUDE));
-		
-		node = new Node(new CommonEntityData(id, version, timestamp, user, changesetId), latitude, longitude);
-		
-		reader.nextTag();
-		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
-			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
-				node.getTags().add(readTag());
-			} else {
-				readUnknownElement();
-			}
-		}
-		
-		reader.nextTag();
-		
-		return node;
-	}
-	
-	private WayNode readWayNode() throws Exception {
-		WayNode node = new WayNode(
-				Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_REF)));
-		reader.nextTag();
-		reader.nextTag();
-		return node;
-	}
-	
-	private Way readWay() throws Exception {
-		long id;
-		int version;
-		TimestampContainer timestamp;
-		OsmUser user;
-		long changesetId;
-		Way way;
-		
-		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
-		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
-		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
-		user = readUser();
-		changesetId = readChangesetId();
-		
-		way = new Way(new CommonEntityData(id, version, timestamp, user, changesetId));
-		
-		reader.nextTag();
-		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
-			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
-				way.getTags().add(readTag());
-			} else if (reader.getLocalName().equals(ELEMENT_NAME_NODE_REFERENCE)) {
-				way.getWayNodes().add(readWayNode());
-			} else {
-				readUnknownElement();
-			}
-		}
-		reader.nextTag();
-
-		return way;
-	}
-	
-	private RelationMember readRelationMember() throws Exception {
-		long id;
-		EntityType type;
-		String role;
-		
-		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_REF));
-		type = memberTypeParser.parse(reader.getAttributeValue(null, ATTRIBUTE_NAME_TYPE));
-		role = reader.getAttributeValue(null, ATTRIBUTE_NAME_ROLE);
-		
-		RelationMember relationMember = new RelationMember(id, type, role);
-		
-		reader.nextTag();
-		reader.nextTag();
-		
-		return relationMember;
-	}
-	
-	private Relation readRelation() throws Exception {
-		long id;
-		int version;
-		TimestampContainer timestamp;
-		OsmUser user;
-		long changesetId;
-		Relation relation;
-		
-		id = Long.parseLong(reader.getAttributeValue(null, ATTRIBUTE_NAME_ID));
-		version = Integer.parseInt(reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION));
-		timestamp = parseTimestamp(reader.getAttributeValue(null, ATTRIBUTE_NAME_TIMESTAMP));
-		user = readUser();
-		changesetId = readChangesetId();
-		
-		relation = new Relation(new CommonEntityData(id, version, timestamp, user, changesetId));
-		
-		reader.nextTag();
-		while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {
-			if (reader.getLocalName().equals(ELEMENT_NAME_TAG)) {
-				relation.getTags().add(readTag());
-			} else if (reader.getLocalName().equals(ELEMENT_NAME_MEMBER)) {
-				relation.getMembers().add(readRelationMember());
-			} else {
-				readUnknownElement();
-			}
-		}
-		reader.nextTag();
-		
-		return relation;
-	}
-
-	
-	/**
-	 * Parses the xml and sends all data to the sink.
-	 */
-	public void readOsm() {
-		
-		try {
-		
-			if (reader.nextTag() == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("osm")) {
-
-				String fileVersion;
-
-				fileVersion = reader.getAttributeValue(null, ATTRIBUTE_NAME_VERSION);
-
-				if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
-					LOG.warning(
-							"Expected version " + XmlConstants.OSM_VERSION
-							+ " but received " + fileVersion + "."
-					);
-				}
-
-				reader.nextTag();
-				
-
-				if (reader.getEventType() == XMLStreamConstants.START_ELEMENT
-						&& reader.getLocalName().equals(ELEMENT_NAME_BOUND)) {
-					sink.process(new BoundContainer(readBound()));
-				}
-
-				while (reader.getEventType() == XMLStreamConstants.START_ELEMENT) {			
-					// Node, way, relation
-					if (reader.getLocalName().equals(ELEMENT_NAME_NODE)) {
-						sink.process(new NodeContainer(readNode()));
-					} else if (reader.getLocalName().equals(ELEMENT_NAME_WAY)) {
-						sink.process(new WayContainer(readWay()));
-					} else if (reader.getLocalName().equals(ELEMENT_NAME_RELATION)) {
-						sink.process(new RelationContainer(readRelation()));
-					} else {
-						readUnknownElement();
-					}
-				}
-
-			} else {
-				throw new XMLStreamException();
-			}
-		} catch (Exception e) {
-			throw new OsmosisRuntimeException(e);
-		}
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java
deleted file mode 100644
index 388e4b6..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeElementProcessor.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import org.xml.sax.Attributes;
-
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.domain.common.TimestampContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
-import org.openstreetmap.osmosis.xml.common.ElementProcessor;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-
-
-/**
- * Provides an element processor implementation for a node.
- * 
- * @author Brett Henderson
- */
-public class NodeElementProcessor extends EntityElementProcessor implements TagListener {
-	private static final String ELEMENT_NAME_TAG = "tag";
-	private static final String ATTRIBUTE_NAME_ID = "id";
-	private static final String ATTRIBUTE_NAME_TIMESTAMP = "timestamp";
-	private static final String ATTRIBUTE_NAME_USER = "user";
-	private static final String ATTRIBUTE_NAME_USERID = "uid";
-	private static final String ATTRIBUTE_NAME_CHANGESET_ID = "changeset";
-	private static final String ATTRIBUTE_NAME_VERSION = "version";
-	private static final String ATTRIBUTE_NAME_LATITUDE = "lat";
-	private static final String ATTRIBUTE_NAME_LONGITUDE = "lon";
-	
-	private TagElementProcessor tagElementProcessor;
-	private Node node;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param parentProcessor
-	 *            The parent of this element processor.
-	 * @param sink
-	 *            The sink for receiving processed data.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current
-	 *            date will be used thus saving parsing time.
-	 */
-	public NodeElementProcessor(BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing) {
-		super(parentProcessor, sink, enableDateParsing);
-		
-		tagElementProcessor = new TagElementProcessor(this, this);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void begin(Attributes attributes) {
-		long id;
-		String sversion;
-		int version;
-		TimestampContainer timestampContainer;
-		String rawUserId;
-		String rawUserName;
-		OsmUser user;
-		long changesetId;
-		double latitude;
-		double longitude;
-		
-		id = Long.parseLong(attributes.getValue(ATTRIBUTE_NAME_ID));
-		sversion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
-		if (sversion == null) {
-			throw new OsmosisRuntimeException("Node " + id
-					+ " does not have a version attribute as OSM 0.6 are required to have.  Is this a 0.5 file?");
-		} else {
-			version = Integer.parseInt(sversion);
-		}
-		timestampContainer = createTimestampContainer(attributes.getValue(ATTRIBUTE_NAME_TIMESTAMP));
-		rawUserId = attributes.getValue(ATTRIBUTE_NAME_USERID);
-		rawUserName = attributes.getValue(ATTRIBUTE_NAME_USER);
-		changesetId = buildChangesetId(attributes.getValue(ATTRIBUTE_NAME_CHANGESET_ID));
-		latitude = Double.parseDouble(attributes.getValue(ATTRIBUTE_NAME_LATITUDE));
-		longitude = Double.parseDouble(attributes.getValue(ATTRIBUTE_NAME_LONGITUDE));
-		
-		user = buildUser(rawUserId, rawUserName);
-		
-		node = new Node(new CommonEntityData(id, version, timestampContainer, user, changesetId), latitude, longitude);
-	}
-	
-	
-	/**
-	 * Retrieves the appropriate child element processor for the newly
-	 * encountered nested element.
-	 * 
-	 * @param uri
-	 *            The element uri.
-	 * @param localName
-	 *            The element localName.
-	 * @param qName
-	 *            The element qName.
-	 * @return The appropriate element processor for the nested element.
-	 */
-	@Override
-	public ElementProcessor getChild(String uri, String localName, String qName) {
-		if (ELEMENT_NAME_TAG.equals(qName)) {
-			return tagElementProcessor;
-		}
-		
-		return super.getChild(uri, localName, qName);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void end() {
-		getSink().process(new NodeContainer(node));
-	}
-	
-	
-	/**
-	 * This is called by child element processors when a tag object is
-	 * encountered.
-	 * 
-	 * @param tag
-	 *            The tag to be processed.
-	 */
-	public void processTag(Tag tag) {
-		node.getTags().add(tag);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java
deleted file mode 100644
index 69a0f34..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/NodeWriter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.io.Writer;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.util.Collection;
-import java.util.Locale;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-
-/**
- * Renders a node as xml.
- *
- * @author Brett Henderson
- */
-public class NodeWriter extends ElementWriter {
-    /**
-     * Write the tags of a node.
-     */
-   private TagWriter tagWriter;
-   private NumberFormat numberFormat;
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 */
-	public NodeWriter(String elementName, int indentLevel) {
-		super(elementName, indentLevel);
-		
-		tagWriter = new TagWriter("tag", indentLevel + 1);
-		
-		// Only write the first 7 decimal places.
-		// Write in US locale so that a '.' is used as the decimal separator.
-		numberFormat = new DecimalFormat(
-			"0.#######;-0.#######",
-			new DecimalFormatSymbols(Locale.US)
-		);
-	}
-	
-	
-	/**
-	 * Writes the node.
-	 * 
-	 * @param node
-	 *            The node to be processed.
-	 */
-	public void process(Node node) {
-		OsmUser user;
-		Collection<Tag> tags;
-		
-		user = node.getUser();
-		
-		beginOpenElement();
-		addAttribute("id", Long.toString(node.getId()));
-		addAttribute("version", Integer.toString(node.getVersion()));
-		addAttribute("timestamp", node.getFormattedTimestamp(getTimestampFormat()));
-		
-		if (!user.equals(OsmUser.NONE)) {
-			addAttribute("uid", Integer.toString(user.getId()));
-			addAttribute("user", user.getName());
-		}
-		
-		if (node.getChangesetId() != 0) {
-			addAttribute("changeset", Long.toString(node.getChangesetId()));
-		}
-		
-		addAttribute("lat", numberFormat.format(node.getLatitude()));
-		addAttribute("lon", numberFormat.format(node.getLongitude()));
-		
-		tags = node.getTags();
-		
-		if (tags.size() > 0) {
-			endOpenElement(false);
-			
-			for (Tag tag : tags) {
-				tagWriter.process(tag);
-			}
-			
-			closeElement();
-			
-		} else {
-			endOpenElement(true);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setWriter(final Writer writer) {
-		super.setWriter(writer);
-		
-		tagWriter.setWriter(writer);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java
deleted file mode 100644
index db77167..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmChangeWriter.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.io.Writer;
-
-import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.common.ChangeAction;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-
-/**
- * Renders OSM changes as xml.
- *
- * @author Brett Henderson
- */
-public class OsmChangeWriter extends ElementWriter {
-
-    /**
-     * The OsmWriter to use for created elements.
-     */
-    private OsmWriter osmCreateWriter;
-
-    /**
-     * The OsmWriter to use for modified elements.
-     */
-    private OsmWriter osmModifyWriter;
-
-    /**
-     * The OsmWriter to use for deleted elements.
-     */
-    private OsmWriter osmDeleteWriter;
-    /**
-     * @see #updateActiveOsmWriter(ChangeAction)
-     */
-    private OsmWriter activeOsmWriter;
-    /**
-     * The last action (add, modify, delete)
-     * that we processed.
-     */
-    private ChangeAction lastAction;
-
-    /**
-     * Creates a new instance that
-     * starts with an <osmChange> -element
-     * at indent-level 0.
-     */
-    public OsmChangeWriter() {
-       this("osmChange", 0);
-    }
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 */
-	public OsmChangeWriter(final String elementName, final int indentLevel) {
-		super(elementName, indentLevel);
-		
-		osmCreateWriter = new OsmWriter("create", indentLevel + 1, false);
-		osmModifyWriter = new OsmWriter("modify", indentLevel + 1, false);
-		osmDeleteWriter = new OsmWriter("delete", indentLevel + 1, false);
-		activeOsmWriter = null;
-		lastAction = null;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setWriter(final Writer aWriter) {
-		super.setWriter(aWriter);
-
-		this.osmCreateWriter.setWriter(aWriter);
-		this.osmModifyWriter.setWriter(aWriter);
-		this.osmDeleteWriter.setWriter(aWriter);
-	}
-
-	/**
-	 * Begins an <osmchange>-element.
-	 */
-	public void begin() {
-		beginOpenElement();
-		addAttribute("version", XmlConstants.OSM_VERSION);
-		addAttribute("generator", "Osmosis " + OsmosisConstants.VERSION);
-		endOpenElement(false);
-	}
-
-	/**
-	 * Ends an <osmchange>-element.
-	 */
-	public void end() {
-		if (activeOsmWriter != null) {
-			activeOsmWriter.end();
-			activeOsmWriter = null;
-		}
-
-		lastAction = null;
-		closeElement();
-	}
-
-	/**
-	 * Returns the appropriate osm writer for the particular change type.
-	 * 
-	 * @param action
-	 *            The change action to be performed.
-	 * @return The osm writer for the change type.
-	 */
-	private OsmWriter getWriterForAction(final ChangeAction action) {
-		if (action.equals(ChangeAction.Create)) {
-			return osmCreateWriter;
-		} else if (action.equals(ChangeAction.Modify)) {
-			return osmModifyWriter;
-		} else if (action.equals(ChangeAction.Delete)) {
-			return osmDeleteWriter;
-		} else {
-			throw new OsmosisRuntimeException("The change action " + action + " is not recognised.");
-		}
-	}
-
-	/**
-	 * Switch to another type of change.
-	 * @param action the action to apply to the next elements.
-	 */
-	private void updateActiveOsmWriter(final ChangeAction action) {
-		if (action != lastAction) {
-			if (activeOsmWriter != null) {
-				activeOsmWriter.end();
-			}
-
-			activeOsmWriter = getWriterForAction(action);
-
-			activeOsmWriter.begin();
-
-			lastAction = action;
-		}
-	}
-
-	/**
-	 * Writes the change in the container.
-	 * 
-	 * @param changeContainer
-	 *            The container holding the change.
-	 */
-	public void process(final ChangeContainer changeContainer) {
-		updateActiveOsmWriter(changeContainer.getAction());
-		activeOsmWriter.process(changeContainer.getEntityContainer());
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java
deleted file mode 100644
index f84081f..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmElementProcessor.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.util.logging.Logger;
-
-import org.xml.sax.Attributes;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-import org.openstreetmap.osmosis.xml.common.BaseElementProcessor;
-import org.openstreetmap.osmosis.xml.common.ElementProcessor;
-
-
-/**
- * Provides an element processor implementation for an osm element.
- * 
- * @author Brett Henderson
- */
-public class OsmElementProcessor extends SourceElementProcessor {
-	
-	private static final Logger LOG = Logger.getLogger(OsmElementProcessor.class.getName());
-	
-	private static final String ELEMENT_NAME_BOUND = "bound";
-	private static final String ELEMENT_NAME_NODE = "node";
-	private static final String ELEMENT_NAME_WAY = "way";
-	private static final String ELEMENT_NAME_RELATION = "relation";
-	private static final String ATTRIBUTE_NAME_VERSION = "version";
-	
-	
-	private BoundElementProcessor boundElementProcessor;
-	private NodeElementProcessor nodeElementProcessor;
-	private WayElementProcessor wayElementProcessor;
-	private RelationElementProcessor relationElementProcessor;
-	
-	private boolean foundBound = false;
-	private boolean foundEntities = false;
-	private boolean validateVersion;
-	
-	
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param parentProcessor
-	 *            The parent of this element processor.
-	 * @param sink
-	 *            The sink for receiving processed data.
-	 * @param enableDateParsing
-	 *            If true, dates will be parsed from xml data, else the current
-	 *            date will be used thus saving parsing time.
-	 *            @param validateVersion If true, a version attribute will be checked and validated.
-	 */
-	public OsmElementProcessor(
-			BaseElementProcessor parentProcessor, Sink sink, boolean enableDateParsing, boolean validateVersion) {
-		super(parentProcessor, sink, enableDateParsing);
-		
-		this.validateVersion = validateVersion;
-		
-		boundElementProcessor = new BoundElementProcessor(this, getSink(), enableDateParsing);
-		nodeElementProcessor = new NodeElementProcessor(this, getSink(), enableDateParsing);
-		wayElementProcessor = new WayElementProcessor(this, getSink(), enableDateParsing);
-		relationElementProcessor = new RelationElementProcessor(this, getSink(), enableDateParsing);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void begin(Attributes attributes) {
-		if (validateVersion) {
-			String fileVersion;
-			
-			fileVersion = attributes.getValue(ATTRIBUTE_NAME_VERSION);
-			
-			if (!XmlConstants.OSM_VERSION.equals(fileVersion)) {
-				LOG.warning(
-					"Expected version " + XmlConstants.OSM_VERSION
-					+ " but received " + fileVersion + "."
-				);
-			}
-		}
-	}
-	
-	
-	/**
-	 * Retrieves the appropriate child element processor for the newly
-	 * encountered nested element.
-	 * 
-	 * @param uri
-	 *            The element uri.
-	 * @param localName
-	 *            The element localName.
-	 * @param qName
-	 *            The element qName.
-	 * @return The appropriate element processor for the nested element.
-	 */
-	@Override
-	public ElementProcessor getChild(String uri, String localName, String qName) {
-		if (ELEMENT_NAME_BOUND.equals(qName)) {
-			if (foundEntities) {
-				throw new OsmosisRuntimeException("Bound element must come before any entities.");
-			}
-			if (foundBound) {
-				throw new OsmosisRuntimeException("Only one bound element allowed.");
-			}
-			foundBound = true;
-			return boundElementProcessor;
-		} else if (ELEMENT_NAME_NODE.equals(qName)) {
-			foundEntities = true;
-			return nodeElementProcessor;
-		} else if (ELEMENT_NAME_WAY.equals(qName)) {
-			foundEntities = true;
-			return wayElementProcessor;
-		} else if (ELEMENT_NAME_RELATION.equals(qName)) {
-			foundEntities = true;
-			return relationElementProcessor;
-		}
-		
-		return super.getChild(uri, localName, qName);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	public void end() {
-		// This class produces no data and therefore doesn't need to do anything
-		// when the end of the element is reached.
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java
deleted file mode 100644
index 86c87ef..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriter.java
+++ /dev/null
@@ -1,184 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.io.Writer;
-
-import org.openstreetmap.osmosis.core.OsmosisConstants;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-
-/**
- * Renders OSM data types as xml.
- *
- * @author Brett Henderson
- */
-public class OsmWriter extends ElementWriter {
-
-	private SubElementWriter subElementWriter;
-	private boolean renderAttributes;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 * @param renderAttributes
-	 *            Specifies whether attributes of the top level element should
-	 *            be rendered. This would typically be set to false if this
-	 *            element is embedded within a higher level element (eg.
-	 *            changesets)
-	 */
-	public OsmWriter(String elementName, int indentLevel, boolean renderAttributes) {
-		super(elementName, indentLevel);
-		
-		this.renderAttributes = renderAttributes;
-		
-		// Create the sub-element writer which calls the appropriate element
-		// writer based on data type.
-		subElementWriter = new SubElementWriter(indentLevel + 1);
-	}
-	
-	
-	/**
-	 * Begins an element.
-	 */
-	public void begin() {
-		beginOpenElement();
-		
-		if (renderAttributes) {
-			addAttribute("version", XmlConstants.OSM_VERSION);
-			addAttribute("generator", "Osmosis " + OsmosisConstants.VERSION);
-		}
-		
-		endOpenElement(false);
-	}
-	
-	
-	/**
-	 * Ends an element.
-	 */
-	public void end() {
-		closeElement();
-	}
-	
-	
-	/**
-	 * Writes the element in the container.
-	 * 
-	 * @param entityContainer
-	 *            The container holding the entity.
-	 */
-	public void process(EntityContainer entityContainer) {
-		entityContainer.process(subElementWriter);
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setWriter(final Writer writer) {
-		super.setWriter(writer);
-		
-		// Tell the sub element writer that a new writer is available. This will
-		// cause the underlying entity writing classes to be updated.
-		subElementWriter.updateWriter(writer);
-	}
-	
-	
-	/**
-	 * Directs data to the appropriate underlying element writer.
-	 * 
-	 * @author Brett Henderson
-	 */
-	private static class SubElementWriter implements EntityProcessor {
-		private NodeWriter nodeWriter;
-		private WayWriter wayWriter;
-		private RelationWriter relationWriter;
-		private BoundWriter boundWriter;
-		private boolean boundWritten = false; // can't write a Bound twice
-		private boolean entitiesWritten = false; // can't write a Bound after any Entities
-		
-		/**
-		 * Creates a new instance.
-		 * 
-		 * @param indentLevel
-		 *            The indent level of the sub-elements.
-		 */
-		public SubElementWriter(int indentLevel) {
-			nodeWriter = new NodeWriter("node", indentLevel);
-			wayWriter = new WayWriter("way", indentLevel);
-			relationWriter = new RelationWriter("relation", indentLevel);
-			boundWriter = new BoundWriter("bound", indentLevel);
-		}
-		
-		
-		/**
-		 * Updates the underlying writer.
-		 * 
-		 * @param writer
-		 *            The writer to be used for all output xml.
-		 */
-		public void updateWriter(final Writer writer) {
-			nodeWriter.setWriter(writer);
-			wayWriter.setWriter(writer);
-			relationWriter.setWriter(writer);
-			boundWriter.setWriter(writer);
-			// reset the flags indicating which data has been written
-			boundWritten = false;
-			entitiesWritten = false;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(NodeContainer node) {
-			nodeWriter.process(node.getEntity());
-			entitiesWritten = true;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(WayContainer way) {
-			wayWriter.process(way.getEntity());
-			entitiesWritten = true;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-		public void process(RelationContainer relation) {
-			relationWriter.process(relation.getEntity());
-			entitiesWritten = true;
-		}
-		
-		
-		/**
-		 * {@inheritDoc}
-		 */
-        public void process(BoundContainer bound) {
-    		if (boundWritten) {
-    			throw new OsmosisRuntimeException("Bound element already written and only one allowed.");
-    		}
-    		if (entitiesWritten) {
-    			throw new OsmosisRuntimeException("Can't write bound element after other entities.");    			
-    		}
-        	boundWriter.process(bound.getEntity());
-    		boundWritten = true;
-        }
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java
deleted file mode 100644
index d1a9d64..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/RelationWriter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.io.Writer;
-import java.util.Collection;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-
-/**
- * Renders a relation as xml.
- *
- * @author Brett Henderson
- */
-public class RelationWriter extends ElementWriter {
-    /**
-     * Write the ordered list of members of a relation.
-     */
-    private RelationMemberWriter relationMemberWriter;
-    /**
-     * Write the tags of a relation.
-     */
-    private TagWriter tagWriter;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 */
-	public RelationWriter(String elementName, int indentLevel) {
-		super(elementName, indentLevel);
-		
-		tagWriter = new TagWriter("tag", indentLevel + 1);
-		relationMemberWriter = new RelationMemberWriter("member", indentLevel + 1);
-	}
-	
-	
-	/**
-	 * Writes the relation.
-	 * 
-	 * @param relation
-	 *            The relation to be processed.
-	 */
-	public void process(Relation relation) {
-		OsmUser user;
-		List<RelationMember> relationMembers;
-		Collection<Tag> tags;
-		
-		user = relation.getUser();
-		
-		beginOpenElement();
-		addAttribute("id", Long.toString(relation.getId()));
-		addAttribute("version", Integer.toString(relation.getVersion()));
-		addAttribute("timestamp", relation.getFormattedTimestamp(getTimestampFormat()));
-		
-		if (!user.equals(OsmUser.NONE)) {
-			addAttribute("uid", Integer.toString(user.getId()));
-			addAttribute("user", user.getName());
-		}
-		
-		if (relation.getChangesetId() != 0) {
-			addAttribute("changeset", Long.toString(relation.getChangesetId()));
-		}
-		
-		relationMembers = relation.getMembers();
-		tags = relation.getTags();
-		
-		if (relationMembers.size() > 0 || tags.size() > 0) {
-			endOpenElement(false);
-
-			for (RelationMember relationMember : relationMembers) {
-				relationMemberWriter.processRelationMember(relationMember);
-			}
-			
-			for (Tag tag : tags) {
-				tagWriter.process(tag);
-			}
-			
-			closeElement();
-			
-		} else {
-			endOpenElement(true);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setWriter(final Writer writer) {
-		super.setWriter(writer);
-		
-		relationMemberWriter.setWriter(writer);
-		tagWriter.setWriter(writer);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java
deleted file mode 100644
index 3052bd4..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/WayWriter.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import java.io.Writer;
-import java.util.Collection;
-import java.util.List;
-
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-import org.openstreetmap.osmosis.xml.common.ElementWriter;
-
-
-/**
- * Renders a way as xml.
- *
- * @author Brett Henderson
- */
-public class WayWriter extends ElementWriter {
-    /**
-     * Write the ordered list of node-references of a way.
-     */
-    private WayNodeWriter wayNodeWriter;
-    /**
-     * Write the tags of a way.
-     */
-    private TagWriter tagWriter;
-
-
-	/**
-	 * Creates a new instance.
-	 * 
-	 * @param elementName
-	 *            The name of the element to be written.
-	 * @param indentLevel
-	 *            The indent level of the element.
-	 */
-	public WayWriter(String elementName, int indentLevel) {
-		super(elementName, indentLevel);
-		
-		tagWriter = new TagWriter("tag", indentLevel + 1);
-		wayNodeWriter = new WayNodeWriter("nd", indentLevel + 1);
-	}
-	
-	
-	/**
-	 * Writes the way.
-	 * 
-	 * @param way
-	 *            The way to be processed.
-	 */
-	public void process(Way way) {
-		OsmUser user;
-		List<WayNode> wayNodes;
-		Collection<Tag> tags;
-		
-		user = way.getUser();
-		
-		beginOpenElement();
-		addAttribute("id", Long.toString(way.getId()));
-		addAttribute("version", Integer.toString(way.getVersion()));
-		addAttribute("timestamp", way.getFormattedTimestamp(getTimestampFormat()));
-		
-		if (!user.equals(OsmUser.NONE)) {
-			addAttribute("uid", Integer.toString(user.getId()));
-			addAttribute("user", user.getName());
-		}
-		
-		if (way.getChangesetId() != 0) {
-			addAttribute("changeset", Long.toString(way.getChangesetId()));
-		}
-		
-		wayNodes = way.getWayNodes();
-		tags = way.getTags();
-		
-		if (wayNodes.size() > 0 || tags.size() > 0) {
-			endOpenElement(false);
-
-			for (WayNode wayNode : wayNodes) {
-				wayNodeWriter.processWayNode(wayNode);
-			}
-			
-			for (Tag tag : tags) {
-				tagWriter.process(tag);
-			}
-			
-			closeElement();
-			
-		} else {
-			endOpenElement(true);
-		}
-	}
-	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void setWriter(final Writer writer) {
-		super.setWriter(writer);
-		
-		wayNodeWriter.setWriter(writer);
-		tagWriter.setWriter(writer);
-	}
-}
diff --git a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java b/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java
deleted file mode 100644
index 175eb8d..0000000
--- a/xml/src/main/java/org/openstreetmap/osmosis/xml/v0_6/impl/XmlConstants.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-
-/**
- * Defines some common constants shared between various xml processing classes.
- * 
- * @author Brett Henderson
- */
-public final class XmlConstants {
-	
-	/**
-	 * This class cannot be instantiated.
-	 */
-	private XmlConstants() {
-	}
-	
-	
-	/**
-	 * Defines the version number to be stored in osm xml files. This number
-	 * will also be applied to osmChange files.
-	 */
-	public static final String OSM_VERSION = "0.6";
-	
-	
-	/**
-	 * The default URL for the production API.
-	 */
-	public static final String DEFAULT_URL = "http://www.openstreetmap.org/api/0.6";
-}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java b/xml/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
deleted file mode 100644
index 737d24e..0000000
--- a/xml/src/test/java/org/openstreetmap/osmosis/test/task/v0_6/SinkEntityInspector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.test.task.v0_6;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
-import org.openstreetmap.osmosis.core.task.v0_6.Sink;
-
-/**
- * Mock object for inspecting the resulting entities after passing through a pipeline task.
- * 
- * @author Karl Newman
- */
-public class SinkEntityInspector implements Sink {
-
-	private List<EntityContainer> processedEntities;
-	
-	
-	/**
-	 * Creates a new instance.
-	 */
-	public SinkEntityInspector() {
-		processedEntities = new LinkedList<EntityContainer>();
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void complete() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Catch all passed entities and save them for later inspection.
-	 * 
-	 * @param entityContainer
-	 *            The entity to be processed.
-	 */
-	@Override
-	public void process(EntityContainer entityContainer) {
-		processedEntities.add(entityContainer);
-	}
-
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void release() {
-		// Nothing to do here
-	}
-
-
-	/**
-	 * Shortcut method if you only care about the most recent EntityContainer.
-	 * 
-	 * @return the lastEntityContainer
-	 */
-	public EntityContainer getLastEntityContainer() {
-		if (processedEntities.isEmpty()) {
-			return null;
-		} else {
-			return processedEntities.get(processedEntities.size() - 1);
-		}
-	}
-
-
-	/**
-	 * Retrieve an Iterable of all the processed EntityContainers.
-	 * 
-	 * @return the processedEntities
-	 */
-	public Iterable<EntityContainer> getProcessedEntities() {
-		return Collections.unmodifiableList(processedEntities);
-	}
-
-}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java b/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java
deleted file mode 100644
index 79f2fc6..0000000
--- a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/XmlChangeReaderWriterTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.junit.Test;
-import org.openstreetmap.osmosis.testutil.AbstractDataTest;
-import org.openstreetmap.osmosis.xml.common.CompressionMethod;
-
-
-/**
- * A simple test verifying the operation of the xml change reader and change
- * writer tasks.
- * 
- * @author Brett Henderson
- */
-public class XmlChangeReaderWriterTest extends AbstractDataTest {
-	
-	/**
-	 * A basic test reading and writing an osm file testing both reader and
-	 * writer tasks.
-	 * 
-	 * @throws IOException
-	 *             if any file operations fail.
-	 */
-	@Test
-	public void testSimple() throws IOException {
-		XmlChangeReader xmlReader;
-		XmlChangeWriter xmlWriter;
-		File inputFile;
-		File outputFile;
-		
-		inputFile = dataUtils.createDataFile("v0_6/xml-task-tests-v0_6.osc");
-		outputFile = dataUtils.newFile();
-		
-		// Create and connect the xml tasks.
-		xmlReader = new XmlChangeReader(inputFile, true, CompressionMethod.None);
-		xmlWriter = new XmlChangeWriter(outputFile, CompressionMethod.None);
-		xmlReader.setChangeSink(xmlWriter);
-		
-		// Process the xml.
-		xmlReader.run();
-		
-		// Validate that the output file matches the input file.
-		dataUtils.compareFiles(inputFile, outputFile);
-	}
-}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java b/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java
deleted file mode 100644
index a50a3c4..0000000
--- a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/BoundWriterTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-
-/**
- * Tests the Bound class implementation.
- */
-public class BoundWriterTest {
-
-	private StringWriter testWriter;
-	private BufferedWriter testBufferedWriter;
-
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		testWriter = new StringWriter();
-		testBufferedWriter = new BufferedWriter(testWriter);
-	}
-
-
-	/**
-	 * Performs post-test activities.
-	 * 
-	 * @throws IOException
-	 *             if stream cleanup fails.
-	 */
-	@After
-	public void tearDown() throws IOException {
-		testBufferedWriter.close();
-		testWriter.close();
-	}
-
-
-	/**
-	 * Test writing out a normal Bound element. 
-	 */
-	@Test
-	public final void testProcess1() {
-		BoundWriter bw = new BoundWriter("bound", 2);
-		bw.setWriter(testBufferedWriter);
-		bw.process(new Bound(20.123456, -21.987654, 22.555555, -23.234567, "originstring"));
-		try {
-			testBufferedWriter.flush();
-		} catch (IOException e) {
-			e.printStackTrace();
-			fail("IOException");
-		}
-		// If this test fails, it could be because the regex has broken. There are a number of
-		// variations which are valid XML which this regex won't catch. It might need any number of
-		// \\s* to account for variable whitespace.
-		String regexMatch = "^\\s*<bound\\s*"
-		        + "box=['\"]-23.23457,-21.98765,22.55556,20.12346['\"]\\s*"
-		        + "origin=['\"]originstring['\"]/>\\s*$";
-		assertTrue(testWriter.toString().matches(regexMatch));
-	}
-
-	
-	/**
-	 * Test non-writing of a Bound element with an empty origin string. 
-	 */
-	@Test
-	public final void testProcess2() {
-		BoundWriter bw = new BoundWriter("bound", 2);
-		bw.setWriter(testBufferedWriter);
-		bw.process(new Bound(20.123456, -21.987654, 22.555555, -23.234567, ""));
-		try {
-			testBufferedWriter.flush();
-		} catch (IOException e) {
-			e.printStackTrace();
-			fail("IOException");
-		}
-		assertTrue(testWriter.toString().equals("")); // not written; empty string
-	}
-}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java b/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java
deleted file mode 100644
index c445880..0000000
--- a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmHandlerTest.java
+++ /dev/null
@@ -1,236 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.test.task.v0_6.SinkEntityInspector;
-
-
-/**
- * Not sure how to go about unit testing this. The individual parser classes seem to require a lot
- * of infrastructure, so this test will just set up the full parser to parse an XML string and check
- * the produced entities.
- * 
- * @author Karl Newman
- * 
- */
-public class OsmHandlerTest {
-
-	private SAXParser parser;
-	private SinkEntityInspector entityInspector;
-	private static final String OSM_PREFIX = "<osm version=\"0.6\">\n";
-	private static final String OSM_SUFFIX = "</osm>";
-
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		entityInspector = new SinkEntityInspector();
-		try {
-			parser = SAXParserFactory.newInstance().newSAXParser();
-		} catch (ParserConfigurationException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Unable to create SAX Parser.", e);
-		}
-	}
-
-
-	private void parseString(String input) {
-		InputStream inputStream = null;
-		try {
-			inputStream = new ByteArrayInputStream(input.getBytes("UTF-8"));
-			parser.parse(inputStream, new OsmHandler(entityInspector, true));
-		} catch (UnsupportedEncodingException e) {
-			throw new OsmosisRuntimeException("String encoding exception", e);
-		} catch (SAXException e) {
-			throw new OsmosisRuntimeException("Parse exception", e);
-		} catch (IOException e) {
-			throw new OsmosisRuntimeException("IOException", e);
-		} finally {
-			try {
-				if (inputStream != null) {
-					inputStream.close();
-				}
-			} catch (IOException e) {
-				throw new OsmosisRuntimeException("IOException", e);
-			} finally {
-				inputStream = null;
-			}
-		}
-	}
-
-
-	/**
-	 * Tests that an empty xml document can be parsed successfully.
-	 */
-	@Test
-	public final void testEmptyDocument() {
-		parseString(OSM_PREFIX + OSM_SUFFIX);
-		assertNull(entityInspector.getLastEntityContainer());
-	}
-
-
-	/**
-	 * Test a normal, well-formed bound element.
-	 */
-	@Test
-	public final void testBoundElement1() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		Bound b = (Bound) entityInspector.getLastEntityContainer().getEntity();
-		assertTrue(Double.compare(b.getRight(), 45.67891) == 0
-		        && Double.compare(b.getLeft(), -23.45678) == 0
-		        && Double.compare(b.getTop(), 34.56789) == 0
-		        && Double.compare(b.getBottom(), -12.34567) == 0);
-		assertTrue(b.getOrigin().equals("someorigin"));
-	}
-
-
-	/**
-	 * Test a malformed box attribute for a bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement2() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12.34567,-23.45678,34.56789\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a missing box attribute of a bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement3() {
-		parseString(OSM_PREFIX + "<bound origin=\"someorigin\"/>" + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a number parse error for a box attribute of a bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement4() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12..34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a missing origin attribute of a bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement5() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test an empty origin attribute of a bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement6() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a repeated bound element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement7() {
-		parseString(OSM_PREFIX
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someotherorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a bound element occurring after a node element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement8() {
-		parseString(OSM_PREFIX
-		        + "<node id=\"12345\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
-		        + "timestamp=\"2008-01-01T15:32:01\" lat=\"-12.34567\" lon=\"-23.45678\"/>"
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a bound element occurring after a way element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement9() {
-		parseString(OSM_PREFIX
-		        + "<way id=\"12346\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
-		        + "timestamp=\"2008-01-01T15:32:01\">"
-		        + "<nd ref=\"12345\"/>"
-		        + "<nd ref=\"12347\"/>"
-		        + "</way>"
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-
-
-	/**
-	 * Test a bound element occurring after a relation element.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testBoundElement10() {
-		parseString(OSM_PREFIX
-		        + "<relation id=\"12348\" user=\"OsmosisTest\" uid=\"12\" version=\"0\""
-		        + "timestamp=\"2008-01-01T15:32:01\">"
-		        + "<member ref=\"12345\" type=\"node\" role=\"node1\"/>"
-		        + "<member ref=\"12346\" type=\"way\" role=\"way1\"/>"
-		        + "</relation>"
-		        + "<bound box=\"-12.34567,-23.45678,34.56789,45.67891\""
-		        + " origin=\"someorigin\"/>"
-		        + OSM_SUFFIX);
-		fail("Expected to throw an exception");
-	}
-}
diff --git a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java b/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java
deleted file mode 100644
index 50e1205..0000000
--- a/xml/src/test/java/org/openstreetmap/osmosis/xml/v0_6/impl/OsmWriterTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-// This software is released into the Public Domain.  See copying.txt for details.
-package org.openstreetmap.osmosis.xml.v0_6.impl;
-
-import static org.junit.Assert.fail;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Date;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
-import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
-import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
-import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
-import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
-import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
-import org.openstreetmap.osmosis.core.domain.v0_6.Node;
-import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
-import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
-import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
-import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
-import org.openstreetmap.osmosis.core.domain.v0_6.Way;
-import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
-
-
-/**
- * Tests the XML osm element writer implementation.
- */
-public class OsmWriterTest {
-
-	private StringWriter testWriter;
-	private BufferedWriter testBufferedWriter;
-	private OsmWriter testOsmWriter;
-
-
-	/**
-	 * Performs pre-test activities.
-	 */
-	@Before
-	public void setUp() {
-		testWriter = new StringWriter();
-		testBufferedWriter = new BufferedWriter(testWriter);
-		testOsmWriter = new OsmWriter("osm", 0, true);
-		testOsmWriter.setWriter(testBufferedWriter);
-	}
-
-
-	/**
-	 * Performs post-test activities.
-	 * 
-	 * @throws IOException
-	 *             if IO stream cleanup fails.
-	 */
-	@After
-	public void tearDown() throws IOException {
-		testBufferedWriter.close();
-		testWriter.close();
-		testOsmWriter = null;
-	}
-
-
-	/**
-	 * Test processing a single Bound entity.
-	 */
-	@Test
-	public final void testProcess1() {
-		testOsmWriter.process(new BoundContainer(new Bound("source")));
-		// Nothing to assert; just expect no exception
-	}
-
-
-	/**
-	 * Test processing a repeated Bound entity.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testProcess2() {
-		testOsmWriter.process(new BoundContainer(new Bound("source")));
-		testOsmWriter.process(new BoundContainer(new Bound("source2")));
-		fail("Expected to throw an exception.");
-	}
-
-
-	/**
-	 * Test processing a Node entity.
-	 */
-	@Test
-	public final void testProcess3() {
-		testOsmWriter.process(
-				new NodeContainer(
-					new Node(
-						new CommonEntityData(
-								1234, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0, new ArrayList<Tag>()),
-						20, 20)));
-		// Nothing to assert; just expect no exception
-	}
-
-
-	/**
-	 * Test processing a Bound after a Node.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testProcess4() {
-		testOsmWriter.process(new NodeContainer(
-				new Node(
-						new CommonEntityData(1234, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0,
-								new ArrayList<Tag>()),
-						20, 20)));
-		testOsmWriter.process(new BoundContainer(new Bound("source")));
-		fail("Expected to throw an exception.");
-	}
-
-
-	/**
-	 * Test processing a Way.
-	 */
-	@Test
-	public final void testProcess6() {
-		Way testWay;
-		
-		testWay = new Way(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
-		testWay.getWayNodes().add(new WayNode(1234));
-		testWay.getWayNodes().add(new WayNode(1235));
-		testWay.getTags().add(new Tag("test_key1", "test_value1"));
-		
-		testOsmWriter.process(new WayContainer(testWay));
-		// Nothing to assert; just expect no exception
-	}
-
-
-	/**
-	 * Test processing a Bound after a Way.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testProcess7() {
-		Way testWay;
-		
-		testWay = new Way(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
-		testWay.getWayNodes().add(new WayNode(1234));
-		testWay.getWayNodes().add(new WayNode(1235));
-		testWay.getTags().add(new Tag("test_key1", "test_value1"));
-		
-		testOsmWriter.process(new WayContainer(testWay));
-		testOsmWriter.process(new BoundContainer(new Bound("source")));
-	}
-
-
-	/**
-	 * Test processing a Relation.
-	 */
-	@Test
-	public final void testProcess8() {
-		Relation testRelation;
-		
-		testRelation = new Relation(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
-		testRelation.getMembers().add(new RelationMember(1234, EntityType.Node, "role1"));
-		testRelation.getTags().add(new Tag("test_key1", "test_value1"));
-		
-		testOsmWriter.process(new RelationContainer(testRelation));
-		// Nothing to assert; just expect no exception
-	}
-
-	
-	/**
-	 * Test processing a Bound after a Relation.
-	 */
-	@Test(expected = OsmosisRuntimeException.class)
-	public final void testProcess9() {
-		Relation testRelation;
-		
-		testRelation = new Relation(new CommonEntityData(3456, 0, new Date(), new OsmUser(12, "OsmosisTest"), 0));
-		testRelation.getMembers().add(new RelationMember(1234, EntityType.Node, "role1"));
-		testRelation.getTags().add(new Tag("test_key1", "test_value1"));
-		
-		testOsmWriter.process(new RelationContainer(testRelation));
-		testOsmWriter.process(new BoundContainer(new Bound("source")));
-	}
-}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osmosis.git



More information about the Pkg-grass-devel mailing list