// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.pgsimple.v0_6.impl;
import java.util.NoSuchElementException;
import org.openstreetmap.osmosis.core.database.DbFeature;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator;
import org.openstreetmap.osmosis.core.store.PeekableIterator;
import org.openstreetmap.osmosis.core.store.PersistentIterator;
import org.openstreetmap.osmosis.core.store.SingleClassObjectSerializationFactory;
import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext;
/**
* Reads instances of an entity type from a database ordered by the identifier.
* It combines the output of the entity feature table readers to produce fully
* configured entity objects.
*
* @author Brett Henderson
* @param <T>
* The entity type to be supported.
*/
public class EntityReader<T extends Entity> implements ReleasableIterator<T> {
private ReleasableIterator<T> entityReader;
private PeekableIterator<DbFeature<Tag>> entityTagReader;
private T nextValue;
private boolean nextValueLoaded;
/**
* Creates a new instance.
*
* @param dbCtx
* The database context to use for accessing the database.
* @param entityMapper
* The database mapper for the entity type.
*/
public EntityReader(DatabaseContext dbCtx, EntityMapper<T> entityMapper) {
// The postgres jdbc driver doesn't appear to allow concurrent result
// sets on the same connection so only the last opened result set may be
// streamed. The rest of the result sets must be persisted first.
entityReader = new PersistentIterator<T>(
new SingleClassObjectSerializationFactory(entityMapper.getEntityClass()),
new EntityTableReader<T>(dbCtx, entityMapper),
"ent",
true
);
entityTagReader = new PeekableIterator<DbFeature<Tag>>(
new PersistentIterator<DbFeature<Tag>>(
new SingleClassObjectSerializationFactory(DbFeature.class),
new EntityFeatureTableReader<Tag, DbFeature<Tag>>(dbCtx, new TagMapper(entityMapper.getEntityName())),
"enttag",
true
)
);
}
/**
* Creates a new instance.
*
* @param dbCtx
* The database context to use for accessing the database.
* @param entityMapper
* The database mapper for the entity type.
* @param constraintTable
* The table containing a column named id defining the list of
* entities to be returned.
*/
public EntityReader(DatabaseContext dbCtx, EntityMapper<T> entityMapper, String constraintTable) {
// The postgres jdbc driver doesn't appear to allow concurrent result
// sets on the same connection so only the last opened result set may be
// streamed. The rest of the result sets must be persisted first.
entityReader = new PersistentIterator<T>(
new SingleClassObjectSerializationFactory(entityMapper.getEntityClass()),
new EntityTableReader<T>(dbCtx, entityMapper, constraintTable),
"nod",
true
);
entityTagReader = new PeekableIterator<DbFeature<Tag>>(
new PersistentIterator<DbFeature<Tag>>(
new SingleClassObjectSerializationFactory(DbFeature.class),
new EntityFeatureTableReader<Tag, DbFeature<Tag>>(
dbCtx, new TagMapper(entityMapper.getEntityName()), constraintTable),
"enttag",
true
)
);
}
/**
* Populates the entity with the its features. These features will be read
* from related tables.
*
* @param entity
* The entity to be populated.
*/
protected void populateEntityFeatures(T entity) {
long entityId;
entityId = entity.getId();
// Skip all tags that are from a lower entity.
while (entityTagReader.hasNext()) {
DbFeature<Tag> dbTag;
dbTag = entityTagReader.peekNext();
if (dbTag.getEntityId() < entityId) {
entityTagReader.next();
} else {
break;
}
}
// Load all tags matching this version of the node.
while (entityTagReader.hasNext() && entityTagReader.peekNext().getEntityId() == entityId) {
entity.getTags().add(entityTagReader.next().getFeature());
}
}
/**
* {@inheritDoc}
*/
public boolean hasNext() {
if (!nextValueLoaded && entityReader.hasNext()) {
T entity;
entity = entityReader.next();
populateEntityFeatures(entity);
nextValue = entity;
nextValueLoaded = true;
}
return nextValueLoaded;
}
/**
* {@inheritDoc}
*/
public T next() {
T result;
if (!hasNext()) {
throw new NoSuchElementException();
}
result = nextValue;
nextValueLoaded = false;
return result;
}
/**
* {@inheritDoc}
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public void close() {
entityReader.close();
entityTagReader.close();
}
}