001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.shapefile.indexed;
017:
018: import java.io.IOException;
019: import java.nio.ByteBuffer;
020: import java.nio.MappedByteBuffer;
021: import java.nio.channels.FileChannel;
022:
023: import org.geotools.data.shapefile.StreamLogging;
024: import org.geotools.resources.NIOUtilities;
025:
026: /**
027: * The Writer writes out the fid and record number of features to the fid
028: * index file.
029: *
030: * @author Jesse
031: */
032: public class IndexedFidWriter {
033: public static final int HEADER_SIZE = 13;
034: public static final int RECORD_SIZE = 12;
035: private FileChannel channel;
036: private ByteBuffer writeBuffer;
037: private IndexedFidReader reader;
038: long fidIndex;
039: private int recordIndex;
040: private boolean closed;
041:
042: private long current;
043:
044: private long position;
045: private int removes;
046: StreamLogging streamLogger = new StreamLogging("IndexedFidReader");
047:
048: public IndexedFidWriter(FileChannel writeChannel,
049: IndexedFidReader reader2) throws IOException {
050: this .channel = writeChannel;
051: reader = reader2;
052: streamLogger.open();
053: allocateBuffers();
054: removes = reader.getRemoves();
055: writeBuffer.position(HEADER_SIZE);
056: closed = false;
057: position = 0;
058: current = -1;
059: recordIndex = 0;
060: fidIndex = 0;
061: }
062:
063: /**
064: * Allocate some buffers for writing.
065: */
066: private void allocateBuffers() {
067: writeBuffer = ByteBuffer.allocateDirect(HEADER_SIZE
068: + RECORD_SIZE * 1024);
069: }
070:
071: /**
072: * Drain internal buffers into underlying channels.
073: *
074: * @throws IOException DOCUMENT ME!
075: */
076: private void drain() throws IOException {
077: writeBuffer.flip();
078:
079: int written = 0;
080:
081: while (writeBuffer.remaining() > 0)
082: written += channel.write(writeBuffer, position);
083:
084: position += written;
085:
086: writeBuffer.flip().limit(writeBuffer.capacity());
087: }
088:
089: private void writeHeader() throws IOException {
090: ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);
091:
092: buffer.put((byte) 1);
093:
094: buffer.putLong(recordIndex);
095: buffer.putInt(removes);
096: buffer.flip();
097: channel.write(buffer, 0);
098: }
099:
100: public boolean hasNext() throws IOException {
101: return reader.hasNext();
102: }
103:
104: public long next() throws IOException {
105:
106: if (current != -1)
107: write();
108:
109: if (reader.hasNext()) {
110: String fid = reader.next();
111: fidIndex = Integer.parseInt(fid.substring(fid
112: .lastIndexOf(".") + 1));
113: } else {
114: fidIndex++;
115: }
116:
117: current = fidIndex;
118:
119: return fidIndex;
120: }
121:
122: public void close() throws IOException {
123: if (closed) {
124: return;
125: }
126:
127: try {
128:
129: while (hasNext())
130: next();
131:
132: if (current != -1)
133: write();
134:
135: drain();
136: writeHeader();
137: } finally {
138: try {
139:
140: if (writeBuffer != null) {
141: if (writeBuffer instanceof MappedByteBuffer) {
142: NIOUtilities.clean(writeBuffer);
143: }
144: }
145:
146: if (channel.isOpen())
147: channel.close();
148: streamLogger.close();
149: } finally {
150: reader.close();
151: }
152: }
153:
154: closed = true;
155: }
156:
157: /**
158: * Increments the fidIndex by 1. Indicates that a feature was
159: * removed from the location. This is intended to ensure that FIDs stay
160: * constant over time. Consider the following case of 5 features. feature
161: * 1 has fid typename.0 feature 2 has fid typename.1 feature 3 has fid
162: * typename.2 feature 4 has fid typename.3 feature 5 has fid typename.4
163: * when feature 3 is removed/deleted the following usage of the write
164: * should take place: next(); (move to feature 1) next(); (move to
165: * feature 2) next(); (move to feature 3) remove();(delete feature 3)
166: * next(); (move to feature 4) // optional write(); (write feature 4)
167: * next(); (move to feature 5) write(); (write(feature 5)
168: *
169: * @throws IOException
170: */
171: public void remove() throws IOException {
172: if (current == -1)
173: throw new IOException(
174: "Current fid index is null, next must be called before remove");
175: if (hasNext()) {
176: removes++;
177: current = -1;
178:
179: // reader.next();
180: }
181: }
182:
183: /**
184: * Writes the current fidIndex. Writes to the same place in the
185: * file each time. Only {@link #next()} moves forward in the file.
186: *
187: * @throws IOException
188: *
189: * @see #next()
190: * @see #remove()
191: */
192: public void write() throws IOException {
193: if (current == -1)
194: throw new IOException(
195: "Current fid index is null, next must be called before remove");
196:
197: if (writeBuffer == null) {
198: allocateBuffers();
199: }
200:
201: if (writeBuffer.remaining() < RECORD_SIZE) {
202: drain();
203: }
204:
205: writeBuffer.putLong(current);
206: writeBuffer.putInt(recordIndex);
207:
208: recordIndex++;
209: current = -1;
210: }
211:
212: public boolean isClosed() {
213: return closed;
214: }
215: }
|