Index: org/apache/lucene/store/NIOFSDirectory.java
===================================================================
--- org/apache/lucene/store/NIOFSDirectory.java	(revision 0)
+++ org/apache/lucene/store/NIOFSDirectory.java	(revision 0)
@@ -0,0 +1,293 @@
+package org.apache.lucene.store;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * NIO version of FSDirectory
+ * 
+ * @see FSDirectory
+ */
+public class NIOFSDirectory extends FSDirectory {
+
+  
+  /**
+   * Creates a new, empty file in the directory with the given name. Returns a
+   * stream writing this file.
+   */
+  public IndexOutput createOutput(String name) throws IOException {
+
+    File file = new File(getFile(), name);
+    if (file.exists() && !file.delete()) // delete existing, if any
+      throw new IOException("Cannot overwrite: " + file);
+
+    return new NIOFSIndexOutput(file);
+  }
+
+  // Inherit javadoc
+  public IndexInput openInput(String name, int bufferSize) throws IOException {
+    return new NIOFSIndexInput(new File(getFile(), name), bufferSize);
+  }
+
+  protected static class NIOFSIndexInput extends IndexInput {
+
+    private static class Descriptor extends RandomAccessFile {
+      // remember if the file is open, so that we don't try to close it
+      // more than once
+      private boolean isOpen;
+      long position;
+      final long length;
+      final FileChannel channel;
+
+      public Descriptor(File file, String mode) throws IOException {
+        super(file, mode);
+        isOpen = true;
+        length = length();
+        channel = this.getChannel();
+      }
+
+      public void close() throws IOException {
+        if (isOpen) {
+          isOpen = false;
+          super.close();
+        }
+      }
+
+      protected void finalize() throws Throwable {
+        try {
+          close();
+        } finally {
+          super.finalize();
+        }
+      }
+    }
+
+    private final Descriptor file;
+    boolean isClone;
+    static final int BUFFER_SIZE = BufferedIndexOutput.BUFFER_SIZE;
+
+    private byte[] buffer;
+    private ByteBuffer bytebuf; // wrapps the buffer for NIO
+    private long bufferStart = 0; // position in file of buffer
+    private int bufferLength = 0; // end of valid bytes
+    private int bufferPosition = 0; // next byte to read
+
+    public NIOFSIndexInput(File path) throws IOException {
+      this(path, BufferedIndexInput.BUFFER_SIZE);
+    }
+
+    public NIOFSIndexInput(File path, int bufferSize) throws IOException {
+      file = new Descriptor(path, "r");
+    }
+
+    public void readBytes(byte[] b, int offset, int len) throws IOException {
+      if (len <= (bufferLength - bufferPosition)) {
+        // the buffer contains enough data to satistfy this request
+        if (len > 0) // to allow b to be null if len is 0...
+          System.arraycopy(buffer, bufferPosition, b, offset, len);
+        bufferPosition += len;
+      } else {
+        // the buffer does not have enough data. First serve all we've got.
+        int available = bufferLength - bufferPosition;
+        if (available > 0) {
+          System.arraycopy(buffer, bufferPosition, b, offset, available);
+          offset += available;
+          len -= available;
+          bufferPosition += available;
+        }
+
+        // and now, read the remaining 'len' bytes:
+        if (len < BUFFER_SIZE) {
+          // If the amount left to read is small enough, do it in the usual
+          // buffered way: fill the buffer and copy from it:
+          refill();
+          if (bufferLength < len) {
+            // Throw an exception when refill() could not read len bytes:
+            System.arraycopy(buffer, 0, b, offset, bufferLength);
+            throw new IOException("read past EOF");
+
+          } else {
+            System.arraycopy(buffer, 0, b, offset, len);
+            bufferPosition = len;
+          }
+        } else {
+          // The amount left to read is larger than the buffer - there's no
+          // performance reason not to read it all at once. Note that unlike
+          // the previous code of this function, there is no need to do a seek
+          // here, because there's no need to reread what we had in the buffer.
+          long after = bufferStart + bufferPosition + len;
+          readInternal(b, offset, len);
+          bufferStart = after;
+          bufferPosition = 0;
+          bufferLength = 0; // trigger refill() on read
+        }
+      }
+    }
+
+    private void read(ByteBuffer bb) throws IOException {
+      long pos = getFilePointer();
+      while (bb.hasRemaining()) {
+        int i = file.channel.read(bb, pos);
+        if (i == -1)
+          throw new IOException("read past EOF");
+        pos += i;
+      }
+    }
+
+    private void refill() throws IOException {
+      long start = bufferStart + bufferPosition;
+      long end = start + BUFFER_SIZE;
+      if (end > length())
+        end = length();
+      bufferLength = (int) (end - start);
+      if (bufferLength <= 0)
+        throw new IOException("read past EOF");
+
+      if (buffer == null) {
+        buffer = new byte[BUFFER_SIZE]; // allocate buffer lazily
+        bytebuf = ByteBuffer.wrap(buffer);
+      } else {
+        bytebuf.clear();
+      }
+
+      // since we try to read a complete buffer, we need to limit it if
+      // it's past the end of the file.
+      if (bufferLength != BUFFER_SIZE)
+        bytebuf.limit(bufferLength);
+
+      read(bytebuf);
+
+      bufferStart = start;
+      bufferPosition = 0;
+    }
+
+    private void readInternal(byte[] b, int offset, int len) throws IOException {
+      read(ByteBuffer.wrap(b, offset, len));
+    }
+
+    public byte readByte() throws IOException {
+      if (bufferPosition >= bufferLength)
+        refill();
+      return buffer[bufferPosition++];
+    }
+
+    public long getFilePointer() {
+      return bufferStart + bufferPosition;
+    }
+
+    public long length() {
+      return file.length;
+    }
+
+    public void seek(long pos) throws IOException {
+      if (pos >= bufferStart && pos < (bufferStart + bufferLength))
+        bufferPosition = (int) (pos - bufferStart); // seek within buffer
+      else {
+        bufferStart = pos;
+        bufferPosition = 0;
+        bufferLength = 0; // trigger refill() on read()
+      }
+    }
+
+    public Object clone() {
+      NIOFSIndexInput clone = (NIOFSIndexInput) super.clone();
+      clone.isClone = true;
+      if (buffer != null) {
+        clone.buffer = new byte[buffer.length];
+        System.arraycopy(buffer, 0, clone.buffer, 0, bufferLength);
+        bytebuf = ByteBuffer.wrap(buffer);
+      }
+      return clone;
+    }
+
+    public void close() throws IOException {
+      // only close the file if this is not a clone
+      if (!isClone)
+        file.close();
+    }
+
+    protected void seekInternal(long position) {
+    }
+
+    /**
+     * Method used for testing. Returns true if the underlying file descriptor
+     * is valid.
+     */
+    boolean isFDValid() throws IOException {
+      return file.getFD().valid();
+    }
+  }
+
+  protected static class NIOFSIndexOutput extends BufferedIndexOutput {
+    RandomAccessFile file = null;
+
+    // remember if the file is open, so that we don't try to close it
+    // more than once
+    private boolean isOpen;
+
+    public NIOFSIndexOutput(File path) throws IOException {
+      file = new RandomAccessFile(path, "rw");
+      isOpen = true;
+    }
+
+    /** output methods: */
+    public void flushBuffer(byte[] b, int offset, int size) throws IOException {
+      file.write(b, offset, size);
+    }
+
+    public void close() throws IOException {
+      // only close the file if it has not been closed yet
+      if (isOpen) {
+        boolean success = false;
+        try {
+          super.close();
+          success = true;
+        } finally {
+          isOpen = false;
+          if (!success) {
+            try {
+              file.close();
+            } catch (Throwable t) {
+              // Suppress so we don't mask original exception
+            }
+          } else
+            file.close();
+        }
+      }
+    }
+
+    /** Random-access methods */
+    public void seek(long pos) throws IOException {
+      super.seek(pos);
+      file.seek(pos);
+    }
+
+    public long length() throws IOException {
+      return file.length();
+    }
+
+    public void setLength(long length) throws IOException {
+      file.setLength(length);
+    }
+  }
+}
