Index: src/java/org/apache/lucene/index/SegmentReader.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/SegmentReader.java	(working copy)
@@ -58,6 +58,9 @@
   private boolean normsDirty = false;
   private int pendingDeleteCount;
 
+  // The SegmentReaderFactory this SegmentReader was opened with
+  private SegmentReaderFactory factory = SegmentReaderFactory.getDefault();
+
   private boolean rollbackHasChanges = false;
   private boolean rollbackDeletedDocsDirty = false;
   private boolean rollbackNormsDirty = false;
@@ -69,6 +72,13 @@
 
   CoreReaders core;
 
+  /**
+   * Called after the the SegmentReader has been initialized.
+   */
+  protected void init() throws IOException {
+    // nothing by default
+  }
+
   // Holds core readers that are shared (unchanged) when
   // SegmentReader is cloned or reopened
   static final class CoreReaders {
@@ -558,6 +568,14 @@
    * @throws CorruptIndexException if the index is corrupt
    * @throws IOException if there is a low-level IO error
    */
+  public static SegmentReader get(boolean readOnly, SegmentInfo si, int termInfosIndexDivisor, SegmentReaderFactory factory) throws CorruptIndexException, IOException {
+    return get(readOnly, si.dir, si, BufferedIndexInput.BUFFER_SIZE, true, termInfosIndexDivisor, factory);
+  }
+
+  /**
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
   public static SegmentReader get(boolean readOnly,
                                   Directory dir,
                                   SegmentInfo si,
@@ -565,7 +583,24 @@
                                   boolean doOpenStores,
                                   int termInfosIndexDivisor)
     throws CorruptIndexException, IOException {
-    SegmentReader instance = readOnly ? new ReadOnlySegmentReader() : new SegmentReader();
+    return get(readOnly, dir, si, readBufferSize, doOpenStores, termInfosIndexDivisor, SegmentReaderFactory.getDefault());
+  }
+
+
+  /**
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static SegmentReader get(boolean readOnly,
+                                  Directory dir,
+                                  SegmentInfo si,
+                                  int readBufferSize,
+                                  boolean doOpenStores,
+                                  int termInfosIndexDivisor,
+                                  SegmentReaderFactory factory)
+    throws CorruptIndexException, IOException {
+    SegmentReader instance = factory.open(readOnly);
+    instance.factory = factory;
     instance.readOnly = readOnly;
     instance.si = si;
     instance.readBufferSize = readBufferSize;
@@ -579,6 +614,10 @@
       }
       instance.loadDeletedDocs();
       instance.openNorms(instance.core.cfsDir, readBufferSize);
+
+      // Allow subclass to perform some initialization
+      instance.init();
+
       success = true;
     } finally {
 
@@ -675,7 +714,7 @@
     assert !doClone || (normsUpToDate && deletionsUpToDate);
 
     // clone reader
-    SegmentReader clone = openReadOnly ? new ReadOnlySegmentReader() : new SegmentReader();
+    SegmentReader clone = factory.reopen(this, openReadOnly);
 
     boolean success = false;
     try {
@@ -730,6 +769,9 @@
       // any norms that have changed:
       clone.openNorms(si.getUseCompoundFile() ? core.getCFSReader() : directory(), readBufferSize);
 
+      // Allow subclass to do some initialization
+      clone.init();
+
       success = true;
     } finally {
       if (!success) {
Index: src/java/org/apache/lucene/index/SegmentReaderFactory.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentReaderFactory.java	(revision 0)
+++ src/java/org/apache/lucene/index/SegmentReaderFactory.java	(revision 0)
@@ -0,0 +1,35 @@
+package org.apache.lucene.index;
+
+import java.io.IOException;
+
+/**
+ * A Factory for creating SegmentReader instances.
+ *
+ * <p>This makes it possible to subclass SegmentReader.</p>
+ *
+ * <p>EXPERT: this API will likely change in future versions.</p>
+ */
+public class SegmentReaderFactory {
+  private static final SegmentReaderFactory DEFAULT = new SegmentReaderFactory();
+
+  /**
+   * Create a new SegmentReader instance.
+   */
+  public SegmentReader open(boolean readOnly) throws IOException {
+    return readOnly ? new ReadOnlySegmentReader() : new SegmentReader();
+  }
+
+  /**
+   * Reopen a SegmentReader instance.
+   */
+  public SegmentReader reopen(SegmentReader reader, boolean readOnly) throws IOException {
+    return open(readOnly);
+  }
+
+  /**
+   * Get the default SegmentReaderFactory.
+   */
+  public static SegmentReaderFactory getDefault() {
+    return DEFAULT;
+  }
+}
\ No newline at end of file

Property changes on: src/java/org/apache/lucene/index/SegmentReaderFactory.java
___________________________________________________________________
Added: svn:eol-style
   + native

Index: src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java
===================================================================
--- src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java	(working copy)
@@ -23,13 +23,13 @@
 import java.util.Map;
 
 class ReadOnlyDirectoryReader extends DirectoryReader {
-  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, int termInfosIndexDivisor) throws IOException {
-    super(directory, sis, deletionPolicy, true, termInfosIndexDivisor);
+  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, int termInfosIndexDivisor, SegmentReaderFactory factory) throws IOException {
+    super(directory, sis, deletionPolicy, true, termInfosIndexDivisor, factory);
   }
 
   ReadOnlyDirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts,  Map<String,byte[]> oldNormsCache, boolean doClone,
-                          int termInfosIndexDivisor) throws IOException {
-    super(directory, infos, oldReaders, oldStarts, oldNormsCache, true, doClone, termInfosIndexDivisor);
+                          int termInfosIndexDivisor, SegmentReaderFactory factory) throws IOException {
+    super(directory, infos, oldReaders, oldStarts, oldNormsCache, true, doClone, termInfosIndexDivisor, factory);
   }
   
   ReadOnlyDirectoryReader(IndexWriter writer, SegmentInfos infos, int termInfosIndexDivisor) throws IOException {
Index: src/java/org/apache/lucene/index/DirectoryReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/DirectoryReader.java	(working copy)
@@ -63,25 +63,32 @@
   private int maxDoc = 0;
   private int numDocs = -1;
   private boolean hasDeletions = false;
+  private SegmentReaderFactory factory; // factory for initializing SegmentReaders
 
   static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly,
                           final int termInfosIndexDivisor) throws CorruptIndexException, IOException {
+    return open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, SegmentReaderFactory.getDefault());
+  }
+
+  static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly,
+                          final int termInfosIndexDivisor, final SegmentReaderFactory factory) throws CorruptIndexException, IOException {
     return (IndexReader) new SegmentInfos.FindSegmentsFile(directory) {
       @Override
       protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
         SegmentInfos infos = new SegmentInfos();
         infos.read(directory, segmentFileName);
         if (readOnly)
-          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy, termInfosIndexDivisor);
+          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy, termInfosIndexDivisor, factory);
         else
