Index: src/test/org/apache/lucene/index/TestMultiReader.java
===================================================================
--- src/test/org/apache/lucene/index/TestMultiReader.java	(revision 582652)
+++ src/test/org/apache/lucene/index/TestMultiReader.java	(working copy)
@@ -45,5 +45,33 @@
     
     return reader;
   }
+  
+  public void testClone() throws IOException, CloneNotSupportedException {
+    sis.read(dir);
 
+    IndexReader reader = new MultiReader(new IndexReader[] 
+                              {SegmentReader.get(sis.info(0)), 
+                               SegmentReader.get(sis.info(1))});
+    
+    IndexReader clone = (IndexReader) reader.clone();
+    
+    TestIndexReader.assertIndexEquals(reader, clone);
+    
+    reader = new MultiReader(new IndexReader[] 
+                  {new TestFilterIndexReader.NotCloneableIndexReader(SegmentReader.get(sis.info(0))), 
+                   SegmentReader.get(sis.info(1))});
+    
+    TestIndexReader.assertIndexEquals(reader, clone);
+    
+    // clone() should now throw an exception, because one of the subreaders
+    // is not cloneable
+    try {
+      clone = (IndexReader) reader.clone();
+      fail("Expected exception not thrown.");
+    } catch (CloneNotSupportedException e) {
+      // expected exception
+    }
+  }
+  
+
 }
Index: src/test/org/apache/lucene/index/TestIndexReader.java
===================================================================
--- src/test/org/apache/lucene/index/TestIndexReader.java	(revision 582652)
+++ src/test/org/apache/lucene/index/TestIndexReader.java	(working copy)
@@ -25,6 +25,7 @@
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader.FieldOption;
 import org.apache.lucene.search.Hits;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.TermQuery;
@@ -1170,6 +1171,82 @@
         }
         dir.delete();
     }
+    
+    public static void assertIndexEquals(IndexReader index1, IndexReader index2) throws IOException {
+      assertEquals("IndexReaders have different values for numDocs.", index1.numDocs(), index2.numDocs());
+      assertEquals("IndexReaders have different values for maxDoc.", index1.maxDoc(), index2.maxDoc());
+      assertEquals("Only one IndexReader has deletions.", index1.hasDeletions(), index2.hasDeletions());
+      assertEquals("Only one index is optimized.", index1.isOptimized(), index2.isOptimized());
+      
+      // check field names
+      Collection fields1 = index1.getFieldNames(FieldOption.ALL);
+      Collection fields2 = index1.getFieldNames(FieldOption.ALL);
+      assertEquals("IndexReaders have different numbers of fields.", fields1.size(), fields2.size());
+      Iterator it1 = fields1.iterator();
+      Iterator it2 = fields1.iterator();
+      while (it1.hasNext()) {
+        assertEquals("Different field names.", (String) it1.next(), (String) it2.next());
+      }
+      
+      // check norms
+      it1 = fields1.iterator();
+      while (it1.hasNext()) {
+        String curField = (String) it1.next();
+        byte[] norms1 = index1.norms(curField);
+        byte[] norms2 = index2.norms(curField);
+        assertEquals(norms1.length, norms2.length);
+        for (int i = 0; i < norms1.length; i++) {
+          assertEquals("Norm different for doc " + i + " and field '" + curField + "'.", norms1[i], norms2[i]);
+        }      
+      }
+      
+      // check deletions
+      for (int i = 0; i < index1.maxDoc(); i++) {
+        assertEquals("Doc " + i + " only deleted in one index.", index1.isDeleted(i), index2.isDeleted(i));
+      }
+      
+      // check stored fields
+      for (int i = 0; i < index1.maxDoc(); i++) {
+        if (!index1.isDeleted(i)) {
+          Document doc1 = index1.document(i);
+          Document doc2 = index2.document(i);
+          fields1 = doc1.getFields();
+          fields2 = doc2.getFields();
+          assertEquals("Different numbers of fields for doc " + i + ".", fields1.size(), fields2.size());
+          it1 = fields1.iterator();
+          it2 = fields2.iterator();
+          while (it1.hasNext()) {
+            Field curField1 = (Field) it1.next();
+            Field curField2 = (Field) it2.next();
+            assertEquals("Different fields names for doc " + i + ".", curField1.name(), curField2.name());
+            assertEquals("Different field values for doc " + i + ".", curField1.stringValue(), curField2.stringValue());
+          }          
+        }
+      }
+      
+      // check dictionary and posting lists
+      TermEnum enum1 = index1.terms();
+      TermEnum enum2 = index2.terms();
+      TermPositions tp1 = index1.termPositions();
+      TermPositions tp2 = index2.termPositions();
+      while(enum1.next()) {
+        assertTrue(enum2.next());
+        assertEquals("Different term in dictionary.", enum1.term(), enum2.term());
+        tp1.seek(enum1.term());
+        tp2.seek(enum1.term());
+        while(tp1.next()) {
+          assertTrue(tp2.next());
+          assertEquals("Different doc id in postinglist of term " + enum1.term() + ".", tp1.doc(), tp2.doc());
+          assertEquals("Different term frequence in postinglist of term " + enum1.term() + ".", tp1.freq(), tp2.freq());
+          for (int i = 0; i < tp1.freq(); i++) {
+            assertEquals("Different positions in postinglist of term " + enum1.term() + ".", tp1.nextPosition(), tp2.nextPosition());
+          }
+        }
+      }
+    }
+    
+    
 
+
     
 }
Index: src/test/org/apache/lucene/index/TestFilterIndexReader.java
===================================================================
--- src/test/org/apache/lucene/index/TestFilterIndexReader.java	(revision 582652)
+++ src/test/org/apache/lucene/index/TestFilterIndexReader.java	(working copy)
@@ -128,4 +128,56 @@
     reader.close();
     directory.close();
   }
