| | |
| | | |
| | | //~--- JDK imports ------------------------------------------------------------ |
| | | |
| | | import javax.swing.JFileChooser; |
| | | import javax.swing.JFrame; |
| | | import java.io.EOFException; |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | |
| | | import java.nio.MappedByteBuffer; |
| | | import java.nio.channels.FileChannel; |
| | | import java.nio.channels.ReadableByteChannel; |
| | | import javax.swing.JFileChooser; |
| | | import javax.swing.JFrame; |
| | | |
| | | import org.apache.log4j.LogManager; |
| | | import org.apache.log4j.Logger; |
| | |
| | | * |
| | | * @author Ulysses |
| | | * @version 0.1 |
| | | * @since 2006/5/17 ¤U¤È 01:24:10 |
| | | * @since 2006/5/17 |
| | | */ |
| | | public class Dgn7fileReader |
| | | { |
| | | public class Dgn7fileReader { |
| | | private static final Logger logger = LogManager.getLogger(Dgn7fileReader.class); |
| | | |
| | | private Dgn7fileHeader header; |
| | |
| | | ByteBuffer buffer; |
| | | private ElementType fileElementType = ElementType.UNDEFINED; |
| | | private ByteBuffer headerTransfer; |
| | | private final Record record = new Record(); |
| | | private final Element.FileRecord record = new Element.FileRecord(); |
| | | private final boolean randomAccessEnabled; |
| | | private Lock lock; |
| | | private boolean useMemoryMappedBuffer; |
| | | private long currentOffset = 0L; |
| | | private StreamLogging streamLogger = new StreamLogging("Shapefile Reader"); |
| | | private StreamLogging streamLogger = new StreamLogging("Dgn7 Reader"); |
| | | private int maxElementId = 0; |
| | | |
| | | public Dgn7fileReader(ReadableByteChannel channel, boolean strict, boolean useMemoryMapped, Lock lock) |
| | | throws IOException, Dgn7fileException |
| | | { |
| | | public Dgn7fileReader(FileChannel channel, boolean strict, boolean useMemoryMapped, Lock lock) |
| | | throws IOException, Dgn7fileException { |
| | | this.channel = channel; |
| | | this.useMemoryMappedBuffer = useMemoryMapped; |
| | | streamLogger.open(); |
| | |
| | | init(strict); |
| | | } |
| | | |
| | | public Dgn7fileReader(ReadableByteChannel channel, Lock lock) throws IOException, Dgn7fileException |
| | | { |
| | | public Dgn7fileReader(FileChannel channel, Lock lock) throws IOException, Dgn7fileException { |
| | | this(channel, true, true, lock); |
| | | } |
| | | |
| | | // ensure the capacity of the buffer is of size by doubling the original |
| | | // capacity until it is big enough |
| | | // this may be naiive and result in out of MemoryError as implemented... |
| | | public static ByteBuffer ensureCapacity(ByteBuffer buffer, int size, boolean useMemoryMappedBuffer) |
| | | { |
| | | public static ByteBuffer ensureCapacity(ByteBuffer buffer, int size, boolean useMemoryMappedBuffer) { |
| | | // This sucks if you accidentally pass is a MemoryMappedBuffer of size |
| | | // 80M |
| | | // like I did while messing around, within moments I had 1 gig of |
| | | // swap... |
| | | if (buffer.isReadOnly() || useMemoryMappedBuffer) |
| | | { |
| | | if (buffer.isReadOnly() || useMemoryMappedBuffer) { |
| | | return buffer; |
| | | } |
| | | |
| | | int limit = buffer.limit(); |
| | | |
| | | while (limit < size) |
| | | { |
| | | while (limit < size) { |
| | | limit *= 2; |
| | | } |
| | | |
| | | if (limit != buffer.limit()) |
| | | { |
| | | if (limit != buffer.limit()) { |
| | | // if (record.ready) { |
| | | buffer = ByteBuffer.allocateDirect(limit); |
| | | |
| | |
| | | } |
| | | |
| | | // for filling a ReadableByteChannel |
| | | public static int fill(ByteBuffer buffer, ReadableByteChannel channel) throws IOException |
| | | { |
| | | public static int fill(ByteBuffer buffer, ReadableByteChannel channel) throws IOException { |
| | | int r = buffer.remaining(); |
| | | |
| | | // channel reads return -1 when EOF or other error |
| | | // because they a non-blocking reads, 0 is a valid return value!! |
| | | while ((buffer.remaining() > 0) && (r != -1)) |
| | | { |
| | | while ((buffer.remaining() > 0) && (r != -1)) { |
| | | r = channel.read(buffer); |
| | | } |
| | | |
| | | if (r == -1) |
| | | { |
| | | if (r == -1) { |
| | | buffer.limit(buffer.position()); |
| | | } |
| | | |
| | | return r; |
| | | } |
| | | |
| | | public static Dgn7fileHeader readHeader(ReadableByteChannel channel, boolean strict) throws IOException |
| | | { |
| | | public static Dgn7fileHeader readHeader(ReadableByteChannel channel, boolean strict) throws IOException { |
| | | ByteBuffer buffer = ByteBuffer.allocateDirect(4); |
| | | |
| | | if (fill(buffer, channel) == -1) |
| | | { |
| | | if (fill(buffer, channel) == -1) { |
| | | throw new EOFException("Premature end of header"); |
| | | } |
| | | |
| | |
| | | buffer = ByteBuffer.allocateDirect(length + 4); |
| | | buffer.put(old); |
| | | |
| | | if (fill(buffer, channel) == -1) |
| | | { |
| | | if (fill(buffer, channel) == -1) { |
| | | throw new EOFException("Premature end of header"); |
| | | } |
| | | |
| | |
| | | return header; |
| | | } |
| | | |
| | | public Dgn7fileHeader getHeader() |
| | | { |
| | | public Dgn7fileHeader getHeader() { |
| | | return header; |
| | | } |
| | | |
| | | public void close() throws IOException |
| | | { |
| | | public void close() throws IOException { |
| | | lock.unlockRead(); |
| | | |
| | | if (channel.isOpen()) |
| | | { |
| | | if (channel.isOpen()) { |
| | | channel.close(); |
| | | streamLogger.close(); |
| | | } |
| | | |
| | | if (buffer instanceof MappedByteBuffer) |
| | | { |
| | | if (buffer instanceof MappedByteBuffer) { |
| | | NIOUtilities.clean(buffer); |
| | | } |
| | | |
| | |
| | | header = null; |
| | | } |
| | | |
| | | public boolean supportsRandomAccess() |
| | | { |
| | | public boolean supportsRandomAccess() { |
| | | return randomAccessEnabled; |
| | | } |
| | | |
| | | public Record nextElement() throws IOException, Dgn7fileException |
| | | { |
| | | public Element.FileRecord nextElement() throws IOException, Dgn7fileException { |
| | | // need to update position |
| | | buffer.position(this.toBufferOffset(record.end)); |
| | | |
| | |
| | | // track the record location |
| | | int elementLength = (buffer.getShort() * 2) + 4; |
| | | |
| | | if (!buffer.isReadOnly() && !useMemoryMappedBuffer) |
| | | { |
| | | if (!buffer.isReadOnly() && !useMemoryMappedBuffer) { |
| | | // capacity is less than required for the record |
| | | // copy the old into the newly allocated |
| | | if (buffer.capacity() < elementLength) |
| | | { |
| | | if (buffer.capacity() < elementLength) { |
| | | this.currentOffset += buffer.position(); |
| | | |
| | | ByteBuffer old = buffer; |
| | |
| | | // remaining is less than record length |
| | | // compact the remaining data and read again, |
| | | // allowing enough room for one more record header |
| | | if (buffer.remaining() < elementLength) |
| | | { |
| | | if (buffer.remaining() < elementLength) { |
| | | this.currentOffset += buffer.position(); |
| | | buffer.compact(); |
| | | fill(buffer, channel); |
| | |
| | | // second guess them... |
| | | buffer.mark(); |
| | | |
| | | if (recordType.isMultiPoint()) |
| | | { |
| | | if (recordType.isMultiPoint()) { |
| | | int lowCoorX = buffer.getInt(); |
| | | |
| | | lowCoorX = DgnUtility.convertFromDGN(lowCoorX); |
| | |
| | | record.length = elementLength; |
| | | record.signature = signature; |
| | | record.number = recordNumber; |
| | | record.buffer = buffer; |
| | | |
| | | // remember, we read one int already... |
| | | record.end = this.toFileOffset(buffer.position()) + elementLength - 4; |
| | |
| | | return record; |
| | | } |
| | | |
| | | public void goTo(int offset) throws IOException, UnsupportedOperationException |
| | | { |
| | | if (randomAccessEnabled) |
| | | { |
| | | if (this.useMemoryMappedBuffer) |
| | | { |
| | | public void goTo(int offset) throws IOException, UnsupportedOperationException { |
| | | if (randomAccessEnabled) { |
| | | if (this.useMemoryMappedBuffer) { |
| | | buffer.position(offset); |
| | | } else |
| | | { |
| | | } else { |
| | | /* |
| | | * Check to see if requested offset is already loaded; ensure |
| | | * that record header is in the buffer |
| | | */ |
| | | if ((this.currentOffset <= offset) && (this.currentOffset + buffer.limit() >= offset + 4)) |
| | | { |
| | | if ((this.currentOffset <= offset) && (this.currentOffset + buffer.limit() >= offset + 4)) { |
| | | buffer.position(this.toBufferOffset(offset)); |
| | | } else |
| | | { |
| | | } else { |
| | | FileChannel fc = (FileChannel) this.channel; |
| | | |
| | | fc.position(offset); |
| | |
| | | |
| | | record.end = offset; |
| | | |
| | | try |
| | | { |
| | | try { |
| | | hasNext(); |
| | | } catch (IOException ioe) |
| | | { |
| | | } catch (IOException ioe) { |
| | | record.end = oldRecordOffset; |
| | | |
| | | throw ioe; |
| | | } |
| | | } else |
| | | { |
| | | } else { |
| | | throw new UnsupportedOperationException("Random Access not enabled"); |
| | | } |
| | | } |
| | | |
| | | public Record elementAt(int offset) throws IOException, UnsupportedOperationException, Dgn7fileException |
| | | { |
| | | if (randomAccessEnabled) |
| | | { |
| | | public Element.FileRecord elementAt(int offset) throws IOException, UnsupportedOperationException, Dgn7fileException { |
| | | if (randomAccessEnabled) { |
| | | this.goTo(offset); |
| | | |
| | | return nextElement(); |
| | |
| | | throw new UnsupportedOperationException("Random Access not enabled"); |
| | | } |
| | | |
| | | public boolean hasNext() throws IOException |
| | | { |
| | | public boolean hasNext() throws IOException { |
| | | // mark current position |
| | | int position = buffer.position(); |
| | | |
| | | // ensure the proper position, regardless of read or handler behavior |
| | | try |
| | | { |
| | | try { |
| | | buffer.position(this.toBufferOffset(record.end)); |
| | | } catch (IllegalArgumentException e) |
| | | { |
| | | } catch (IllegalArgumentException e) { |
| | | logger.warn("position=" + this.toBufferOffset(record.end), e); |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // no more data left |
| | | if (buffer.remaining() < 4) |
| | | { |
| | | if (buffer.remaining() < 4) { |
| | | return false; |
| | | } |
| | | |
| | |
| | | boolean hasNext = true; |
| | | short type = buffer.getShort(); |
| | | |
| | | if (type == -1) |
| | | { |
| | | if (type == -1) { |
| | | hasNext = false; |
| | | } |
| | | |
| | |
| | | return hasNext; |
| | | } |
| | | |
| | | private void init(boolean strict) throws IOException, Dgn7fileException |
| | | { |
| | | private void init(boolean strict) throws IOException, Dgn7fileException { |
| | | header = readHeader(channel, strict); |
| | | |
| | | // fileElementType = header.getElementType(); |
| | |
| | | // { |
| | | // throw new IOException("Unsuported shape type:" + fileElementType); |
| | | // } |
| | | if ((channel instanceof FileChannel) && useMemoryMappedBuffer) |
| | | { |
| | | if ((channel instanceof FileChannel) && useMemoryMappedBuffer) { |
| | | FileChannel fc = (FileChannel) channel; |
| | | |
| | | buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); |
| | |
| | | // buffer.position(100); |
| | | buffer.position(header.size()); |
| | | this.currentOffset = 0; |
| | | } else |
| | | { |
| | | } else { |
| | | // force useMemoryMappedBuffer to false |
| | | this.useMemoryMappedBuffer = false; |
| | | |
| | |
| | | record.end = toFileOffset(buffer.position()); |
| | | } |
| | | |
| | | private int toBufferOffset(int offset) |
| | | { |
| | | private int toBufferOffset(int offset) { |
| | | return (int) (offset - currentOffset); |
| | | } |
| | | |
| | | private int toFileOffset(int offset) |
| | | { |
| | | private int toFileOffset(int offset) { |
| | | return (int) (currentOffset + offset); |
| | | } |
| | | |
| | | public int getCount(int count) throws Dgn7fileException |
| | | { |
| | | try |
| | | { |
| | | if (channel == null) |
| | | { |
| | | public int getCount(int count) throws Dgn7fileException { |
| | | try { |
| | | if (channel == null) { |
| | | return -1; |
| | | } |
| | | |
| | | count = 0; |
| | | |
| | | for (int tmp = readElement(); tmp != -1; tmp = readElement()) |
| | | { |
| | | for (int tmp = readElement(); tmp != -1; tmp = readElement()) { |
| | | count += tmp; |
| | | } |
| | | } catch (IOException ioe) |
| | | { |
| | | } catch (IOException ioe) { |
| | | count = -1; |
| | | |
| | | // What now? This seems arbitrarily appropriate ! |
| | |
| | | return count; |
| | | } |
| | | |
| | | public int getCount() throws Dgn7fileException |
| | | { |
| | | public int getCount() throws Dgn7fileException { |
| | | return getCount(0); |
| | | } |
| | | |
| | | private int readElement() throws IOException |
| | | { |
| | | if (!fillBuffer()) |
| | | { |
| | | private int readElement() throws IOException { |
| | | if (!fillBuffer()) { |
| | | return -1; |
| | | } |
| | | |
| | | // burn the record number |
| | | buffer.getInt(); |
| | | |
| | | if (!fillBuffer()) |
| | | { |
| | | if (!fillBuffer()) { |
| | | return -1; |
| | | } |
| | | |
| | |
| | | // subtract that from the record length |
| | | recordlength -= 4; |
| | | |
| | | if (!fillBuffer()) |
| | | { |
| | | if (!fillBuffer()) { |
| | | return -1; |
| | | } |
| | | |
| | |
| | | int type = buffer.getInt(); |
| | | |
| | | // go to end of record |
| | | while (buffer.limit() < buffer.position() + recordlength) |
| | | { |
| | | while (buffer.limit() < buffer.position() + recordlength) { |
| | | recordlength -= buffer.limit() - buffer.position(); |
| | | buffer.clear(); |
| | | |
| | | if (channel.read(buffer) < 1) |
| | | { |
| | | if (channel.read(buffer) < 1) { |
| | | return -1; |
| | | } |
| | | } |
| | |
| | | buffer.position(buffer.position() + recordlength); |
| | | |
| | | // return 0 if record is null. Null records should be counted. |
| | | if (type == 0) |
| | | { |
| | | if (type == 0) { |
| | | // this is a null feature |
| | | return 0; |
| | | } |
| | |
| | | return 1; |
| | | } |
| | | |
| | | private boolean fillBuffer() throws IOException |
| | | { |
| | | private boolean fillBuffer() throws IOException { |
| | | int result = 1; |
| | | |
| | | if (buffer.limit() <= buffer.position() + 4) |
| | | { |
| | | if (buffer.limit() <= buffer.position() + 4) { |
| | | result = fill(buffer, channel); |
| | | } |
| | | |
| | | return result > 0; |
| | | } |
| | | |
| | | public static void main(String[] args) |
| | | { |
| | | public static void main(String[] args) { |
| | | JFileChooser jfc = new JFileChooser("D:/TEMP"); |
| | | File f = null; |
| | | int r = jfc.showOpenDialog(new JFrame()); |
| | | |
| | | if (r == JFileChooser.APPROVE_OPTION) |
| | | { |
| | | try |
| | | { |
| | | if (r == JFileChooser.APPROVE_OPTION) { |
| | | try { |
| | | f = jfc.getSelectedFile(); |
| | | |
| | | FileChannel channel = new FileInputStream(f).getChannel(); |
| | |
| | | count = 0; |
| | | size = 0; |
| | | |
| | | try |
| | | { |
| | | try { |
| | | Element lastComplex = null; |
| | | |
| | | while (reader.hasNext()) |
| | | { |
| | | while (reader.hasNext()) { |
| | | size++; |
| | | |
| | | Dgn7fileReader.Record record = reader.nextElement(); |
| | | Element.FileRecord record = reader.nextElement(); |
| | | |
| | | if (record.element() != null) |
| | | { |
| | | if (record.element() != null) { |
| | | Element element = (Element) record.element(); |
| | | ElementType type = element.getElementType(); |
| | | |
| | | if ((!type.isComplexElement()) && (!element.isComponentElement())) |
| | | { |
| | | if (lastComplex != null) |
| | | { |
| | | if ((!type.isComplexElement()) && (!element.isComponentElement())) { |
| | | if (lastComplex != null) { |
| | | // @todo add process in here |
| | | count++; |
| | | lastComplex = null; |
| | |
| | | |
| | | // @todo add process in here |
| | | count++; |
| | | } else if (element.isComponentElement()) |
| | | { |
| | | if (lastComplex != null) |
| | | { |
| | | } else if (element.isComponentElement()) { |
| | | if (lastComplex != null) { |
| | | ((ComplexElement) lastComplex).add(element); |
| | | } |
| | | } else if (type.isComplexElement()) |
| | | { |
| | | if (lastComplex == null) |
| | | { |
| | | } else if (type.isComplexElement()) { |
| | | if (lastComplex == null) { |
| | | lastComplex = element; |
| | | } else |
| | | { |
| | | } else { |
| | | // @todo add process in here |
| | | count++; |
| | | lastComplex = element; |
| | |
| | | } |
| | | } |
| | | } |
| | | } catch (IOException e) |
| | | { |
| | | } catch (IOException e) { |
| | | logger.warn("Stop read dgn file", e); |
| | | } catch (Dgn7fileException e) |
| | | { |
| | | e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. |
| | | } finally |
| | | { |
| | | } catch (Dgn7fileException e) { |
| | | logger.warn(e.getMessage(), e); |
| | | } finally { |
| | | reader.close(); |
| | | } |
| | | |
| | | System.out.println("count=" + count + " size=" + size); |
| | | logger.debug("count=" + count + " size=" + size); |
| | | // reader.close(); |
| | | } catch (IOException ioe) |
| | | { |
| | | System.out.println(ioe); |
| | | ioe.printStackTrace(); |
| | | } catch (Dgn7fileException e) |
| | | { |
| | | e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. |
| | | } catch (IOException ioe) { |
| | | logger.warn(ioe.getMessage(), ioe); |
| | | } catch (Dgn7fileException e) { |
| | | logger.warn(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | System.exit(0); |
| | | } |
| | | |
| | | public final class Record |
| | | { |
| | | int length; |
| | | int number = 0; |
| | | int offset; // Relative to the whole file |
| | | int start = 0; // Relative to the current loaded buffer |
| | | short signature = 0; |
| | | |
| | | /** |
| | | * The minimum X value. |
| | | */ |
| | | public double minX; |
| | | |
| | | /** |
| | | * The minimum Y value. |
| | | */ |
| | | public double minY; |
| | | |
| | | /** |
| | | * The minimum Z value. |
| | | */ |
| | | public double minZ; |
| | | |
| | | /** |
| | | * The maximum X value. |
| | | */ |
| | | public double maxX; |
| | | |
| | | /** |
| | | * The maximum Y value. |
| | | */ |
| | | public double maxY; |
| | | |
| | | /** |
| | | * The maximum Z value. |
| | | */ |
| | | public double maxZ; |
| | | |
| | | // ElementType type; |
| | | int end = 0; // Relative to the whole file |
| | | Object element = null; |
| | | IElementHandler handler; |
| | | |
| | | public Object element() |
| | | { |
| | | if (element == null) |
| | | { |
| | | buffer.position(start); |
| | | buffer.order(ByteOrder.LITTLE_ENDIAN); |
| | | |
| | | if (handler == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | element = handler.read(buffer, signature, length); |
| | | } |
| | | |
| | | return element; |
| | | } |
| | | |
| | | public int offset() |
| | | { |
| | | return offset; |
| | | } |
| | | |
| | | /** |
| | | * A summary of the record. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return "Record " + number + " length " + length + " bounds " + minX + "," + minY + " " + maxX + "," + maxY; |
| | | } |
| | | } |
| | | } |