-          return new DirectoryReader(directory, infos, deletionPolicy, false, termInfosIndexDivisor);
+          return new DirectoryReader(directory, infos, deletionPolicy, false, termInfosIndexDivisor, factory);
       }
     }.run(commit);
   }
 
   /** Construct reading the named set of readers. */
-  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor) throws IOException {
+  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, SegmentReaderFactory factory) throws IOException {
     this.directory = directory;
+    this.factory = factory;
     this.readOnly = readOnly;
     this.segmentInfos = sis;
     this.deletionPolicy = deletionPolicy;
@@ -102,7 +109,7 @@
     for (int i = sis.size()-1; i >= 0; i--) {
       boolean success = false;
       try {
-        readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor);
+        readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor, factory);
         success = true;
       } finally {
         if (!success) {
@@ -125,6 +132,7 @@
   DirectoryReader(IndexWriter writer, SegmentInfos infos, int termInfosIndexDivisor) throws IOException {
     this.directory = writer.getDirectory();
     this.readOnly = true;
+    this.factory = writer.getSegmentReaderFactory();
     this.segmentInfos = infos;
     segmentInfosStart = (SegmentInfos) infos.clone();
     this.termInfosIndexDivisor = termInfosIndexDivisor;
@@ -178,9 +186,10 @@
 
   /** This constructor is only used for {@link #reopen()} */
   DirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts,
-                  Map<String,byte[]> oldNormsCache, boolean readOnly, boolean doClone, int termInfosIndexDivisor) throws IOException {
+                  Map<String,byte[]> oldNormsCache, boolean readOnly, boolean doClone, int termInfosIndexDivisor, SegmentReaderFactory factory) throws IOException {
     this.directory = directory;
     this.readOnly = readOnly;
+    this.factory = factory;
     this.segmentInfos = infos;
     this.termInfosIndexDivisor = termInfosIndexDivisor;
     if (!readOnly) {
@@ -226,7 +235,7 @@
           assert !doClone;
 
           // this is a new reader; in case we hit an exception we can close it safely
-          newReader = SegmentReader.get(readOnly, infos.info(i), termInfosIndexDivisor);
+          newReader = SegmentReader.get(readOnly, infos.info(i), termInfosIndexDivisor, factory);
         } else {
           newReader = newReaders[i].reopenSegment(infos.info(i), doClone, readOnly);
         }
@@ -438,9 +447,9 @@
   private synchronized DirectoryReader doReopen(SegmentInfos infos, boolean doClone, boolean openReadOnly) throws CorruptIndexException, IOException {
     DirectoryReader reader;
     if (openReadOnly) {
-      reader = new ReadOnlyDirectoryReader(directory, infos, subReaders, starts, normsCache, doClone, termInfosIndexDivisor);
+      reader = new ReadOnlyDirectoryReader(directory, infos, subReaders, starts, normsCache, doClone, termInfosIndexDivisor, factory);
     } else {
-      reader = new DirectoryReader(directory, infos, subReaders, starts, normsCache, false, doClone, termInfosIndexDivisor);
+      reader = new DirectoryReader(directory, infos, subReaders, starts, normsCache, false, doClone, termInfosIndexDivisor, factory);
     }
     return reader;
   }
Index: src/java/org/apache/lucene/index/IndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/IndexReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/IndexReader.java	(working copy)
@@ -263,6 +263,36 @@
   }
 
   /** Expert: returns an IndexReader reading the index in
+   *  the given Directory, with a custom {@link
+   *  IndexDeletionPolicy}.  You should pass readOnly=true,
+   *  since it gives much better concurrent performance,
+   *  unless you intend to do write operations (delete
+   *  documents or change norms) with the reader.
+   * @param directory the index directory
+   * @param deletionPolicy a custom deletion policy (only used
+   *  if you use this reader to perform deletes or to set
+   *  norms); see {@link IndexWriter} for details.
+   * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader
+   * @param termInfosIndexDivisor Subsamples which indexed
+   *  terms are loaded into RAM. This has the same effect as {@link
+   *  IndexWriter#setTermIndexInterval} except that setting
+   *  must be done at indexing time while this setting can be
+   *  set per reader.  When set to N, then one in every
+   *  N*termIndexInterval terms in the index is loaded into
+   *  memory.  By setting this to a value > 1 you can reduce
+   *  memory usage, at the expense of higher latency when
+   *  loading a TermInfo.  The default value is 1.  Set this
+   *  to -1 to skip loading the terms index entirely.
+   * @param factory The {@link SegmentReaderFactory} used to construct underlaying SegmentReaders
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, SegmentReaderFactory factory) throws CorruptIndexException, IOException {
+    return open(directory, deletionPolicy, null, readOnly, termInfosIndexDivisor, factory);
+  }
+
+
+  /** Expert: returns an IndexReader reading the index in
    *  the given Directory, using a specific commit and with
    *  a custom {@link IndexDeletionPolicy}.  You should pass
    *  readOnly=true, since it gives much better concurrent
@@ -312,10 +342,46 @@
     return open(commit.getDirectory(), deletionPolicy, commit, readOnly, termInfosIndexDivisor);
   }
 
+  /** Expert: returns an IndexReader reading the index in
+   *  the given Directory, using a specific commit and with
+   *  a custom {@link IndexDeletionPolicy}.  You should pass
+   *  readOnly=true, since it gives much better concurrent
+   *  performance, unless you intend to do write operations
+   *  (delete documents or change norms) with the reader.
+   * @param commit the specific {@link IndexCommit} to open;
+   * see {@link IndexReader#listCommits} to list all commits
+   * in a directory
+   * @param deletionPolicy a custom deletion policy (only used
+   *  if you use this reader to perform deletes or to set
+   *  norms); see {@link IndexWriter} for details.
+   * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader
+   * @param termInfosIndexDivisor Subsamples which indexed
+   *  terms are loaded into RAM. This has the same effect as {@link
+   *  IndexWriter#setTermIndexInterval} except that setting
+   *  must be done at indexing time while this setting can be
+   *  set per reader.  When set to N, then one in every
+   *  N*termIndexInterval terms in the index is loaded into
+   *  memory.  By setting this to a value > 1 you can reduce
+   *  memory usage, at the expense of higher latency when
+   *  loading a TermInfo.  The default value is 1.  Set this
+   *  to -1 to skip loading the terms index entirely.
+   * @param factory The {@link SegmentReaderFactory} used to construct underlaying SegmentReaders
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, SegmentReaderFactory factory) throws CorruptIndexException, IOException {
+    return open(commit.getDirectory(), deletionPolicy, commit, readOnly, termInfosIndexDivisor, factory);
+  }
+
+
   private static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly, int termInfosIndexDivisor) throws CorruptIndexException, IOException {
-    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor);
+    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, SegmentReaderFactory.getDefault());
   }
 
+  private static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly, int termInfosIndexDivisor, SegmentReaderFactory factory) throws CorruptIndexException, IOException {
+    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, factory);
+  }
+
   /**
    * Refreshes an IndexReader if the index has changed since this instance 
    * was (re)opened. 
Index: src/java/org/apache/lucene/index/IndexWriter.java
===================================================================
--- src/java/org/apache/lucene/index/IndexWriter.java	(revision 927380)
+++ src/java/org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -265,6 +265,7 @@
 
   private DocumentsWriter docWriter;
   private IndexFileDeleter deleter;
+  private SegmentReaderFactory segmentReaderFactory = SegmentReaderFactory.getDefault();
 
   private Set<SegmentInfo> segmentsToOptimize = new HashSet<SegmentInfo>();           // used by optimize to note those needing optimization
 
@@ -4850,6 +4851,21 @@
     return mergedSegmentWarmer;
   }
 
+  /**
+   * Set the {@link SegmentReaderFactory} used to initialize SegmentReaders.
+   */
+  public void setSegmentReaderFactory(SegmentReaderFactory factory) {
+    segmentReaderFactory = factory;
+  }
+
+  /**
+   * Get the {@link SegmentReaderFactory} used to initialize SegmentReaders.
+   */
+  public SegmentReaderFactory getSegmentReaderFactory() {
+    return segmentReaderFactory;
+  }
+
+
   private void handleOOM(OutOfMemoryError oom, String location) {
     if (infoStream != null) {
       message("hit OutOfMemoryError inside " + location);