+  
+  public void testClone() throws IOException, CloneNotSupportedException {
+    RAMDirectory directory = new MockRAMDirectory();
+    IndexWriter writer =
+      new IndexWriter(directory, new WhitespaceAnalyzer(), true);
+
+    Document d1 = new Document();
+    d1.add(new Field("default","one two", Field.Store.YES, Field.Index.TOKENIZED));
+    writer.addDocument(d1);
+
+    writer.close();
+
+    IndexReader clone = null;
+    IndexReader reader = new FilterIndexReader(IndexReader.open(directory));
+    
+    try {
+      clone = (IndexReader) reader.clone();
+      clone.close();
+      fail("Expected exception not thrown.");
+    } catch (CloneNotSupportedException e) {
+      // expected exception not thrown
+    }
+    
+    reader.close();
+    
+    reader = new CloneableIndexReader(IndexReader.open(directory));
+    clone = (IndexReader) reader.clone();
+
+    assertEquals(reader.getClass(), clone.getClass());
+    TestIndexReader.assertIndexEquals(reader, clone);
+    
+  }
+  
+  /** not really needed, by default FilterIndexReader isn't clonable */
+  static class NotCloneableIndexReader extends FilterIndexReader {
+
+    public NotCloneableIndexReader(IndexReader in) {
+      super(in);
+    }
+  }
+    
+  static class CloneableIndexReader extends FilterIndexReader implements Cloneable {
+
+    public CloneableIndexReader(IndexReader in) {
+      super(in);
+    }
+    
+    public Object clone() throws CloneNotSupportedException {
+      return super.clone();
+    }    
+  }
+
 }
Index: src/test/org/apache/lucene/index/TestIndexReaderReopen.java
===================================================================
--- src/test/org/apache/lucene/index/TestIndexReaderReopen.java	(revision 0)
+++ src/test/org/apache/lucene/index/TestIndexReaderReopen.java	(revision 0)
@@ -0,0 +1,328 @@
+package org.apache.lucene.index;
+
+/**
+ * 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.IOException;
+
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.store.AlreadyClosedException;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+
+import junit.framework.TestCase;
+
+public class TestIndexReaderReopen extends TestCase {
+    
+  public void testReopen() throws Exception {
+    final Directory dir1 = new RAMDirectory();
+    
+    createIndex(dir1, true);
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        TestIndexReaderReopen.modifyIndex(i, dir1);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        return IndexReader.open(dir1);
+      }
+      
+    });
+    
+    final Directory dir2 = new RAMDirectory();
+    
+    createIndex(dir2, false);
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        TestIndexReaderReopen.modifyIndex(i, dir2);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        return IndexReader.open(dir2);
+      }
+      
+    });
+
+
+  }
+  
+  private void performDefaultTests(TestReopen test) throws Exception {
+    IndexReader index1 = test.openReader();
+    IndexReader index2 = test.openReader();
+        
+    TestIndexReader.assertIndexEquals(index1, index2);
+    
+    // verify that reopen() does not return a new reader instance
+    // in case the index has no changes
+    index2 = refreshReader(index2, false);
+    
+    test.modifyIndex(0);
+    
+    // don't close old reader
+    IndexReader index2_refreshed = refreshReader(index2, false, true);
+    IndexReader r = test.openReader();
+    // test if refreshed reader and newly opened reader return equal results
+    TestIndexReader.assertIndexEquals(r, index2_refreshed);
+    r.close();
+    
+    // now use refreshed reader to perform changes and verify that the old
+    // reader is completely independent of these changes
+    index2_refreshed.deleteDocument(1);
+    index2_refreshed.setNorm(2, "field1", 101);
+    index2_refreshed.setNorm(2, "field2", 102);
+    
+    TestIndexReader.assertIndexEquals(index1, index2);
+        
+    // check if closing the refreshed reader does not affect the old reader
+    index2_refreshed.close();    
+    TestIndexReader.assertIndexEquals(index1, index2);
+    
+    index1.close();
+    index1 = test.openReader();
+    // now close old reader
+    index2_refreshed = refreshReader(index2, true, true);
+    
+    // the old reader should now throw an IOException 
+    // indicating that the subreaders were closed during reopen()
+    try {
+      index2.ensureOpen();
+      fail("Expected exception not thrown.");
+    } catch (AlreadyClosedException expected) {
+      // expected exception
+    }
+    
+    index2 = index2_refreshed;
+    TestIndexReader.assertIndexEquals(index1, index2);
+    
+    for (int i = 1; i < 4; i++) {
+      test.modifyIndex(i);
+      
+      // refresh IndexReader
+      index1.close();
+      index1 = test.openReader();
+      
+      index2 = refreshReader(index2, true);
+      TestIndexReader.assertIndexEquals(index1, index2);
+    }
+    
+    index1.close();
+    index2.close();
+  }
+  
+  public void testFilterIndexReaderReopen() throws Exception {
+    final Directory dir = new RAMDirectory();
+    
+    createIndex(dir, true);
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        TestIndexReaderReopen.modifyIndex(i, dir);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        return new TestFilterIndexReader.CloneableIndexReader(IndexReader.open(dir));
+      }
+      
+    });
+  }
+  
+  public void testParallelReaderReopen() throws Exception {
+    final Directory dir1 = new RAMDirectory();
+    createIndex(dir1, true);
+    final Directory dir2 = new RAMDirectory();
+    createIndex(dir2, true);
+    
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        TestIndexReaderReopen.modifyIndex(i, dir1);
+        TestIndexReaderReopen.modifyIndex(i, dir2);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        ParallelReader pr = new ParallelReader();
+        pr.add(IndexReader.open(dir1));
+        pr.add(IndexReader.open(dir2));
+        return pr;
+      }
+      
+    });
+
+  }
+
+  public void testMultiReaderReopen() throws Exception {
+    final Directory dir1 = new RAMDirectory();
+    createIndex(dir1, true);
+    final Directory dir2 = new RAMDirectory();
+    createIndex(dir2, true);
+
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        TestIndexReaderReopen.modifyIndex(i, dir1);
+        TestIndexReaderReopen.modifyIndex(i, dir2);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        return new MultiReader(new IndexReader[] 
+                        {IndexReader.open(dir1), 
+                         IndexReader.open(dir2)});
+      }
+      
+    });
+  }
+
+  public void testMixedReaders() throws Exception {
+    final Directory dir1 = new RAMDirectory();
+    createIndex(dir1, true);
+    final Directory dir2 = new RAMDirectory();
+    createIndex(dir2, true);
+    final Directory dir3 = new RAMDirectory();
+    createIndex(dir3, false);
+    final Directory dir4 = new RAMDirectory();
+    createIndex(dir4, true);
+    final Directory dir5 = new RAMDirectory();
+    createIndex(dir5, false);
+    
+    performDefaultTests(new TestReopen() {
+
+      protected void modifyIndex(int i) throws IOException {
+        // only change norms in this index to maintain the same number of docs for each of ParallelReader's subreaders
+        if (i == 1) TestIndexReaderReopen.modifyIndex(i, dir1);  
+        
+        TestIndexReaderReopen.modifyIndex(i, dir4);
+        TestIndexReaderReopen.modifyIndex(i, dir5);
+      }
+
+      protected IndexReader openReader() throws IOException {
+        ParallelReader pr = new ParallelReader();
+        pr.add(new TestFilterIndexReader.CloneableIndexReader(IndexReader.open(dir1)));
+        pr.add(IndexReader.open(dir2));
+        MultiReader mr = new MultiReader(new IndexReader[] {
+            IndexReader.open(dir3), IndexReader.open(dir4)});
+        return new MultiReader(new IndexReader[] {
+           pr, mr, new TestFilterIndexReader.CloneableIndexReader(IndexReader.open(dir5)) 
+        });
+      }
+    });
+  }  
+  
+  private IndexReader refreshReader(IndexReader reader, boolean hasChanges) throws IOException {
+    return refreshReader(reader, false, hasChanges);
+  }
+  
+  private IndexReader refreshReader(IndexReader reader, boolean closeOldReader, boolean hasChanges) throws IOException {
+    IndexReader refreshed = reader.reopen(closeOldReader);
+    if (hasChanges) {
+      if (refreshed == reader) {
+        fail("No new IndexReader instance created during refresh.");
+      }
+    } else {
+      if (refreshed != reader) {
+        fail("New IndexReader instance created during refresh even though index had no changes.");
+      }
+    }
+    
+    return refreshed;
+  }
+  
+  private static void createIndex(Directory dir, boolean multiSegment) throws IOException {
+    IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer());
+    
+    w.setMergePolicy(new LogDocMergePolicy());
+    
+    for (int i = 0; i < 100; i++) {
+      w.addDocument(createDocument(i));
+      if (multiSegment && (i % 10) == 0) {
+        w.flush();
+      }
+    }
+    
+    if (!multiSegment) {
+      w.optimize();
+    }
+    
+    w.close();
+    
+    IndexReader r = IndexReader.open(dir);
+    if (multiSegment) {
+      assertTrue(r instanceof MultiSegmentReader);
+    } else {
+      assertTrue(r instanceof SegmentReader);
+    }
+    r.close();
+  }
+
+  private static Document createDocument(int n) {
+    StringBuffer sb = new StringBuffer();
+    Document doc = new Document();
+    sb.append("a");
+    sb.append(n);
+    doc.add(new Field("field1", sb.toString(), Store.YES, Index.TOKENIZED));
+    sb.append(" b");
+    sb.append(n);
+    doc.add(new Field("field2", sb.toString(), Store.YES, Index.TOKENIZED));
+    return doc;
+  }
+
+  private static void modifyIndex(int i, Directory dir) throws IOException {
+    switch (i) {
+      case 0: {
+        IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer());
+        w.setMergeScheduler(new SerialMergeScheduler());
+        w.deleteDocuments(new Term("field2", "a11"));
+        w.deleteDocuments(new Term("field2", "b30"));
+        w.close();
+        break;
+      }
+      case 1: {
+        IndexReader reader = IndexReader.open(dir);
+        reader.setNorm(4, "field1", 123);
+        reader.setNorm(44, "field2", 222);
+        reader.close();
+        break;
+      }
+      case 2: {
+        IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer());
+        w.optimize();
+        w.close();
+        break;
+      }
+      case 3: {
+        IndexWriter w = new IndexWriter(dir, new WhitespaceAnalyzer());
+        w.addDocument(createDocument(101));
+        w.optimize();
+        w.addDocument(createDocument(102));
+        w.addDocument(createDocument(103));
+        w.close();
+        break;
+      }
+    }
+  }  
+
+  private abstract static class TestReopen {
+    protected abstract IndexReader openReader() throws IOException;
+    protected abstract void modifyIndex(int i) throws IOException;
+  }
+  
+}

Property changes on: src/test/org/apache/lucene/index/TestIndexReaderReopen.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: src/test/org/apache/lucene/index/TestParallelReader.java
===================================================================
--- src/test/org/apache/lucene/index/TestParallelReader.java	(revision 582652)
+++ src/test/org/apache/lucene/index/TestParallelReader.java	(working copy)
@@ -271,4 +271,31 @@
     return dir2;
   }
 
+  public void testClone() throws IOException, CloneNotSupportedException {
+    Directory dir1 = getDir1();
+    Directory dir2 = getDir1();
+
+    ParallelReader reader = new ParallelReader();
+    reader.add(IndexReader.open(dir1));
+    reader.add(IndexReader.open(dir2));
+
+    ParallelReader clone = (ParallelReader) reader.clone();
+    
+    TestIndexReader.assertIndexEquals(reader, clone);
+
+    reader = new ParallelReader();
+    reader.add(new TestFilterIndexReader.NotCloneableIndexReader(IndexReader.open(dir1)));
+    reader.add(IndexReader.open(dir2));
+    
+    TestIndexReader.assertIndexEquals(reader, clone);
+    
+    // clone() should now throw an exception, because one of the subreaders
+    // is not cloneable
+    try {
+      clone = (ParallelReader) reader.clone();
+      fail("Expected exception not thrown.");
+    } catch (CloneNotSupportedException e) {
+      // expected exception
+    }
+  }
 }
Index: src/java/org/apache/lucene/index/MultiReader.java
===================================================================
--- src/java/org/apache/lucene/index/MultiReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/MultiReader.java	(working copy)
@@ -32,7 +32,7 @@
  *
  * @version $Id$
  */
