// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.pgsimple.v0_6.impl; import java.sql.CallableStatement; import java.sql.SQLException; import java.util.HashSet; import java.util.Set; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.database.ReleasableStatementContainer; 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.pgsimple.common.DatabaseContext; import org.openstreetmap.osmosis.pgsimple.common.NoSuchRecordException; import org.openstreetmap.osmosis.core.task.common.ChangeAction; /** * 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() { CallableStatement updateStatement; try (ReleasableStatementContainer statementContainer = new ReleasableStatementContainer()) { updateStatement = statementContainer.add(dbCtx.prepareCall("{call osmosisUpdate()}")); updateStatement.executeUpdate(); } catch (SQLException e) { throw new OsmosisRuntimeException("Unable to invoke the osmosis update stored function.", e); } // Clear all action records. actionDao.truncate(); } /** * Releases all resources. */ public void release() { } }