package com.revolsys.record.io.format.saif; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.zip.ZipFile; import com.revolsys.record.Record; import com.revolsys.record.RecordFactory; import com.revolsys.record.io.format.saif.util.OsnConverter; import com.revolsys.record.io.format.saif.util.OsnConverterRegistry; import com.revolsys.record.io.format.saif.util.OsnIterator; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordDefinitionFactory; import com.revolsys.record.schema.RecordDefinitionImpl; public class OsnReader implements Iterator<Record>, Closeable { private final OsnConverterRegistry converters; private File directory; private boolean endOfFile = false; private RecordFactory factory; private final String fileName; private boolean nextChecked = false; private OsnIterator osnIterator; private final RecordDefinitionFactory recordDefinitionFactory; private ZipFile zipFile; public OsnReader(final RecordDefinitionFactory recordDefinitionFactory, final File directory, final String fileName, final int srid) throws IOException { this.recordDefinitionFactory = recordDefinitionFactory; this.directory = directory; this.fileName = fileName; this.converters = new OsnConverterRegistry(srid); open(); } public OsnReader(final RecordDefinitionFactory recordDefinitionFactory, final ZipFile zipFile, final String fileName, final int srid) throws IOException { this.recordDefinitionFactory = recordDefinitionFactory; this.fileName = fileName; this.zipFile = zipFile; this.converters = new OsnConverterRegistry(srid); open(); } /** * Get an attribute definition from the iterator. * * @param record * @param typePath TODO * @param iterator The OsnIterator. * @return The attribute definition. * @throws IOException If an I/O error occurs. */ private void addField(final Record record) { if (this.osnIterator.getEventType() != OsnIterator.START_ATTRIBUTE) { if (this.osnIterator.next() != OsnIterator.START_ATTRIBUTE) { this.osnIterator.throwParseError("Excepecting an attribute name"); } } final String name = this.osnIterator.getStringValue(); final Object value = getExpression(); record.setValue(name, value); } @Override public void close() { try { this.osnIterator.close(); } catch (final IOException e) { throw new RuntimeException(e.getMessage(), e); } } /** * Get the value of an expression from the iterator. * * @return The value of the expression. * @throws IOException If an I/O error occurs. */ private Object getExpression() { final Object eventType = this.osnIterator.next(); if (eventType == OsnIterator.START_DEFINITION) { return getRecord(); } else if (eventType == OsnIterator.START_SET) { final Set<Object> set = new LinkedHashSet<>(); processCollection(set, OsnIterator.END_SET); return set; } else if (eventType == OsnIterator.START_LIST) { final List<Object> list = new ArrayList<>(); processCollection(list, OsnIterator.END_LIST); return list; } else if (eventType == OsnIterator.TEXT_VALUE) { return this.osnIterator.getValue(); } else if (eventType == OsnIterator.NUMERIC_VALUE) { return this.osnIterator.getValue(); } else if (eventType == OsnIterator.BOOLEAN_VALUE) { return this.osnIterator.getValue(); } else if (eventType == OsnIterator.ENUM_TAG) { return this.osnIterator.getValue(); } else if (eventType == OsnIterator.UNKNOWN) { this.osnIterator.throwParseError("Expected an expression"); } return null; } /** * @return the factory */ public RecordFactory getFactory() { return this.factory; } /** * @return the fileName */ public String getFileName() { return this.fileName; } private Object getRecord() { final String typePath = this.osnIterator.getPathValue(); final OsnConverter converter = this.converters.getConverter(typePath); if (converter != null) { return converter.read(this.osnIterator); } else { final RecordDefinition type = this.recordDefinitionFactory.getRecordDefinition(typePath); final Record record = this.factory.newRecord(type); while (this.osnIterator.next() != OsnIterator.END_OBJECT) { addField(record); } return record; } } public RecordDefinitionFactory getRecordDefinitionFactory() { return this.recordDefinitionFactory; } @Override public boolean hasNext() { if (this.nextChecked) { return true; } else if (this.endOfFile) { return false; } else { if (this.osnIterator.hasNext()) { Object eventType = this.osnIterator.getEventType(); if (eventType != OsnIterator.START_DEFINITION) { eventType = this.osnIterator.next(); } if (eventType == OsnIterator.START_DEFINITION) { this.nextChecked = true; return true; } else if (eventType != OsnIterator.END_DOCUMENT && eventType != OsnIterator.END_SET) { this.osnIterator.throwParseError("Excepecting start of an object"); } } } this.endOfFile = true; return false; } @Override public Record next() { if (hasNext()) { this.nextChecked = false; return (Record)getRecord(); } else { throw new NoSuchElementException(); } } public void open() { try { if (this.directory != null) { this.osnIterator = new OsnIterator(this.directory, this.fileName); } else { this.osnIterator = new OsnIterator(this.zipFile, this.fileName); } skipToFirstRecord(); } catch (final IOException e) { throw new RuntimeException(e.getMessage(), e); } } /** * Process a collection, loading all the values into the collection. * * @param collection The collection to save the objects to. * @param endEventType The event type indicating the end of a collection. * @throws IOException If an I/O error occurs. */ private void processCollection(final Collection<Object> collection, final Object endEventType) { while (this.osnIterator.getEventType() != endEventType) { final Object value = getExpression(); if (value != null || this.osnIterator.getEventType() == OsnIterator.NULL_VALUE) { collection.add(value); } } } @Override public void remove() { throw new UnsupportedOperationException("Removing SAIF objects is not supported"); } /** * @param factory the factory to set */ public void setFactory(final RecordFactory factory) { this.factory = factory; } /** * Skip all objects and attributes until the first object in the collection. * * @return True if an object was found. * @throws IOException If an I/O error occurs. */ private boolean skipToFirstRecord() throws IOException { if (this.osnIterator.next() == OsnIterator.START_DEFINITION) { final String typePath = this.osnIterator.getPathValue(); final RecordDefinitionImpl type = (RecordDefinitionImpl)this.recordDefinitionFactory .getRecordDefinition(typePath); final RecordDefinition spatialDataSetType = this.recordDefinitionFactory .getRecordDefinition("/SpatialDataSet"); if (type != null && type.isInstanceOf(spatialDataSetType)) { final String oiName = this.osnIterator.nextFieldName(); if (oiName != null && oiName.equals("objectIdentifier")) { this.osnIterator.nextStringValue(); final String fieldName = this.osnIterator.nextFieldName(); if (fieldName != null && (fieldName.equals("geoComponents") || fieldName.equals("annotationComponents"))) { if (this.osnIterator.next() == OsnIterator.START_SET) { return true; } else { this.osnIterator.throwParseError("Expecting a set of objects"); } } else { this.osnIterator.throwParseError("Excepecting the 'geoComponents' attribute"); } } else { this.osnIterator.throwParseError("Expecting the 'objectIdentifier' attribute"); } } else { return true; } } else { this.osnIterator.throwParseError("Expecting a start of an object definition"); } return false; } @Override public String toString() { return this.fileName; } }