-public class MultiReader extends IndexReader {
+public class MultiReader extends IndexReader implements Cloneable {
   protected IndexReader[] subReaders;
   private int[] starts;                           // 1st docno for each segment
   private Hashtable normsCache = new Hashtable();
@@ -55,6 +55,8 @@
   
   private void initialize(IndexReader[] subReaders) {
     this.subReaders = subReaders;
+    maxDoc = 0;
+    numDocs = -1;
     starts = new int[subReaders.length + 1];    // build starts array
     for (int i = 0; i < subReaders.length; i++) {
       starts[i] = maxDoc;
@@ -66,7 +68,72 @@
     starts[subReaders.length] = maxDoc;
   }
 
+  /**
+   * Tries to reopen the subreaders (see {@link IndexReader#reopen()}).
+   * <br>
+   * If one or more subreaders were reopened successfully, then a new
+   * MultiReader instance is returned, otherwise this instance is returned.
+   * <p>
+   * Note that the subreaders have to support <code>clone()</code> when
+   * this method is used with <code>closeOldReader==false</code>. See
+   * {@link IndexReader#reopen(boolean)} and {@link IndexReader#isCloneSupported()} 
+   * for further details.
+   * 
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error 
+   */
+  public synchronized IndexReader reopen(boolean closeOldReader) throws CorruptIndexException, IOException {
+    boolean reopened = false;
+    IndexReader[] newSubReaders = new IndexReader[subReaders.length];
+    for (int i = 0; i < subReaders.length; i++) {
+      newSubReaders[i] = subReaders[i].reopen(closeOldReader);
+      // if at least one of the subreaders was updated we remember that
+      // and return a new MultiReader
+      reopened |= (newSubReaders[i] != subReaders[i]);
+    }
+    
+    if (reopened) {
+      for (int i = 0; i < subReaders.length; i++) {
+       if (subReaders[i] == newSubReaders[i]) {
+         if (subReaders[i] instanceof Cloneable) {
+           try {
+             newSubReaders[i] = (IndexReader) subReaders[i].clone();
+           } catch (CloneNotSupportedException e1) {
+             throw new UnsupportedOperationException("subreader#"+i+" could not be cloned: " + subReaders[i].getClass(), e1);
+           }
+         } else {
+           throw new UnsupportedOperationException("subreader#"+i+" is not Cloneable: " + subReaders[i].getClass());
+         }
+       } 
+      }
+      MultiReader mr = new MultiReader(newSubReaders);
+      if (closeOldReader) {
+        closed = true;
+      }
+      return mr;
+    } else {
+      return this;
+    }
+  }
+  
+  /**
+   * All subreaders must support <code>clone()</code>.
+   */
+  public Object clone() throws CloneNotSupportedException {
+    MultiReader clone = (MultiReader) super.clone();
+    
+    IndexReader[] newSubReaders = new IndexReader[this.subReaders.length];
+    for (int i = 0; i < this.subReaders.length; i++) {
+      newSubReaders[i] = (IndexReader) this.subReaders[i].clone();
+    }
+    
+    clone.initialize(newSubReaders);
+    clone.normsCache = new Hashtable();
+    
+    return clone;
+  }
 
+
   public TermFreqVector[] getTermFreqVectors(int n) throws IOException {
     ensureOpen();
     int i = readerIndex(n);        // find segment num
Index: src/java/org/apache/lucene/index/DirectoryIndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryIndexReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/DirectoryIndexReader.java	(working copy)
@@ -30,8 +30,8 @@
  * whenever index modifications are performed.
  */
 abstract class DirectoryIndexReader extends IndexReader {
-  private Directory directory;
-  private boolean closeDirectory;
+  protected Directory directory;
+  protected boolean closeDirectory;
   private IndexDeletionPolicy deletionPolicy;
 
   private SegmentInfos segmentInfos;
@@ -58,6 +58,44 @@
     init(directory, segmentInfos, closeDirectory);
   }
   
+  public final synchronized IndexReader reopen(final boolean closeOldReader) throws CorruptIndexException, IOException {
+    ensureOpen();
+
+    if (this.isCurrent()) {
+      return this;
+    }
+
+    return (DirectoryIndexReader) new SegmentInfos.FindSegmentsFile(directory) {
+
+      protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
+        SegmentInfos infos = new SegmentInfos();
+        infos.read(directory, segmentFileName);
+
+        DirectoryIndexReader newReader = doReopen(infos, closeOldReader);
+
+        newReader.init(directory, infos, closeDirectory);
+        newReader.deletionPolicy = deletionPolicy;
+
+        if (newReader != DirectoryIndexReader.this && closeOldReader) {
+          closed = true;
+        }
+        
+        return newReader;
+      }
+    }.run();
+  }
+
+  protected abstract DirectoryIndexReader doReopen(SegmentInfos infos, boolean closeOldReader) throws CorruptIndexException, IOException;
+
+  public Object clone() throws CloneNotSupportedException {
+    DirectoryIndexReader clone = (DirectoryIndexReader) super.clone();
+    if (this.segmentInfos != null) {
+      clone.segmentInfos = (SegmentInfos) this.segmentInfos.clone();
+    }
+    
+    return clone;
+  }
+  
   public void setDeletionPolicy(IndexDeletionPolicy deletionPolicy) {
     this.deletionPolicy = deletionPolicy;
   }
Index: src/java/org/apache/lucene/index/FieldInfos.java
===================================================================
--- src/java/org/apache/lucene/index/FieldInfos.java	(revision 582652)
+++ src/java/org/apache/lucene/index/FieldInfos.java	(working copy)
@@ -32,7 +32,7 @@
  *  be adding documents at a time, with no other reader or writer threads
  *  accessing this object.
  */
-final class FieldInfos {
+final class FieldInfos implements Cloneable {
   
   static final byte IS_INDEXED = 0x1;
   static final byte STORE_TERMVECTOR = 0x2;
Index: src/java/org/apache/lucene/index/FieldsReader.java
===================================================================
--- src/java/org/apache/lucene/index/FieldsReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/FieldsReader.java	(working copy)
@@ -37,17 +37,17 @@
  *
  * @version $Id$
  */
-final class FieldsReader {
-  private final FieldInfos fieldInfos;
+final class FieldsReader implements Cloneable {
+  private FieldInfos fieldInfos;
 
   // The main fieldStream, used only for cloning.
-  private final IndexInput cloneableFieldsStream;
+  private IndexInput cloneableFieldsStream;
 
   // This is a clone of cloneableFieldsStream used for reading documents.
   // It should not be cloned outside of a synchronized context.
-  private final IndexInput fieldsStream;
+  private IndexInput fieldsStream;
 
-  private final IndexInput indexStream;
+  private IndexInput indexStream;
   private int size;
   private boolean closed;
 
@@ -497,6 +497,30 @@
     return bos.toByteArray();
   }
   
+  public Object clone() {
+    FieldsReader clone = null;
+    try {
+      clone = (FieldsReader) super.clone();
+    } catch (CloneNotSupportedException e) {}
+
+    if (clone != null) {
+      clone.fieldInfos = (FieldInfos) this.fieldInfos.clone();
+      
+      if (this.cloneableFieldsStream != null) {
+        clone.cloneableFieldsStream = (IndexInput) this.cloneableFieldsStream.clone();
+      }
+      if (this.fieldsStream != null) {
+        clone.fieldsStream = (IndexInput) this.fieldsStream.clone();
+      }
+      if (this.indexStream != null) {
+        clone.indexStream = (IndexInput) this.indexStream.clone();
+      }
+      clone.fieldsStreamTL = new ThreadLocal();
+    }
+
+    return clone;
+  }
+
   // Instances of this class hold field properties and data
   // for merge
   final static class FieldForMerge extends AbstractField {
Index: src/java/org/apache/lucene/index/FilterIndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/FilterIndexReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/FilterIndexReader.java	(working copy)
@@ -104,6 +104,30 @@
     this.in = in;
   }
 
+  /**
+   * Tries to reopen the underlying IndexReader instance.
+   * (See {@link IndexReader#reopen()}).
+   * <br>
+   * If the underlying IndexReader was refreshed successfully, then
+   * a new instance of FilterIndexReader is returned, otherwise this
+   * instance is returned.
+   * 
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public synchronized IndexReader reopen(boolean closeOldReader) throws CorruptIndexException, IOException {
+    IndexReader reopened = in.reopen(closeOldReader);
+    if (reopened == in) {
+      return this;
+    }
+    
+    if (closeOldReader) {
+      close();
+    }
+    
+    return new FilterIndexReader(reopened);
+  }
+
   public Directory directory() {
     return in.directory();
   }
@@ -227,4 +251,14 @@
     ensureOpen();
     return in.isOptimized();
   }
+  
+  /**
+   * Returns a new instance of FilterIndexReader containing a clone
+   * of the underlying reader, which must support clone. 
+   */
+  public Object clone() throws CloneNotSupportedException {
+    FilterIndexReader clone = (FilterIndexReader) super.clone();
+    clone.in = (IndexReader) in.clone();
+    return clone;
+  }
 }
Index: src/java/org/apache/lucene/index/SegmentInfo.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentInfo.java	(revision 582652)
+++ src/java/org/apache/lucene/index/SegmentInfo.java	(working copy)
@@ -285,6 +285,10 @@
     }
   }
 
+  int getNumFields() {
+    return normGen == null ? 0 : normGen.length;
+  }
+  
   /**
    * Returns true if this field for this segment has saved a separate norms file (_<segment>_N.sX).
    *
Index: src/java/org/apache/lucene/index/ParallelReader.java
===================================================================
--- src/java/org/apache/lucene/index/ParallelReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/ParallelReader.java	(working copy)
@@ -43,7 +43,7 @@
  * same order to the other indexes. <em>Failure to do so will result in
  * undefined behavior</em>.
  */
-public class ParallelReader extends IndexReader {
+public class ParallelReader extends IndexReader implements Cloneable {
   private List readers = new ArrayList();
   private SortedMap fieldToReader = new TreeMap();
   private Map readerToFields = new HashMap();
@@ -91,6 +91,10 @@
       throw new IllegalArgumentException
         ("All readers must have same numDocs: "+numDocs+"!="+reader.numDocs());
 
+    addReader(reader, ignoreStoredFields);
+  }
+  
+  private void addReader(IndexReader reader, boolean ignoreStoredFields) {
     Collection fields = reader.getFieldNames(IndexReader.FieldOption.ALL);
     readerToFields.put(reader, fields);
     Iterator i = fields.iterator();
@@ -103,8 +107,85 @@
     if (!ignoreStoredFields)
       storedFieldReaders.add(reader);             // add to storedFieldReaders
     readers.add(reader);
+    
   }
+  
+  /**
+   * Tries to reopen the subreaders (see {@link IndexReader#reopen()}).
+   * <br>
+   * If one or more subreaders were reopened successfully, then a new
+   * ParallelReader instance is returned, otherwise this instance is returned.
+   * <p>
+   * Note that the subreaders have to support <code>clone()</code> when
+   * this method is used with <code>closeOldReader==false</code>. See
+   * {@link IndexReader#reopen(boolean)} and {@link IndexReader#isCloneSupported()} 
+   * for further details.
 
+   * 
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error 
+   */
+  public synchronized IndexReader reopen(boolean closeOldReaders) throws CorruptIndexException, IOException {
+    boolean reopened = false;
+    List newReaders = new ArrayList();
+    for (int i = 0; i < readers.size(); i++) {
+      IndexReader oldReader = (IndexReader) readers.get(i);
+      IndexReader newReader = oldReader.reopen(closeOldReaders);
+      newReaders.add(newReader);
+      // if at least one of the subreaders was updated we remember that
+      // and return a new MultiReader
+      reopened |= (newReader != oldReader); 
+    }
+
+    if (reopened) {
+      ParallelReader pr = new ParallelReader();
+      for (int i = 0; i < readers.size(); i++) {
+        IndexReader oldReader = (IndexReader) readers.get(i);
+        IndexReader newReader = (IndexReader) newReaders.get(i);
+        if (oldReader == newReader) {
+          if (oldReader instanceof Cloneable) {
+            try {
+              newReader = (IndexReader) oldReader.clone();
+            } catch (CloneNotSupportedException e1) {
+              throw new UnsupportedOperationException("reader#"+i+" could not be cloned: " + oldReader.getClass(), e1);
+            }
+          } else {
+            throw new UnsupportedOperationException("reader#"+i+" is not Cloneable: " + oldReader.getClass());
+          }
+        } 
+        pr.add(newReader, !storedFieldReaders.contains(oldReader));
+      }
+      
+      if (closeOldReaders) {
+        closed = true;
+      }
+
+      return pr;
+    } else {
+      // No subreader was refreshed
+      return this;
+    }
+  }
+  
+  
+  /**
+   * All subreaders must support <code>clone()</code>.
+   */
+  public Object clone() throws CloneNotSupportedException {
+    ParallelReader clone = (ParallelReader) super.clone();
+    clone.readers = new ArrayList();
+    clone.fieldToReader = new TreeMap();
+    clone.readerToFields = new HashMap();
+    clone.storedFieldReaders = new ArrayList();
+    
+    for (int i = 0; i < readers.size(); i++) {
+      IndexReader r = (IndexReader) readers.get(i);
+      clone.addReader((IndexReader) r.clone(), !storedFieldReaders.contains(r));
+    }
+    
+    return clone;
+  }
+
   public int numDocs() {
     // Don't call ensureOpen() here (it could affect performance)
     return numDocs;
Index: src/java/org/apache/lucene/index/SegmentReader.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/SegmentReader.java	(working copy)
@@ -32,9 +32,11 @@
 /**
  * @version $Id$
  */
-class SegmentReader extends DirectoryIndexReader {
+class SegmentReader extends DirectoryIndexReader implements Cloneable {
+  private boolean cloned = false;
   private String segment;
   private SegmentInfo si;
+  private int readBufferSize;
 
   FieldInfos fieldInfos;
   private FieldsReader fieldsReader;
@@ -62,7 +64,7 @@
   CompoundFileReader cfsReader = null;
   CompoundFileReader storeCFSReader = null;
 
-  private class Norm {
+  private class Norm implements Cloneable {
     public Norm(IndexInput in, int number, long normSeek)
     {
       this.in = in;
@@ -99,6 +101,20 @@
       }
       in = null;
     }
+    
+    public Object clone() {
+      Norm clone = null;
+      try {
+        clone = (Norm) super.clone();
+      } catch (CloneNotSupportedException e) {}
+
+      // Clone the bytes array, but not the InputStream
+      if (clone != null && this.bytes != null) {
+        clone.bytes = (byte[]) this.bytes.clone();
+      }
+
+      return clone;
+    }
   }
 
   private Hashtable norms = new Hashtable();
@@ -199,6 +215,7 @@
   private void initialize(SegmentInfo si, int readBufferSize, boolean doOpenStores) throws CorruptIndexException, IOException {
     segment = si.name;
     this.si = si;
+    this.readBufferSize = readBufferSize;
 
     boolean success = false;
 
@@ -249,16 +266,8 @@
 
       tis = new TermInfosReader(cfsDir, segment, fieldInfos, readBufferSize);
       
-      // NOTE: the bitvector is stored using the regular directory, not cfs
-      if (hasDeletions(si)) {
-        deletedDocs = new BitVector(directory(), si.getDelFileName());
-
-        // Verify # deletes does not exceed maxDoc for this segment:
-        if (deletedDocs.count() > maxDoc()) {
-          throw new CorruptIndexException("number of deletes (" + deletedDocs.count() + ") exceeds max doc (" + maxDoc() + ") for segment " + si.name);
-        }
-      }
-
+      loadDeletedDocs();
+      
       // make sure that all index files have been read or are kept open
       // so that if an index update removes them we'll still have them
       freqStream = cfsDir.openInput(segment + ".frq", readBufferSize);
@@ -286,7 +295,151 @@
       }
     }
   }
+  
+  private void loadDeletedDocs() throws IOException {
+    // NOTE: the bitvector is stored using the regular directory, not cfs
+    if (hasDeletions(si)) {
+      deletedDocs = new BitVector(directory(), si.getDelFileName());
 
+      // Verify # deletes does not exceed maxDoc for this segment:
+      if (deletedDocs.count() > maxDoc()) {
+        throw new CorruptIndexException("number of deletes (" + deletedDocs.count() + ") exceeds max doc (" + maxDoc() + ") for segment " + si.name);
+      }
+    }
+  }
+
+  protected DirectoryIndexReader doReopen(SegmentInfos infos, boolean closeOldReader) throws CorruptIndexException, IOException {
+    if (infos.size() == 1) {
+      SegmentInfo si = infos.info(0);
+      if (segment.equals(si.name) && si.getUseCompoundFile() == SegmentReader.this.si.getUseCompoundFile()) {
+        return reopenSegment(si, closeOldReader);
+      } else { 
+        // segment not referenced anymore, reopen not possible
+        // or segment format changed
+        return SegmentReader.get(infos, infos.info(0), false);
+      }
+    } else {
+      return new MultiSegmentReader(directory, infos, true, new SegmentReader[] {this}, closeOldReader);
+    }
+  }
+     
+  
+  SegmentReader reopenSegment(SegmentInfo si, boolean reuseOld) throws CorruptIndexException, IOException {
+    boolean deletionsUpToDate = (this.si.hasDeletions() == si.hasDeletions()) 
+                                  && (!si.hasDeletions() || this.si.getDelFileName().equals(si.getDelFileName()));
+    boolean normsUpToDate = this.si.getNumFields() == si.getNumFields();
+     
+    if (normsUpToDate) {
+      for (int i = 0; i < si.getNumFields(); i++) {
+        if (!this.si.getNormFileName(i).equals(si.getNormFileName(i))) {
+          normsUpToDate = false;
+          break;
+        }
+      }
+    }
+
+    SegmentReader clone;
+    
+    if (!reuseOld) {
+      clone = doClone(deletionsUpToDate, normsUpToDate);
+    } else {
+      clone = doClone(false, false);
+      if (deletionsUpToDate) {
+        clone.deletedDocs = this.deletedDocs;
+      }
+
+      if (normsUpToDate) {
+        clone.norms = this.norms;
+        clone.singleNormStream = this.singleNormStream;
+      }
+    }
+
+    clone.si = si;
+
+    if (!deletionsUpToDate) {
+      // load deleted docs
+      clone.deletedDocs = null;
+      clone.loadDeletedDocs();
+    }
+
+    if (!normsUpToDate) {
+      // load norms
+      clone.norms = new Hashtable();
+      clone.openNorms(si.getUseCompoundFile() ? cfsReader : directory(), readBufferSize);
+    }
+
+    if (reuseOld) {
+      closed = true;
+    }
+   
+    return clone;
+  }
+
+  public Object clone() {
+    return doClone(true, true);
+  }
+
+  private SegmentReader doClone(boolean cloneDeletedDocs, boolean cloneNorms) {
+    SegmentReader clone;
+    try {
+      clone = (SegmentReader) super.clone();
+    } catch (CloneNotSupportedException e1) {
+      throw new RuntimeException("impossible situation: super.clone() throw exception even though this instanceof Clonable", e1);
+    }
+    clone.fieldInfos = fieldInfos;
+    if (cfsReader != null) {
+      clone.cfsReader = (CompoundFileReader) cfsReader.clone();
+    }
+    if (storeCFSReader != null) {
+      clone.storeCFSReader = (CompoundFileReader) storeCFSReader.clone();
+    }
+    if (fieldsReader != null) {
+      clone.fieldsReader = (FieldsReader) fieldsReader.clone();
+    }
+    if (tis != null) {
+      clone.tis = (TermInfosReader) tis.clone();
+    }
+    if (freqStream != null) {
+      clone.freqStream = (IndexInput) freqStream.clone();
+    }
+    if (proxStream != null) {
+      clone.proxStream = (IndexInput) proxStream.clone();
+    }
+    if (termVectorsReaderOrig != null) {
+      clone.termVectorsReaderOrig = (TermVectorsReader) termVectorsReaderOrig.clone();
+    }
+
+    if (cloneDeletedDocs) {
+      if (deletedDocs != null) {
+        clone.deletedDocs = (BitVector) deletedDocs.clone();
+      }
+    }
+
+    clone.norms = new Hashtable();
+    if (cloneNorms) {
+      if (singleNormStream != null) {
+        clone.singleNormStream = (IndexInput) singleNormStream.clone();
+      }
+      
+      Iterator it = this.norms.keySet().iterator();
+      while (it.hasNext()) {
+        String curField = (String) it.next();
+        Norm curNorm = (Norm) this.norms.get(curField);
+        Norm clonedNorm = (Norm) curNorm.clone();
+        if (curNorm.in == singleNormStream) {
+          clonedNorm.in = clone.singleNormStream;
+        } else if (curNorm.in != null) {
+          clonedNorm.in = (IndexInput) curNorm.in.clone();
+        }
+        clone.norms.put(curField, clonedNorm);
+      }
+      
+    }
+
+    clone.cloned = true;
+    return clone;
+  }
+  
   protected void commitChanges() throws IOException {
     if (deletedDocsDirty) {               // re-write deleted
       si.advanceDelGen();
@@ -703,6 +856,13 @@
   String getSegmentName() {
     return segment;
   }
+  
+  /**
+   * Return the SegmentInfo of the segment this reader is reading.
+   */
+  SegmentInfo getSegmentInfo() {
+    return si;
+  }
 
   void setSegmentInfo(SegmentInfo info) {
     si = info;
Index: src/java/org/apache/lucene/index/CompoundFileReader.java
===================================================================
--- src/java/org/apache/lucene/index/CompoundFileReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/CompoundFileReader.java	(working copy)
@@ -35,7 +35,7 @@
  *
  * @version $Id$
  */
-class CompoundFileReader extends Directory {
+class CompoundFileReader extends Directory implements Cloneable {
 
     private int readBufferSize;
 
@@ -199,6 +199,19 @@
         throw new UnsupportedOperationException();
     }
 
+    public Object clone() {
+      CompoundFileReader clone = null;
+      try {
+        clone = (CompoundFileReader) super.clone();
+      } catch (CloneNotSupportedException e) {}
+
+      if (this.stream != null) {
+        clone.stream = (IndexInput) this.stream.clone();
+      }
+      
+      return clone;
+    }
+    
     /** Implementation of an IndexInput that reads from a portion of the
      *  compound file. The visibility is left as "package" *only* because
      *  this helps with testing since JUnit test cases in a different class
Index: src/java/org/apache/lucene/index/TermInfosReader.java
===================================================================
--- src/java/org/apache/lucene/index/TermInfosReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/TermInfosReader.java	(working copy)
@@ -26,7 +26,7 @@
  * Directory.  Pairs are accessed either by Term or by ordinal position the
  * set.  */
 
-final class TermInfosReader {
+final class TermInfosReader implements Cloneable {
   private Directory directory;
   private String segment;
   private FieldInfos fieldInfos;
@@ -232,4 +232,25 @@
     get(term);
     return (SegmentTermEnum)getEnum().clone();
   }
+  
+  public Object clone() {
+    TermInfosReader clone = null;
+
+    try {
+      clone = (TermInfosReader) super.clone();
+    } catch (CloneNotSupportedException e) {}
+
+    if (clone != null) {
+      clone.enumerators = new ThreadLocal();
+      if (this.origEnum != null) {
+        clone.origEnum = (SegmentTermEnum) this.origEnum.clone();
+      }
+      if (this.indexEnum != null) {
+        clone.indexEnum = (SegmentTermEnum) this.indexEnum.clone();
+      }
+    }
+
+    return clone;
+  }
+
 }
Index: src/java/org/apache/lucene/index/IndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/IndexReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/IndexReader.java	(working copy)
@@ -44,6 +44,16 @@
  <p> An IndexReader can be opened on a directory for which an IndexWriter is
  opened already, but it cannot be used to delete documents from the index then.
 
+ <p> IndexReader implements the Cloneable interface. The built-in Lucene 
+ IndexReader implementations support clone() since Lucene 2.3, however, custom 
+ implementations might not support clone(). For backwards compatibility
+ the method {@link #isCloneSupported()} was added that returns false by default.
+ Actual implementations must overwrite this method and <code>clone()</code>
+ in order to allow cloning.<br>
+ Clone() is needed to support {@link #reopen()} for certain IndexReader 
+ implementations ({@link MultiReader}, {@link ParallelReader}). See {@link #reopen()}
+ for further details. 
+
  <p>
  NOTE: for backwards API compatibility, several methods are not listed 
  as abstract, but have no useful implementations in this base class and 
@@ -187,7 +197,71 @@
       }
     }.run();
   }
+  
+  /**
+   * Refreshes an IndexReader if the index has changed since this instance 
+   * was (re)opened. 
+   * <p>
+   * Opening an IndexReader is an expensive operation. This method can be used
+   * to refresh an existing IndexReader to reduce these costs. This method 
+   * tries to only load segments that have changed or were created after the 
+   * IndexReader was opened.
+   * <p>
+   * If the IndexReader could be refreshed successfully then a new IndexReader
+   * instance is returned, the old instance is not closed and remains usable. 
+   * If the index has not changed since, then the same IndexReader instance is 
+   * returned.<br>
+   * You can determine whether a reader was actually reopened by comparing the
+   * old instance with the instance returned by this method: 
+   * <pre>
+   * IndexReader reader = ... 
+   * ...
+   * IndexReader new = r.reopen();
+   * if (new != reader) {
+   *   ...     // reader was reopened
+   *   reader.close(); 
+   * }
+   * reader = new;
+   * ...
+   * </pre>
+   * <p>
+   * Depending on your application it might not be necessary to keep the old
+   * IndexReader instance open. Closing the old instance can speed up the reopen
+   * process depending on the IndexReader implementation. Use {@link #reopen(boolean)}
+   * if you can want to close the old reader. 
+   * <p>
+   * 
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */  
+  public final synchronized IndexReader reopen() throws CorruptIndexException, IOException {
+    return reopen(false);
+  }
 
+  /**
+   * In addition to {@link #reopen()} this methods offers the ability to close
+   * the old IndexReader instance. This speeds up the reopening process for
+   * certain IndexReader implementations and reduces memory consumption, because
+   * resources of the old instance can be reused for the reopened IndexReader
+   * as it avoids the need of copying the resources.
+   * <p>
+   * The reopen performance especially benefits if IndexReader instances returned 
+   * by one of the <code>open()</code> methods are reopened with 
+   * <code>closeOldReader==true</code>.
+   * <p>
+   * Certain IndexReader implementations ({@link MultiReader}, {@link ParallelReader})
+   * require that the subreaders support the clone() operation (see {@link #isCloneSupported()}
+   * in order to perform reopen with <code>closeOldReader==false</code>.  
+   */
+  public synchronized IndexReader reopen(boolean closeOldReader) throws CorruptIndexException, IOException {
+    throw new UnsupportedOperationException("This reader does not support reopen()."); 
+  }
+  
+  public Object clone() throws CloneNotSupportedException {
+    return super.clone();
+  }
+
+
   /** 
    * Returns the directory associated with this index.  The Default 
    * implementation returns the directory specified by subclasses when 
@@ -733,6 +807,7 @@
     if (!closed) {
       commit();
       doClose();
+      closed = true;
     }
   }
 
Index: src/java/org/apache/lucene/index/MultiSegmentReader.java
===================================================================
--- src/java/org/apache/lucene/index/MultiSegmentReader.java	(revision 582652)
+++ src/java/org/apache/lucene/index/MultiSegmentReader.java	(working copy)
@@ -23,14 +23,16 @@
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Map;
 import java.util.Set;
 
 /** 
  * An IndexReader which reads indexes with multiple segments.
  */
-class MultiSegmentReader extends DirectoryIndexReader {
+class MultiSegmentReader extends DirectoryIndexReader implements Cloneable {
   protected SegmentReader[] subReaders;
   private int[] starts;                           // 1st docno for each segment
   private Hashtable normsCache = new Hashtable();
@@ -40,26 +42,46 @@
 
   /** Construct reading the named set of readers. */
   MultiSegmentReader(Directory directory, SegmentInfos sis, boolean closeDirectory) throws IOException {
-    super(directory, sis, closeDirectory);
-    // To reduce the chance of hitting FileNotFound
-    // (and having to retry), we open segments in
-    // reverse because IndexWriter merges & deletes
-    // the newest segments first.
+    this(directory, sis, closeDirectory, null, false);
+  }
+  
+  MultiSegmentReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, boolean closeOldReaders) throws IOException {
+    super(directory, infos, closeDirectory);
+    // the index has multiple segments
+    Map segmentReaders = new HashMap();
 
-    SegmentReader[] readers = new SegmentReader[sis.size()];
-    for (int i = sis.size()-1; i >= 0; i--) {
-      try {
-        readers[i] = SegmentReader.get(sis.info(i));
-      } catch (IOException e) {
-        // Close all readers we had opened:
-        for(i++;i<sis.size();i++) {
-          readers[i].close();
-        }
-        throw e;
+    if (oldReaders != null) {
+      // create a Map SegmentName->SegmentReader
+      for (int i = 0; i < oldReaders.length; i++) {
+        segmentReaders.put(((SegmentReader) oldReaders[i]).getSegmentName(), oldReaders[i]);
       }
     }
 
-    initialize(readers);
+   SegmentReader[] newReaders = new SegmentReader[infos.size()];
+   boolean[] closeAfterException = new boolean[infos.size()];
+
+   for (int i = infos.size() - 1; i>=0; i--) {
+     newReaders[i] = (SegmentReader) segmentReaders.get(infos.info(i).name);
+
+     try {
+       if (newReaders[i] == null || infos.info(i).getUseCompoundFile() != newReaders[i].getSegmentInfo().getUseCompoundFile()) {
+         newReaders[i] = SegmentReader.get(infos.info(i));
+         // this is a new reader; in case we hit an exception we can close it safely
+         closeAfterException[i] = true;
+       } else {
+         newReaders[i] = (SegmentReader) newReaders[i].reopenSegment(infos.info(i), closeOldReaders);
+       }
+     } catch (IOException e) {
+       for (i++; i < infos.size(); i++) {
+         if (closeAfterException[i]) {
+           newReaders[i].close();
+         }
+       }
+       throw e;
+     }
+   }
+
+    initialize(newReaders);
   }
 
   private void initialize(SegmentReader[] subReaders) {
@@ -74,8 +96,37 @@
     }
     starts[subReaders.length] = maxDoc;
   }
+  
+  public Object clone() throws CloneNotSupportedException {
+    MultiSegmentReader clone = null;
 
+    clone = (MultiSegmentReader) super.clone();
 
+    if (clone != null) {
+      clone.subReaders = new SegmentReader[this.subReaders.length];
+      for (int i = 0; i < this.subReaders.length; i++) {
+        clone.subReaders[i] = (SegmentReader) this.subReaders[i].clone();
+      }
+      clone.starts = new int[this.starts.length];
+      System.arraycopy(this.starts, 0, clone.starts, 0, this.starts.length);
+
+      clone.normsCache = new Hashtable();
+      
+    }
+    return clone;
+  }
+  
+  protected DirectoryIndexReader doReopen(SegmentInfos infos, boolean closeOldReaders) throws CorruptIndexException, IOException {
+    if (infos.size() == 1) {
+      // The index has only one segment now, so we can't refresh the MultiSegmentReader.
+      // Return a new SegmentReader instead
+      return SegmentReader.get(infos, infos.info(0), false);
+    } else {
+      SegmentReader[] oldReaders = subReaders;
+      return new MultiSegmentReader(directory, infos, closeDirectory, oldReaders, closeOldReaders);
+    }            
+  }
+
   public TermFreqVector[] getTermFreqVectors(int n) throws IOException {
     ensureOpen();
     int i = readerIndex(n);        // find segment num
Index: src/java/org/apache/lucene/index/SegmentInfos.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentInfos.java	(revision 582652)
+++ src/java/org/apache/lucene/index/SegmentInfos.java	(working copy)
@@ -27,7 +27,7 @@
 import java.io.PrintStream;
 import java.util.Vector;
 
-final class SegmentInfos extends Vector {
+final class SegmentInfos extends Vector implements Cloneable {
   
   
   /** The file format version, a negative number. */
Index: src/java/org/apache/lucene/util/BitVector.java
===================================================================
--- src/java/org/apache/lucene/util/BitVector.java	(revision 582652)
+++ src/java/org/apache/lucene/util/BitVector.java	(working copy)
@@ -35,7 +35,7 @@
 
   @version $Id$
   */
-public final class BitVector {
+public final class BitVector implements Cloneable {
 
   private byte[] bits;
   private int size;
@@ -211,4 +211,14 @@
     }          
   }
   
+  public Object clone() {
+    BitVector clone = null;
+    try {
+      clone = (BitVector) super.clone();
+    } catch (CloneNotSupportedException e) {}
+
+    clone.bits = new byte[this.bits.length];
+    System.arraycopy(this.bits, 0, clone.bits, 0, this.bits.length);
+    return clone;
+  }
 }
