package com.revolsys.gis.esri.gdb.file;
import java.util.NoSuchElementException;
import com.revolsys.collection.iterator.AbstractIterator;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.gis.esri.gdb.file.capi.swig.Row;
import com.revolsys.gis.esri.gdb.file.capi.swig.Table;
import com.revolsys.gis.esri.gdb.file.capi.type.AbstractFileGdbFieldDefinition;
import com.revolsys.gis.esri.gdb.file.convert.GeometryConverter;
import com.revolsys.logging.Logs;
import com.revolsys.record.Record;
import com.revolsys.record.RecordFactory;
import com.revolsys.record.RecordState;
import com.revolsys.record.io.RecordReader;
import com.revolsys.record.property.FieldProperties;
import com.revolsys.record.query.Query;
import com.revolsys.record.schema.FieldDefinition;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.util.Strings;
import com.revolsys.util.count.LabelCountMap;
public class FileGdbQueryIterator extends AbstractIterator<Record> implements RecordReader {
private BoundingBox boundingBox;
private final String catalogPath;
private int count;
private String fields;
private int limit = Integer.MAX_VALUE;
private int offset;
private RecordDefinition recordDefinition;
private RecordFactory<Record> recordFactory;
private FileGdbRecordStore recordStore;
private FileGdbEnumRowsIterator rows;
private String sql;
private LabelCountMap labelCountMap;
private Table table;
private boolean closed = false;
FileGdbQueryIterator(final FileGdbRecordStore recordStore, final String catalogPath) {
this(recordStore, catalogPath, "*", "", null, 0, -1);
}
FileGdbQueryIterator(final FileGdbRecordStore recordStore, final String catalogPath,
final String whereClause) {
this(recordStore, catalogPath, "*", whereClause, null, 0, -1);
}
FileGdbQueryIterator(final FileGdbRecordStore recordStore, final String catalogPath,
final String whereClause, final BoundingBox boundingBox, final Query query, final int offset,
final int limit) {
this(recordStore, catalogPath, "*", whereClause, boundingBox, offset, limit);
final RecordFactory<Record> recordFactory = query.getRecordFactory();
if (recordFactory != null) {
this.recordFactory = recordFactory;
}
}
FileGdbQueryIterator(final FileGdbRecordStore recordStore, final String catalogPath,
final String fields, final String sql, final BoundingBox boundingBox, final int offset,
final int limit) {
this.catalogPath = catalogPath;
this.sql = sql;
this.recordDefinition = recordStore.getRecordDefinition(catalogPath);
if (this.recordDefinition == null) {
this.closed = true;
} else {
this.recordStore = recordStore;
this.table = recordStore.getTable(this.recordDefinition);
if ("*".equals(fields)) {
this.fields = Strings.toString(this.recordDefinition.getFieldNames());
} else {
this.fields = fields;
}
setBoundingBox(boundingBox);
this.recordFactory = recordStore.getRecordFactory();
this.offset = offset;
if (limit >= 0) {
this.limit = limit;
}
}
}
@Override
protected void closeDo() {
boolean close = true;
if (this.closed || this.recordStore == null) {
close = false;
} else {
this.closed = true;
}
if (close) {
synchronized (this) {
if (this.recordDefinition != null) {
this.recordDefinition = null;
try {
try {
if (this.rows != null) {
this.rows.close();
}
} finally {
this.recordStore.releaseTable(this.catalogPath);
}
} catch (final Throwable e) {
Logs.error(this, "Error closing query: " + this.catalogPath, e);
} finally {
this.boundingBox = null;
this.recordStore = null;
this.fields = null;
this.rows = null;
this.sql = null;
this.table = null;
}
}
}
}
}
@Override
protected synchronized Record getNext() throws NoSuchElementException {
final FileGdbRecordStore recordStore = this.recordStore;
final FileGdbEnumRowsIterator rows = this.rows;
if (rows == null || this.closed) {
throw new NoSuchElementException();
} else {
Row row = null;
while (this.offset > 0 && this.count < this.offset) {
row = rows.next();
this.count++;
if (this.closed) {
throw new NoSuchElementException();
}
}
if (this.count - this.offset >= this.limit) {
throw new NoSuchElementException();
}
row = rows.next();
this.count++;
try {
final Record record = this.recordFactory.newRecord(this.recordDefinition);
if (this.labelCountMap == null) {
recordStore.addStatistic("query", record);
} else {
this.labelCountMap.addCount(record);
}
record.setState(RecordState.INITIALIZING);
for (final FieldDefinition field : this.recordDefinition.getFields()) {
final String name = field.getName();
final AbstractFileGdbFieldDefinition esriFieldDefinition = (AbstractFileGdbFieldDefinition)field;
final Object value = esriFieldDefinition.getValue(row);
record.setValue(name, value);
if (this.closed) {
throw new NoSuchElementException();
}
}
record.setState(RecordState.PERSISTED);
if (this.closed) {
throw new NoSuchElementException();
}
return record;
} catch (final RuntimeException e) {
if (this.closed) {
throw new NoSuchElementException();
} else {
throw e;
}
} finally {
row.delete();
}
}
}
@Override
public RecordDefinition getRecordDefinition() {
if (this.recordDefinition == null) {
hasNext();
}
return this.recordDefinition;
}
public LabelCountMap getStatistics() {
return this.labelCountMap;
}
@Override
protected synchronized void initDo() {
if (!this.closed) {
synchronized (this.recordStore.getApiSync()) {
if (this.boundingBox == null) {
if (this.sql.startsWith("SELECT")) {
this.rows = this.recordStore.query(this.sql, true);
} else {
this.rows = this.recordStore.search(this.catalogPath, this.table, this.fields, this.sql,
true);
}
} else {
BoundingBox boundingBox = this.boundingBox;
if (boundingBox.getWidth() == 0) {
boundingBox = boundingBox.expand(1, 0);
}
if (boundingBox.getHeight() == 0) {
boundingBox = boundingBox.expand(0, 1);
}
final com.revolsys.gis.esri.gdb.file.capi.swig.Envelope envelope = GeometryConverter
.toEsri(boundingBox);
String sql = this.sql;
if ("1 = 1".equals(sql)) {
sql = "";
}
this.rows = this.recordStore.search(this.catalogPath, this.table, this.fields, sql,
envelope, true);
}
if (this.rows == null) {
close();
}
}
}
}
public void setBoundingBox(final BoundingBox boundingBox) {
final RecordDefinition recordDefinition = this.recordDefinition;
if (recordDefinition != null) {
this.boundingBox = boundingBox;
if (boundingBox != null) {
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField != null) {
final GeometryFactory geometryFactory = geometryField
.getProperty(FieldProperties.GEOMETRY_FACTORY);
if (geometryFactory != null) {
this.boundingBox = boundingBox.convert(geometryFactory);
}
}
}
}
}
public void setStatistics(final LabelCountMap labelCountMap) {
this.labelCountMap = labelCountMap;
}
@Override
public String toString() {
return this.catalogPath + " " + this.sql;
}
}