Index: src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java	(revision 0)
@@ -0,0 +1,115 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/* :TODO: javadocs */
+public class ByteArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse bytes from document fields.
+   */
+  public interface ByteParser {
+    /** Return a byte representation of this field's value. */
+    public byte parseByte(String string);
+  }
+
+  private static final ByteParser DEFAULT_PARSER = new ByteParser() {
+      public byte parseByte(String value) {
+        return Byte.parseByte(value);
+      }
+    };
+
+  String field;
+  ByteParser parser = DEFAULT_PARSER;
+    
+  public ByteArrayCacheKey(String f, ByteParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public ByteArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final ByteArrayCacheKey other = (ByteArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final byte[] retArray = new byte[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        byte termval = parser.parseByte(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    byte[] results = new byte[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      byte[] src = (byte[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java	(revision 0)
@@ -0,0 +1,122 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable floats.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class FloatArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse floats from document fields.
+   */
+  public interface FloatParser {
+    /** Return a float representation of this field's value. */
+    public float parseFloat(String string);
+  }
+
+  private static final FloatParser DEFAULT_PARSER = new FloatParser() {
+      public float parseFloat(String value) {
+        return Float.parseFloat(value);
+      }
+    };
+
+  String field;
+  FloatParser parser = DEFAULT_PARSER;
+    
+  public FloatArrayCacheKey(String f, FloatParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public FloatArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final FloatArrayCacheKey other = (FloatArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building float data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final float[] retArray = new float[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        float termval = parser.parseFloat(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building float data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    float[] results = new float[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      float[] src = (float[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java	(revision 0)
@@ -0,0 +1,99 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable Strings.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class StringArrayCacheKey extends CacheKey {
+
+  String field;
+    
+  public StringArrayCacheKey(String f) {
+    field = f.intern();
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result =  prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final StringArrayCacheKey other = (StringArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    return true;
+  }
+
+
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final String[] retArray = new String[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        String termval = term.text();
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    String[] results = new String[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      String[] src = (String[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/Cache.java
===================================================================
--- src/java/org/apache/lucene/index/cache/Cache.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/Cache.java	(revision 0)
@@ -0,0 +1,54 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+
+/**
+ * A simple Interface for modeling some form of Cache
+ */
+public interface Cache {
+
+  /**
+   * Get data from the Cache, returns null if the key is not found 
+   * in the cache.
+   */
+  public CacheData get(CacheKey key);
+
+  /**
+   * Puts data in the Cache.
+   */
+  public void put(CacheKey key, CacheData data);
+  /**
+   * returns true if the Cache contains data for the specified key.
+   */
+  public boolean containsKey(CacheKey key);
+
+  /**
+   * Called when this Cache will no longer be used anymore, so that 
+   * it can free any external resources it may have.
+   */
+  public void close();
+  
+  public static CacheFactory FACTORY = new CacheFactory() {
+    public Cache getCache(IndexReader r) {
+      return new SimpleMapCache();
+    }
+  };
+
+}
Index: src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java	(revision 0)
@@ -0,0 +1,124 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+
+/** 
+ * A Key for identifying cacheable doubles.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class DoubleArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse doubles from document fields.
+   */
+  public interface DoubleParser {
+    /** Return a double representation of this field's value. */
+    public double parseDouble(String string);
+  }
+
+  private static final DoubleParser DEFAULT_PARSER = new DoubleParser() {
+      public double parseDouble(String value) {
+        return Double.parseDouble(value);
+      }
+    };
+
+  String field;
+  DoubleParser parser = DEFAULT_PARSER;
+    
+  public DoubleArrayCacheKey(String f, DoubleParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public DoubleArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final DoubleArrayCacheKey other = (DoubleArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building double data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final double[] retArray = new double[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        double termval = parser.parseDouble(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building double data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    double[] results = new double[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      double[] src = (double[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java	(revision 0)
@@ -0,0 +1,123 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable ints.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class IntArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse ints from document fields.
+   */
+  public interface IntParser {
+    /** Return an integer representation of this field's value. */
+    public int parseInt(String string);
+  }
+
+  private static final IntParser DEFAULT_PARSER = new IntParser() {
+      public int parseInt(String value) {
+        return Integer.parseInt(value);
+      }
+    };
+  
+  String field;
+  IntParser parser = DEFAULT_PARSER;
+    
+  public IntArrayCacheKey(String f, IntParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public IntArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result =  prime  + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+  
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final IntArrayCacheKey other = (IntArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building int data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final int[] retArray = new int[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        int termval = parser.parseInt(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building int data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+  
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    System.out.println("merging ints");
+    int[] results = new int[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      int[] src = (int[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/CacheFactory.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheFactory.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheFactory.java	(revision 0)
@@ -0,0 +1,33 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+
+/**
+ * 
+ *
+ */
+public interface CacheFactory {
+
+  public Cache getCache(IndexReader r) throws IOException;
+
+}
Index: src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java	(revision 0)
@@ -0,0 +1,106 @@
+package org.apache.lucene.index.cache;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+/** 
+ * A Key for identifying cacheable longs.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class LongArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse longs from document fields.
+   */
+  public interface LongParser {
+    /** Return a long representation of this field's value. */
+    public long parseLong(String string);
+  }
+
+  private static final LongParser DEFAULT_PARSER = new LongParser() {
+      public long parseLong(String value) {
+        return Long.parseLong(value);
+      }
+    };
+
+  String field;
+  LongParser parser = DEFAULT_PARSER;
+    
+  public LongArrayCacheKey(String f, LongParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public LongArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final LongArrayCacheKey other = (LongArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building long data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final long[] retArray = new long[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        long termval = parser.parseLong(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building long data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    long[] results = new long[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      long[] src = (long[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java	(revision 0)
@@ -0,0 +1,105 @@
+package org.apache.lucene.index.cache;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+/** 
+ * A Key for identifying cacheable shorts.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class ShortArrayCacheKey extends CacheKey {
+  /** 
+   * Interface to parse shortss from document fields.
+   */
+  public interface ShortParser {
+    /** Return a short representation of this field's value. */
+    public short parseShort(String string);
+  }
+
+  private static final ShortParser DEFAULT_PARSER = new ShortParser() {
+      public short parseShort(String value) {
+        return Short.parseShort(value);
+      }
+    };
+
+  String field;
+  ShortParser parser = DEFAULT_PARSER;
+    
+  public ShortArrayCacheKey(String f, ShortParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public ShortArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime  + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+  
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final ShortArrayCacheKey other = (ShortArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building short data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final short[] retArray = new short[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        short termval = parser.parseShort(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building short data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    short[] results = new short[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      short[] src = (short[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/SimpleMapCache.java
===================================================================
--- src/java/org/apache/lucene/index/cache/SimpleMapCache.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/SimpleMapCache.java	(revision 0)
@@ -0,0 +1,55 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+
+
+/**
+ * A simple Map based Cache.
+ *
+ */
+public class SimpleMapCache implements Cache {
+
+  private Map data = new HashMap();
+  
+  public SimpleMapCache() { /* NOOP */ }
+
+  public synchronized void close() { /* NOOP */ }
+
+  public synchronized void put(CacheKey k, CacheData v) {
+    data.put(k, v);
+  }
+
+  public synchronized boolean containsKey(CacheKey key) {
+    return data.containsKey(key);
+  }
+  public synchronized Set keySet() {
+    return Collections.unmodifiableSet(data.keySet());
+  }
+
+  public synchronized CacheData get(CacheKey key) {
+    return (CacheData) data.get(key);
+  }
+ 
+}
Index: src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java	(revision 0)
@@ -0,0 +1,131 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable Strings. Caches all unique Strings as well 
+ * as lookup indexes.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class StringIndexCacheKey extends CacheKey {
+
+  public static class StringIndex {
+
+    /** All the term values, in natural order. */
+    public final String[] lookup;
+
+    /** For each document, an index into the lookup array. */
+    public final int[] order;
+
+    /** Creates one of these objects */
+    public StringIndex (int[] values, String[] lookup) {
+      this.order = values;
+      this.lookup = lookup;
+    }
+  }
+
+  String field;
+    
+  public StringIndexCacheKey(String f) {
+    field = f.intern();
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final StringIndexCacheKey other = (StringIndexCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    return true;
+  }
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final int[] retArray = new int[reader.maxDoc()];
+    String[] mterms = new String[reader.maxDoc()+1];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    int t = 0;  // current term number
+    
+    // an entry for documents that have no terms in this field
+    // should a document with no terms be at top or bottom?
+    // this puts them at the top - if it is changed, FieldDocSortedHitQueue
+    // needs to change as well.
+    mterms[t++] = null;
+    
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        
+        // store term text
+        // we expect that there is at most one term per document
+        if (t >= mterms.length) 
+          throw new RuntimeException 
+            ("there are more terms than documents in field \"" + field + 
+             "\", but it's impossible to sort on tokenized fields");
+        mterms[t] = term.text();
+        
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = t;
+        }
+        
+        t++;
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    
+    if (t == 0) {
+      // if there are no terms, make the term array
+      // have a single null entry
+      mterms = new String[1];
+    } else if (t < mterms.length) {
+      // if there are less terms than documents,
+      // trim off the dead array space
+      String[] terms = new String[t];
+      System.arraycopy (mterms, 0, terms, 0, t);
+      mterms = terms;
+    }
+    
+    return new CacheData(new StringIndex (retArray, mterms));
+
+  }
+}
Index: src/java/org/apache/lucene/index/cache/CacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheKey.java	(revision 0)
@@ -0,0 +1,129 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable data.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public abstract class CacheKey {
+
+  public abstract boolean equals(Object o);
+  public abstract int hashCode();
+
+  /**
+   * Builds up the CacheData associated with this Key for the 
+   * specified IndexReader.
+   */
+  public abstract CacheData buildData(IndexReader r) throws IOException;
+
+  /**
+   * Merges the CacheData returned by buildData for various IndexReaders 
+   * such that the result is the same as if buildData had been called on a 
+   * MultiReader wrapping those IndexReaders.
+   *
+   * @param starts from a MultiReader, n+1 elements, where the first n are the starting offsets of each IndexReader and the last element is the total maxDoc.
+   * @param data n elements resulting from n calls to buildData
+   * @exception UnsupportedOperationException unless isMergable returns true.
+   */
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+
+    throw new UnsupportedOperationException
+      ("data from this CacheKey cannot be merged");
+  }
+
+  /**
+   * Returns true if mergeData is a supported method for this CacheKey
+   *
+   * @see #mergeData
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /** The pattern used to detect integer values in a field */
+  /** removed for java 1.3 compatibility
+   protected static final Pattern pIntegers = Pattern.compile ("[0-9\\-]+");
+   **/
+
+  /** The pattern used to detect float values in a field */
+  /**
+   * removed for java 1.3 compatibility
+   * protected static final Object pFloats = Pattern.compile ("[0-9+\\-\\.eEfFdD]+");
+   */
+  
+  public static CacheKey getAutoCacheKey(IndexReader reader, String field) throws IOException {
+    field = ((String)field).intern();
+    TermEnum enumerator = reader.terms (new Term (field, ""));
+    try {
+      Term term = enumerator.term();
+      if (term == null) {
+        throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
+      }
+      CacheKey ret = null;
+      if (term.field() == field) {
+        String termtext = term.text().trim();
+        System.out.println("test auto term:" + termtext);
+
+        /**
+         * Java 1.4 level code:
+
+         if (pIntegers.matcher(termtext).matches())
+         return IntegerSortedHitQueue.comparator (reader, enumerator, field);
+
+         else if (pFloats.matcher(termtext).matches())
+         return FloatSortedHitQueue.comparator (reader, enumerator, field);
+         */
+
+        // Java 1.3 level code:
+        try {
+          Integer.parseInt (termtext);
+          ret = new IntArrayCacheKey(field);
+        } catch (NumberFormatException nfe1) {
+          try {
+            Long.parseLong(termtext);
+            ret = new LongArrayCacheKey(field);
+          } catch (NumberFormatException nfe2) {
+            try {
+              Float.parseFloat (termtext);
+              ret = new FloatArrayCacheKey(field);
+            } catch (NumberFormatException nfe3) {
+              ret = new StringArrayCacheKey(field);
+            }
+          }
+        }
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+      }
+      return ret;
+    } finally {
+      enumerator.close();
+    }
+  
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/CacheData.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheData.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheData.java	(revision 0)
@@ -0,0 +1,31 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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.
+ */
+
+
+/**
+ * Encapsulates cached data
+ *
+ */
+public class CacheData {
+  public CacheData(Object payload) {
+    this.payload = payload;
+  }
+  private Object payload;
+  public Object getPayload() { return payload; }
+}
Index: src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ByteArrayCacheKey.java	(revision 0)
@@ -0,0 +1,115 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/* :TODO: javadocs */
+public class ByteArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse bytes from document fields.
+   */
+  public interface ByteParser {
+    /** Return a byte representation of this field's value. */
+    public byte parseByte(String string);
+  }
+
+  private static final ByteParser DEFAULT_PARSER = new ByteParser() {
+      public byte parseByte(String value) {
+        return Byte.parseByte(value);
+      }
+    };
+
+  String field;
+  ByteParser parser = DEFAULT_PARSER;
+    
+  public ByteArrayCacheKey(String f, ByteParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public ByteArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final ByteArrayCacheKey other = (ByteArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final byte[] retArray = new byte[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        byte termval = parser.parseByte(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    byte[] results = new byte[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      byte[] src = (byte[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/Cache.java
===================================================================
--- src/java/org/apache/lucene/index/cache/Cache.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/Cache.java	(revision 0)
@@ -0,0 +1,54 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+
+/**
+ * A simple Interface for modeling some form of Cache
+ */
+public interface Cache {
+
+  /**
+   * Get data from the Cache, returns null if the key is not found 
+   * in the cache.
+   */
+  public CacheData get(CacheKey key);
+
+  /**
+   * Puts data in the Cache.
+   */
+  public void put(CacheKey key, CacheData data);
+  /**
+   * returns true if the Cache contains data for the specified key.
+   */
+  public boolean containsKey(CacheKey key);
+
+  /**
+   * Called when this Cache will no longer be used anymore, so that 
+   * it can free any external resources it may have.
+   */
+  public void close();
+  
+  public static CacheFactory FACTORY = new CacheFactory() {
+    public Cache getCache(IndexReader r) {
+      return new SimpleMapCache();
+    }
+  };
+
+}
Index: src/java/org/apache/lucene/index/cache/CacheData.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheData.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheData.java	(revision 0)
@@ -0,0 +1,31 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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.
+ */
+
+
+/**
+ * Encapsulates cached data
+ *
+ */
+public class CacheData {
+  public CacheData(Object payload) {
+    this.payload = payload;
+  }
+  private Object payload;
+  public Object getPayload() { return payload; }
+}
Index: src/java/org/apache/lucene/index/cache/CacheFactory.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheFactory.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheFactory.java	(revision 0)
@@ -0,0 +1,33 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+
+/**
+ * 
+ *
+ */
+public interface CacheFactory {
+
+  public Cache getCache(IndexReader r) throws IOException;
+
+}
Index: src/java/org/apache/lucene/index/cache/CacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/CacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/CacheKey.java	(revision 0)
@@ -0,0 +1,129 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable data.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public abstract class CacheKey {
+
+  public abstract boolean equals(Object o);
+  public abstract int hashCode();
+
+  /**
+   * Builds up the CacheData associated with this Key for the 
+   * specified IndexReader.
+   */
+  public abstract CacheData buildData(IndexReader r) throws IOException;
+
+  /**
+   * Merges the CacheData returned by buildData for various IndexReaders 
+   * such that the result is the same as if buildData had been called on a 
+   * MultiReader wrapping those IndexReaders.
+   *
+   * @param starts from a MultiReader, n+1 elements, where the first n are the starting offsets of each IndexReader and the last element is the total maxDoc.
+   * @param data n elements resulting from n calls to buildData
+   * @exception UnsupportedOperationException unless isMergable returns true.
+   */
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+
+    throw new UnsupportedOperationException
+      ("data from this CacheKey cannot be merged");
+  }
+
+  /**
+   * Returns true if mergeData is a supported method for this CacheKey
+   *
+   * @see #mergeData
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /** The pattern used to detect integer values in a field */
+  /** removed for java 1.3 compatibility
+   protected static final Pattern pIntegers = Pattern.compile ("[0-9\\-]+");
+   **/
+
+  /** The pattern used to detect float values in a field */
+  /**
+   * removed for java 1.3 compatibility
+   * protected static final Object pFloats = Pattern.compile ("[0-9+\\-\\.eEfFdD]+");
+   */
+  
+  public static CacheKey getAutoCacheKey(IndexReader reader, String field) throws IOException {
+    field = ((String)field).intern();
+    TermEnum enumerator = reader.terms (new Term (field, ""));
+    try {
+      Term term = enumerator.term();
+      if (term == null) {
+        throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
+      }
+      CacheKey ret = null;
+      if (term.field() == field) {
+        String termtext = term.text().trim();
+        System.out.println("test auto term:" + termtext);
+
+        /**
+         * Java 1.4 level code:
+
+         if (pIntegers.matcher(termtext).matches())
+         return IntegerSortedHitQueue.comparator (reader, enumerator, field);
+
+         else if (pFloats.matcher(termtext).matches())
+         return FloatSortedHitQueue.comparator (reader, enumerator, field);
+         */
+
+        // Java 1.3 level code:
+        try {
+          Integer.parseInt (termtext);
+          ret = new IntArrayCacheKey(field);
+        } catch (NumberFormatException nfe1) {
+          try {
+            Long.parseLong(termtext);
+            ret = new LongArrayCacheKey(field);
+          } catch (NumberFormatException nfe2) {
+            try {
+              Float.parseFloat (termtext);
+              ret = new FloatArrayCacheKey(field);
+            } catch (NumberFormatException nfe3) {
+              ret = new StringArrayCacheKey(field);
+            }
+          }
+        }
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+      }
+      return ret;
+    } finally {
+      enumerator.close();
+    }
+  
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/DoubleArrayCacheKey.java	(revision 0)
@@ -0,0 +1,124 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+
+/** 
+ * A Key for identifying cacheable doubles.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class DoubleArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse doubles from document fields.
+   */
+  public interface DoubleParser {
+    /** Return a double representation of this field's value. */
+    public double parseDouble(String string);
+  }
+
+  private static final DoubleParser DEFAULT_PARSER = new DoubleParser() {
+      public double parseDouble(String value) {
+        return Double.parseDouble(value);
+      }
+    };
+
+  String field;
+  DoubleParser parser = DEFAULT_PARSER;
+    
+  public DoubleArrayCacheKey(String f, DoubleParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public DoubleArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final DoubleArrayCacheKey other = (DoubleArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building double data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final double[] retArray = new double[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        double termval = parser.parseDouble(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building double data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    double[] results = new double[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      double[] src = (double[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/FloatArrayCacheKey.java	(revision 0)
@@ -0,0 +1,122 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable floats.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class FloatArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse floats from document fields.
+   */
+  public interface FloatParser {
+    /** Return a float representation of this field's value. */
+    public float parseFloat(String string);
+  }
+
+  private static final FloatParser DEFAULT_PARSER = new FloatParser() {
+      public float parseFloat(String value) {
+        return Float.parseFloat(value);
+      }
+    };
+
+  String field;
+  FloatParser parser = DEFAULT_PARSER;
+    
+  public FloatArrayCacheKey(String f, FloatParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public FloatArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final FloatArrayCacheKey other = (FloatArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building float data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final float[] retArray = new float[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        float termval = parser.parseFloat(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building float data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    float[] results = new float[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      float[] src = (float[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/IntArrayCacheKey.java	(revision 0)
@@ -0,0 +1,123 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable ints.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class IntArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse ints from document fields.
+   */
+  public interface IntParser {
+    /** Return an integer representation of this field's value. */
+    public int parseInt(String string);
+  }
+
+  private static final IntParser DEFAULT_PARSER = new IntParser() {
+      public int parseInt(String value) {
+        return Integer.parseInt(value);
+      }
+    };
+  
+  String field;
+  IntParser parser = DEFAULT_PARSER;
+    
+  public IntArrayCacheKey(String f, IntParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public IntArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result =  prime  + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+  
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final IntArrayCacheKey other = (IntArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building int data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final int[] retArray = new int[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        int termval = parser.parseInt(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building int data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+  
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    System.out.println("merging ints");
+    int[] results = new int[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      int[] src = (int[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/LongArrayCacheKey.java	(revision 0)
@@ -0,0 +1,106 @@
+package org.apache.lucene.index.cache;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+/** 
+ * A Key for identifying cacheable longs.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class LongArrayCacheKey extends CacheKey {
+
+  /** 
+   * Interface to parse longs from document fields.
+   */
+  public interface LongParser {
+    /** Return a long representation of this field's value. */
+    public long parseLong(String string);
+  }
+
+  private static final LongParser DEFAULT_PARSER = new LongParser() {
+      public long parseLong(String value) {
+        return Long.parseLong(value);
+      }
+    };
+
+  String field;
+  LongParser parser = DEFAULT_PARSER;
+    
+  public LongArrayCacheKey(String f, LongParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public LongArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final LongArrayCacheKey other = (LongArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building long data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final long[] retArray = new long[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        long termval = parser.parseLong(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building long data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    long[] results = new long[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      long[] src = (long[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ShortArrayCacheKey.java	(revision 0)
@@ -0,0 +1,105 @@
+package org.apache.lucene.index.cache;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+/** 
+ * A Key for identifying cacheable shorts.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class ShortArrayCacheKey extends CacheKey {
+  /** 
+   * Interface to parse shortss from document fields.
+   */
+  public interface ShortParser {
+    /** Return a short representation of this field's value. */
+    public short parseShort(String string);
+  }
+
+  private static final ShortParser DEFAULT_PARSER = new ShortParser() {
+      public short parseShort(String value) {
+        return Short.parseShort(value);
+      }
+    };
+
+  String field;
+  ShortParser parser = DEFAULT_PARSER;
+    
+  public ShortArrayCacheKey(String f, ShortParser p) {
+    this(f);
+    if (null != p) parser = p;
+  }
+  public ShortArrayCacheKey(String f) {
+    field = f;
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime  + ((field == null) ? 0 : field.hashCode());
+    result = prime * result + ((parser == null) ? 0 : parser.hashCode());
+    return result;
+  }
+  
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final ShortArrayCacheKey other = (ShortArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    if (parser == null) {
+      if (other.parser != null)
+        return false;
+    } else if (!parser.equals(other.parser))
+      return false;
+    return true;
+  }
+  
+  public CacheData buildData(IndexReader reader) throws IOException {
+    System.out.println("building short data - field:" + field);
+    long stime = System.currentTimeMillis();
+    final short[] retArray = new short[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        short termval = parser.parseShort(term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    System.out.println("done building short data:" + (System.currentTimeMillis() - stime));
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    short[] results = new short[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      short[] src = (short[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/SimpleMapCache.java
===================================================================
--- src/java/org/apache/lucene/index/cache/SimpleMapCache.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/SimpleMapCache.java	(revision 0)
@@ -0,0 +1,55 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+
+
+/**
+ * A simple Map based Cache.
+ *
+ */
+public class SimpleMapCache implements Cache {
+
+  private Map data = new HashMap();
+  
+  public SimpleMapCache() { /* NOOP */ }
+
+  public synchronized void close() { /* NOOP */ }
+
+  public synchronized void put(CacheKey k, CacheData v) {
+    data.put(k, v);
+  }
+
+  public synchronized boolean containsKey(CacheKey key) {
+    return data.containsKey(key);
+  }
+  public synchronized Set keySet() {
+    return Collections.unmodifiableSet(data.keySet());
+  }
+
+  public synchronized CacheData get(CacheKey key) {
+    return (CacheData) data.get(key);
+  }
+ 
+}
Index: src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringArrayCacheKey.java	(revision 0)
@@ -0,0 +1,99 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable Strings.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class StringArrayCacheKey extends CacheKey {
+
+  String field;
+    
+  public StringArrayCacheKey(String f) {
+    field = f.intern();
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result =  prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final StringArrayCacheKey other = (StringArrayCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    return true;
+  }
+
+
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final String[] retArray = new String[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        String termval = term.text();
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+
+    return new CacheData(retArray);
+  }
+
+  public CacheData mergeData(int[] starts, CacheData[] data) 
+    throws UnsupportedOperationException {
+    
+    String[] results = new String[starts[starts.length-1]];
+    for (int i = 0; i < data.length; i++) {
+      String[] src = (String[]) data[i].getPayload();
+      System.arraycopy(src, 0, results, starts[i], src.length);
+    }
+    return new CacheData(results);
+  }
+
+  public boolean isMergable() { return true; }
+
+}
Index: src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringIndexCacheKey.java	(revision 0)
@@ -0,0 +1,131 @@
+package org.apache.lucene.index.cache;
+
+/**
+ * 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 org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import java.io.IOException;
+
+/** 
+ * A Key for identifying cacheable Strings. Caches all unique Strings as well 
+ * as lookup indexes.
+ *
+ * CacheKey instances provide all of the functionality for generating 
+ * CacheData based on an IndexReader instance.
+ */
+public class StringIndexCacheKey extends CacheKey {
+
+  public static class StringIndex {
+
+    /** All the term values, in natural order. */
+    public final String[] lookup;
+
+    /** For each document, an index into the lookup array. */
+    public final int[] order;
+
+    /** Creates one of these objects */
+    public StringIndex (int[] values, String[] lookup) {
+      this.order = values;
+      this.lookup = lookup;
+    }
+  }
+
+  String field;
+    
+  public StringIndexCacheKey(String f) {
+    field = f.intern();
+  }
+
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final StringIndexCacheKey other = (StringIndexCacheKey) obj;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    return true;
+  }
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final int[] retArray = new int[reader.maxDoc()];
+    String[] mterms = new String[reader.maxDoc()+1];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    int t = 0;  // current term number
+    
+    // an entry for documents that have no terms in this field
+    // should a document with no terms be at top or bottom?
+    // this puts them at the top - if it is changed, FieldDocSortedHitQueue
+    // needs to change as well.
+    mterms[t++] = null;
+    
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        
+        // store term text
+        // we expect that there is at most one term per document
+        if (t >= mterms.length) 
+          throw new RuntimeException 
+            ("there are more terms than documents in field \"" + field + 
+             "\", but it's impossible to sort on tokenized fields");
+        mterms[t] = term.text();
+        
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = t;
+        }
+        
+        t++;
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+    
+    if (t == 0) {
+      // if there are no terms, make the term array
+      // have a single null entry
+      mterms = new String[1];
+    } else if (t < mterms.length) {
+      // if there are less terms than documents,
+      // trim off the dead array space
+      String[] terms = new String[t];
+      System.arraycopy (mterms, 0, terms, 0, t);
+      mterms = terms;
+    }
+    
+    return new CacheData(new StringIndex (retArray, mterms));
+
+  }
+}
Index: src/java/org/apache/lucene/index/DirectoryIndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryIndexReader.java	(revision 637844)
+++ src/java/org/apache/lucene/index/DirectoryIndexReader.java	(working copy)
@@ -24,6 +24,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.lucene.index.cache.Cache;
+import org.apache.lucene.index.cache.SimpleMapCache;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.Lock;
 import org.apache.lucene.store.LockObtainFailedException;
@@ -93,6 +95,7 @@
           reader = new MultiSegmentReader(directory, infos, closeDirectory);
         }
         reader.setDeletionPolicy(deletionPolicy);
+        reader.useCacheFactory(Cache.FACTORY);
         return reader;
       }
     }.run();
@@ -100,9 +103,9 @@
 
   public final synchronized IndexReader reopen() throws CorruptIndexException, IOException {
     ensureOpen();
-
     if (this.hasChanges || this.isCurrent()) {
       // the index hasn't changed - nothing to do here
+
       return this;
     }
 
@@ -118,7 +121,7 @@
           newReader.init(directory, infos, closeDirectory);
           newReader.deletionPolicy = deletionPolicy;
         }
-
+        newReader.useCacheFactory(Cache.FACTORY);
         return newReader;
       }
     }.run();
Index: src/java/org/apache/lucene/index/DocumentsWriter.java
===================================================================
--- src/java/org/apache/lucene/index/DocumentsWriter.java	(revision 637844)
+++ src/java/org/apache/lucene/index/DocumentsWriter.java	(working copy)
@@ -283,12 +283,16 @@
         tvf.close();
         tvd.close();
         tvx = null;
+        assert 4+numDocsInStore*16 == directory.fileLength(docStoreSegment + "." + IndexFileNames.VECTORS_INDEX_EXTENSION):
+          "after flush: tvx size mismatch: " + numDocsInStore + " docs vs " + directory.fileLength(docStoreSegment + "." + IndexFileNames.VECTORS_INDEX_EXTENSION) + " length in bytes of " + docStoreSegment + "." + IndexFileNames.VECTORS_INDEX_EXTENSION;
       }
 
       if (fieldsWriter != null) {
         assert docStoreSegment != null;
         fieldsWriter.close();
         fieldsWriter = null;
+        assert numDocsInStore*8 == directory.fileLength(docStoreSegment + "." + IndexFileNames.FIELDS_INDEX_EXTENSION):
+          "after flush: fdx size mismatch: " + numDocsInStore + " docs vs " + directory.fileLength(docStoreSegment + "." + IndexFileNames.FIELDS_INDEX_EXTENSION) + " length in bytes of " + docStoreSegment + "." + IndexFileNames.FIELDS_INDEX_EXTENSION;
       }
 
       String s = docStoreSegment;
Index: src/java/org/apache/lucene/index/IndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/IndexReader.java	(revision 637844)
+++ src/java/org/apache/lucene/index/IndexReader.java	(working copy)
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.index.cache.*;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.FieldSelector;
 import org.apache.lucene.search.Similarity;
@@ -93,6 +94,7 @@
 
   private boolean closed;
   protected boolean hasChanges;
+  private Cache cache;
   
   private volatile int refCount;
   
@@ -149,10 +151,24 @@
   protected IndexReader(Directory directory) {
     this();
     this.directory = directory;
+    //TODO: find best set points
+    try {
+      cache = Cache.FACTORY.getCache(this);
+    } catch (IOException e) {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
   }
   
-  protected IndexReader() { 
+  protected IndexReader() {
     refCount = 1;
+    //TODO: find best set points
+    try {
+      cache = Cache.FACTORY.getCache(this);
+    } catch (IOException e) {
+      // TODO: how to deal with this?
+      e.printStackTrace();
+    }
   }
   
   /**
@@ -783,7 +799,57 @@
     hasChanges = true;
     doUndeleteAll();
   }
+  
+  /** 
+   * Use this CacheFactory to generate a Cache for the current IndexReader.
+   * Closes the previous cache first.
+   */
+  public void useCacheFactory(CacheFactory factory) throws IOException {
+    if (null != cache) cache.close();
+    cache = factory.getCache(this);
+  }
 
+  /**
+   * EXPERT: raw access to the cache for introspection
+   */
+  public Cache getCache() {
+    return cache;
+  }
+
+  /**
+   * Generates and caches the data identified by the CacheKey, 
+   * if it is not already in the cache.
+   */
+  public CacheData getCachedData(CacheKey key) throws IOException {
+    CacheData value;
+    synchronized (cache) {
+      value = cache.get(key);
+      if (value == null) {
+        value = new CacheData(new CacheCreationPlaceholder());
+        cache.put(key, value);
+      }
+    }
+    Object payload = value.getPayload();
+    if (payload instanceof CacheCreationPlaceholder) {
+      synchronized (payload) {
+        CacheCreationPlaceholder progress = (CacheCreationPlaceholder) payload;
+        if (progress.value == null) {
+            progress.value = key.buildData(this);
+            synchronized (cache) {
+              cache.put(key, progress.value);
+            }
+        }
+        return progress.value;
+      }
+    }
+    return value;
+  }
+
+  private static final class CacheCreationPlaceholder {
+    CacheData value;
+  }
+
+
   /** Implements actual undeleteAll() in subclass. */
   protected abstract void doUndeleteAll() throws CorruptIndexException, IOException;
 
@@ -830,6 +896,7 @@
   public final synchronized void close() throws IOException {
     if (!closed) {
       decRef();
+      if (null != cache) cache.close();
       closed = true;
     }
   }
Index: src/java/org/apache/lucene/index/MultiReader.java
===================================================================
--- src/java/org/apache/lucene/index/MultiReader.java	(revision 637844)
+++ src/java/org/apache/lucene/index/MultiReader.java	(working copy)
@@ -27,6 +27,10 @@
 import org.apache.lucene.index.MultiSegmentReader.MultiTermDocs;
 import org.apache.lucene.index.MultiSegmentReader.MultiTermEnum;
 import org.apache.lucene.index.MultiSegmentReader.MultiTermPositions;
+import org.apache.lucene.index.cache.Cache;
+import org.apache.lucene.index.cache.CacheData;
+import org.apache.lucene.index.cache.CacheFactory;
+import org.apache.lucene.index.cache.CacheKey;
 
 /** An IndexReader which reads multiple indexes, appending their content.
  *
@@ -85,6 +89,13 @@
         hasDeletions = true;
     }
     starts[subReaders.length] = maxDoc;
+    //TODO: find best spots for this
+    try {
+      useCacheFactory(Cache.FACTORY);
+    } catch (IOException e) {
+      // TODO: decide how to deal with this
+      e.printStackTrace();
+    }
   }
 
   /**
@@ -161,6 +172,19 @@
       }
     }
   }
+  
+  /** 
+   * Use this CacheFactory to generate a Cache for the current IndexReader, 
+   * as well as all of the subReaders.
+   *
+   * Closes the previous cache first.
+   */
+  public void useCacheFactory(CacheFactory factory) throws IOException {
+    super.useCacheFactory(factory);
+    for (int i = 0; i < subReaders.length; i++) {
+      subReaders[i].useCacheFactory(factory);
+    }
+  }
 
   public TermFreqVector[] getTermFreqVectors(int n) throws IOException {
     ensureOpen();
@@ -336,6 +360,24 @@
     }
   }
   
+  /**
+   * Generates and caches the data identified by the CacheKey, 
+   * if it is not already in the cache.  
+   *
+  * Delegates to subReaders if the CacheKey is mergeable, and 
+   * then caches the merged result.
+   */
+  public CacheData getCachedData(CacheKey key) throws IOException {
+    if ( ! key.isMergable() )
+      return super.getCachedData(key);
+
+    CacheData[] data = new CacheData[subReaders.length];
+    for (int i = 0; i < subReaders.length; i++) {
+      data[i] = subReaders[i].getCachedData(key);
+    }
+    return key.mergeData(starts, data);
+  }
+  
   public Collection getFieldNames (IndexReader.FieldOption fieldNames) {
     ensureOpen();
     return MultiSegmentReader.getFieldNames(fieldNames, this.subReaders);
Index: src/java/org/apache/lucene/index/MultiSegmentReader.java
===================================================================
--- src/java/org/apache/lucene/index/MultiSegmentReader.java	(revision 637844)
+++ src/java/org/apache/lucene/index/MultiSegmentReader.java	(working copy)
@@ -19,6 +19,8 @@
 
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.index.cache.CacheData;
+import org.apache.lucene.index.cache.CacheKey;
 import org.apache.lucene.store.Directory;
 
 import java.io.IOException;
@@ -179,7 +181,26 @@
       }
     }
   }
+  
+  /**
+   * Generates and caches the data identified by the CacheKey, 
+   * if it is not already in the cache.  
+   *
+  * Delegates to subReaders if the CacheKey is mergeable, and 
+   * then caches the merged result.
+   */
+  public CacheData getCachedData(CacheKey key) throws IOException {
+    if ( ! key.isMergable() )
+      return super.getCachedData(key);
 
+    CacheData[] data = new CacheData[subReaders.length];
+    for (int i = 0; i < subReaders.length; i++) {
+      data[i] = subReaders[i].getCachedData(key);
+    }
+
+    return key.mergeData(starts, data);
+  }
+
   private void initialize(SegmentReader[] subReaders) {
     this.subReaders = subReaders;
     starts = new int[subReaders.length + 1];    // build starts array
Index: src/java/org/apache/lucene/search/ExtendedFieldCache.java
===================================================================
--- src/java/org/apache/lucene/search/ExtendedFieldCache.java	(revision 635542)
+++ src/java/org/apache/lucene/search/ExtendedFieldCache.java	(working copy)
@@ -1,29 +1,23 @@
 package org.apache.lucene.search;
 
-import org.apache.lucene.index.IndexReader;
-
 import java.io.IOException;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.DoubleArrayCacheKey;
+import org.apache.lucene.index.cache.LongArrayCacheKey;
 
+
 /**
  *
- *
+ * @deprecated use IndexReader.getCachedData
  **/
 public interface ExtendedFieldCache extends FieldCache {
-  public interface LongParser {
-    /**
-     * Return an long representation of this field's value.
-     */
-    public long parseLong(String string);
-  }
+  public interface LongParser extends LongArrayCacheKey.LongParser { }
+  public interface DoubleParser extends DoubleArrayCacheKey.DoubleParser { }
 
-  public interface DoubleParser {
-    /**
-     * Return an long representation of this field's value.
-     */
-    public double parseDouble(String string);
-  }
-
+  /**
+  * @deprecated 
+  **/
   public static ExtendedFieldCache EXT_DEFAULT = new ExtendedFieldCacheImpl();
 
   /**
@@ -36,6 +30,7 @@
    * @param field  Which field contains the longs.
    * @return The values in the given field for each document.
    * @throws java.io.IOException If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new LongArrayCacheKey(field))
    */
   public long[] getLongs(IndexReader reader, String field)
           throws IOException;
@@ -51,6 +46,7 @@
    * @param parser Computes integer for string values.
    * @return The values in the given field for each document.
    * @throws IOException If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new LongArrayCacheKey(field), parser)
    */
   public long[] getLongs(IndexReader reader, String field, LongParser parser)
           throws IOException;
@@ -66,6 +62,7 @@
    * @param field  Which field contains the doubles.
    * @return The values in the given field for each document.
    * @throws IOException If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new DoubleArrayCacheKey(field))
    */
   public double[] getDoubles(IndexReader reader, String field)
           throws IOException;
@@ -81,6 +78,7 @@
    * @param parser Computes integer for string values.
    * @return The values in the given field for each document.
    * @throws IOException If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new DoubleArrayCacheKey(field), parser)
    */
   public double[] getDoubles(IndexReader reader, String field, DoubleParser parser)
           throws IOException;
Index: src/java/org/apache/lucene/search/ExtendedFieldCacheImpl.java
===================================================================
--- src/java/org/apache/lucene/search/ExtendedFieldCacheImpl.java	(revision 635542)
+++ src/java/org/apache/lucene/search/ExtendedFieldCacheImpl.java	(working copy)
@@ -1,28 +1,27 @@
 package org.apache.lucene.search;
 
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.TermDocs;
-import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.cache.CacheKey;
+import org.apache.lucene.index.cache.DoubleArrayCacheKey;
+import org.apache.lucene.index.cache.LongArrayCacheKey;
 
 import java.io.IOException;
 
 
 /**
- *
- *
+ * @deprecated use IndexReader.getCachedData
  **/
 class ExtendedFieldCacheImpl extends FieldCacheImpl implements ExtendedFieldCache {
   private static final LongParser LONG_PARSER = new LongParser() {
-      public long parseLong(String value) {
-        return Long.parseLong(value);
-      }
+    public long parseLong(String value) {
+      return Long.parseLong(value);
+    }
   };
 
   private static final DoubleParser DOUBLE_PARSER = new DoubleParser() {
-      public double parseDouble(String value) {
-        return Double.parseDouble(value);
-      }
+    public double parseDouble(String value) {
+      return Double.parseDouble(value);
+    }
   };
 
 
@@ -33,37 +32,11 @@
   // inherit javadocs
   public long[] getLongs(IndexReader reader, String field, LongParser parser)
       throws IOException {
-    return (long[]) longsCache.get(reader, new Entry(field, parser));
+    //System.out.println("get longs");
+    return (long[]) reader.getCachedData
+     (new LongArrayCacheKey(field, parser)).getPayload();
   }
 
-  Cache longsCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      LongParser parser = (LongParser) entry.custom;
-      final long[] retArray = new long[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term(field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          long termval = parser.parseLong(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
-
   // inherit javadocs
   public double[] getDoubles(IndexReader reader, String field)
     throws IOException {
@@ -73,93 +46,14 @@
   // inherit javadocs
   public double[] getDoubles(IndexReader reader, String field, DoubleParser parser)
       throws IOException {
-    return (double[]) doublesCache.get(reader, new Entry(field, parser));
+    return (double[]) reader.getCachedData
+    (new DoubleArrayCacheKey(field, parser)).getPayload();
   }
 
-  Cache doublesCache = new Cache() {
 
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      DoubleParser parser = (DoubleParser) entry.custom;
-      final double[] retArray = new double[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          double termval = parser.parseDouble(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
-
-
   // inherit javadocs
   public Object getAuto(IndexReader reader, String field) throws IOException {
-    return autoCache.get(reader, field);
+    return CacheKey.getAutoCacheKey(reader, field);
   }
 
-  Cache autoCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object fieldKey)
-        throws IOException {
-      String field = ((String)fieldKey).intern();
-      TermEnum enumerator = reader.terms (new Term (field, ""));
-      try {
-        Term term = enumerator.term();
-        if (term == null) {
-          throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
-        }
-        Object ret = null;
-        if (term.field() == field) {
-          String termtext = term.text().trim();
-
-          /**
-           * Java 1.4 level code:
-
-           if (pIntegers.matcher(termtext).matches())
-           return IntegerSortedHitQueue.comparator (reader, enumerator, field);
-
-           else if (pFloats.matcher(termtext).matches())
-           return FloatSortedHitQueue.comparator (reader, enumerator, field);
-           */
-
-          // Java 1.3 level code:
-          try {
-            Integer.parseInt (termtext);
-            ret = getInts (reader, field);
-          } catch (NumberFormatException nfe1) {
-            try {
-              Long.parseLong(termtext);
-              ret = getLongs (reader, field);
-            } catch (NumberFormatException nfe2) {
-              try {
-                Float.parseFloat (termtext);
-                ret = getFloats (reader, field);
-              } catch (NumberFormatException nfe3) {
-                ret = getStringIndex (reader, field);
-              }
-            }
-          }
-        } else {
-          throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
-        }
-        return ret;
-      } finally {
-        enumerator.close();
-      }
-    }
-  };
-
 }
Index: src/java/org/apache/lucene/search/FieldCache.java
===================================================================
--- src/java/org/apache/lucene/search/FieldCache.java	(revision 635542)
+++ src/java/org/apache/lucene/search/FieldCache.java	(working copy)
@@ -18,6 +18,7 @@
  */
 
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.*;
 import java.io.IOException;
 
 /**
@@ -28,6 +29,7 @@
  * @author  Tim Jones (Nacimiento Software)
  * @since   lucene 1.4
  * @version $Id$
+ * @deprecated use IndexReader.getCachedData
  */
 public interface FieldCache {
 
@@ -37,53 +39,38 @@
   public static final int STRING_INDEX = -1;
 
 
-  /** Expert: Stores term text values and document ordering data. */
-  public static class StringIndex {
-
-    /** All the term values, in natural order. */
-    public final String[] lookup;
-
-    /** For each document, an index into the lookup array. */
-    public final int[] order;
-
-    /** Creates one of these objects */
+   /** Expert: Stores term text values and document ordering data. 
+     * @deprecated use StringIndexCacheKey.StringIndex
+     */
+  public static class StringIndex extends StringIndexCacheKey.StringIndex { 
     public StringIndex (int[] values, String[] lookup) {
-      this.order = values;
-      this.lookup = lookup;
-    }
+      super(values,lookup);
+     }
   }
 
   /** Interface to parse bytes from document fields.
    * @see FieldCache#getBytes(IndexReader, String, FieldCache.ByteParser)
+   * @deprecated use ByteArrayCacheKey.ByteParser
    */
-  public interface ByteParser {
-    /** Return a single Byte representation of this field's value. */
-    public byte parseByte(String string);
-  }
+  public interface ByteParser extends ByteArrayCacheKey.ByteParser { }
 
   /** Interface to parse shorts from document fields.
    * @see FieldCache#getShorts(IndexReader, String, FieldCache.ShortParser)
+   * @deprecated use IntArrayCacheKey.ShortParser
    */
-  public interface ShortParser {
-    /** Return a short representation of this field's value. */
-    public short parseShort(String string);
-  }
+  public interface ShortParser extends ShortArrayCacheKey.ShortParser { }
 
   /** Interface to parse ints from document fields.
    * @see FieldCache#getInts(IndexReader, String, FieldCache.IntParser)
+   * @deprecated use IntArrayCacheKey.IntParser
    */
-  public interface IntParser {
-    /** Return an integer representation of this field's value. */
-    public int parseInt(String string);
-  }
+  public interface IntParser extends IntArrayCacheKey.IntParser { }
 
   /** Interface to parse floats from document fields.
    * @see FieldCache#getFloats(IndexReader, String, FieldCache.FloatParser)
+   * @deprecated use FloatArrayCacheKey.FloatParser
    */
-  public interface FloatParser {
-    /** Return an float representation of this field's value. */
-    public float parseFloat(String string);
-  }
+  public interface FloatParser extends FloatArrayCacheKey.FloatParser { }
 
   /** Expert: The cache used internally by sorting and range query classes. */
   public static FieldCache DEFAULT = new FieldCacheImpl();
@@ -96,6 +83,7 @@
    * @param field   Which field contains the single byte values.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new ByteArrayCacheKey(field))
    */
   public byte[] getBytes (IndexReader reader, String field)
   throws IOException;
@@ -109,6 +97,7 @@
    * @param parser  Computes byte for string values.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new ByteArrayCacheKey(field), parser)
    */
   public byte[] getBytes (IndexReader reader, String field, ByteParser parser)
   throws IOException;
@@ -121,6 +110,7 @@
    * @param field   Which field contains the shorts.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new ShortArrayCacheKey(field))
    */
   public short[] getShorts (IndexReader reader, String field)
   throws IOException;
@@ -134,6 +124,7 @@
    * @param parser  Computes short for string values.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new ShortArrayCacheKey(field), parser)
    */
   public short[] getShorts (IndexReader reader, String field, ShortParser parser)
   throws IOException;
@@ -146,6 +137,7 @@
    * @param field   Which field contains the integers.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new IntArrayCacheKey(field))
    */
   public int[] getInts (IndexReader reader, String field)
   throws IOException;
@@ -159,6 +151,7 @@
    * @param parser  Computes integer for string values.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new IntArrayCacheKey(field), parser)
    */
   public int[] getInts (IndexReader reader, String field, IntParser parser)
   throws IOException;
@@ -171,6 +164,7 @@
    * @param field   Which field contains the floats.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new FloatArrayCacheKey(field))
    */
   public float[] getFloats (IndexReader reader, String field)
   throws IOException;
@@ -184,6 +178,7 @@
    * @param parser  Computes float for string values.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new FloatArrayCacheKey(field), parser)
    */
   public float[] getFloats (IndexReader reader, String field,
                             FloatParser parser) throws IOException;
@@ -196,6 +191,7 @@
    * @param field   Which field contains the strings.
    * @return The values in the given field for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new StringArrayCacheKey(field))
    */
   public String[] getStrings (IndexReader reader, String field)
   throws IOException;
@@ -208,6 +204,7 @@
    * @param field   Which field contains the strings.
    * @return Array of terms and index into the array for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCachedData(reader, new StringIndexArrayCacheKey(field))
    */
   public StringIndex getStringIndex (IndexReader reader, String field)
   throws IOException;
@@ -222,6 +219,7 @@
    * @param field   Which field contains the values.
    * @return int[], float[] or StringIndex.
    * @throws IOException  If any error occurs.
+   * @deprecated use CachKey.getAuto(reader, field)
    */
   public Object getAuto (IndexReader reader, String field)
   throws IOException;
@@ -236,6 +234,7 @@
    * @param comparator Used to convert terms into something to sort by.
    * @return Array of sort objects, one for each document.
    * @throws IOException  If any error occurs.
+   * @deprecated use IndexReader.getCacheData with a custom CacheKey
    */
   public Comparable[] getCustom (IndexReader reader, String field, SortComparator comparator)
   throws IOException;
Index: src/java/org/apache/lucene/search/FieldCacheImpl.java
===================================================================
--- src/java/org/apache/lucene/search/FieldCacheImpl.java	(revision 635542)
+++ src/java/org/apache/lucene/search/FieldCacheImpl.java	(working copy)
@@ -17,17 +17,24 @@
  * limitations under the License.
  */
 
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.index.TermDocs;
-import org.apache.lucene.index.TermEnum;
-
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.cache.ByteArrayCacheKey;
+import org.apache.lucene.index.cache.CacheKey;
+import org.apache.lucene.index.cache.FloatArrayCacheKey;
+import org.apache.lucene.index.cache.IntArrayCacheKey;
+import org.apache.lucene.index.cache.LongArrayCacheKey;
+import org.apache.lucene.index.cache.ShortArrayCacheKey;
+import org.apache.lucene.index.cache.StringArrayCacheKey;
+import org.apache.lucene.index.cache.StringIndexCacheKey;
+
 /**
  * Expert: The default cache implementation, storing all values in memory.
  * A WeakHashMap is used for storage.
@@ -37,6 +44,7 @@
  * @author  Tim Jones (Nacimiento Software)
  * @since   lucene 1.4
  * @version $Id$
+ * @deprecated use IndexReader.getCachedData
  */
 class FieldCacheImpl
 implements FieldCache {
@@ -81,11 +89,14 @@
     }
   }
 
+  /** @deprecated */
   static final class CreationPlaceholder {
     Object value;
   }
 
-  /** Expert: Every composite-key in the internal cache is of this type. */
+  /** Expert: Every composite-key in the internal cache is of this type. 
+   * @deprecated 
+   */
   static class Entry {
     final String field;        // which Fieldable
     final int type;            // which SortField type
@@ -155,6 +166,7 @@
         return Float.parseFloat(value);
       }
   };
+  
 
   // inherit javadocs
   public byte[] getBytes (IndexReader reader, String field) throws IOException {
@@ -164,348 +176,119 @@
   // inherit javadocs
   public byte[] getBytes(IndexReader reader, String field, ByteParser parser)
       throws IOException {
-    return (byte[]) bytesCache.get(reader, new Entry(field, parser));
+    return (byte[]) reader.getCachedData
+    (new ByteArrayCacheKey(field, parser)).getPayload();
   }
-
-  Cache bytesCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      ByteParser parser = (ByteParser) entry.custom;
-      final byte[] retArray = new byte[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          byte termval = parser.parseByte(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
   
   // inherit javadocs
   public short[] getShorts (IndexReader reader, String field) throws IOException {
-    return getShorts(reader, field, SHORT_PARSER);
+    return getShorts(reader, field, null);
   }
 
   // inherit javadocs
   public short[] getShorts(IndexReader reader, String field, ShortParser parser)
       throws IOException {
-    return (short[]) shortsCache.get(reader, new Entry(field, parser));
+    return (short[]) reader.getCachedData
+    (new ShortArrayCacheKey(field, parser)).getPayload();
   }
 
-  Cache shortsCache = new Cache() {
 
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      ShortParser parser = (ShortParser) entry.custom;
-      final short[] retArray = new short[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          short termval = parser.parseShort(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
   
   // inherit javadocs
   public int[] getInts (IndexReader reader, String field) throws IOException {
-    return getInts(reader, field, INT_PARSER);
+    return getInts(reader, field, null);
   }
 
   // inherit javadocs
   public int[] getInts(IndexReader reader, String field, IntParser parser)
       throws IOException {
-    return (int[]) intsCache.get(reader, new Entry(field, parser));
+        return (int[]) reader.getCachedData
+         (new IntArrayCacheKey(field, parser)).getPayload();
   }
 
-  Cache intsCache = new Cache() {
 
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      IntParser parser = (IntParser) entry.custom;
-      final int[] retArray = new int[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          int termval = parser.parseInt(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
 
-
   // inherit javadocs
   public float[] getFloats (IndexReader reader, String field)
     throws IOException {
-    return getFloats(reader, field, FLOAT_PARSER);
+    return getFloats(reader, field, null);
   }
 
   // inherit javadocs
   public float[] getFloats(IndexReader reader, String field, FloatParser parser)
       throws IOException {
-    return (float[]) floatsCache.get(reader, new Entry(field, parser));
+        return (float[]) reader.getCachedData
+          (new FloatArrayCacheKey(field, parser)).getPayload();
   }
 
-  Cache floatsCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      FloatParser parser = (FloatParser) entry.custom;
-      final float[] retArray = new float[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          float termval = parser.parseFloat(term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
-
   // inherit javadocs
   public String[] getStrings(IndexReader reader, String field)
       throws IOException {
-    return (String[]) stringsCache.get(reader, field);
+        return (String[]) reader.getCachedData
+         (new StringArrayCacheKey(field)).getPayload();
   }
 
-  Cache stringsCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object fieldKey)
-        throws IOException {
-      String field = ((String) fieldKey).intern();
-      final String[] retArray = new String[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          String termval = term.text();
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
-
   // inherit javadocs
   public StringIndex getStringIndex(IndexReader reader, String field)
       throws IOException {
-    return (StringIndex) stringsIndexCache.get(reader, field);
+   StringIndexCacheKey.StringIndex data = (StringIndexCacheKey.StringIndex) 
+       reader.getCachedData(new StringIndexCacheKey(field)).getPayload();
+      return new StringIndex(data.order, data.lookup);
   }
 
-  Cache stringsIndexCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object fieldKey)
-        throws IOException {
-      String field = ((String) fieldKey).intern();
-      final int[] retArray = new int[reader.maxDoc()];
-      String[] mterms = new String[reader.maxDoc()+1];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      int t = 0;  // current term number
-
-      // an entry for documents that have no terms in this field
-      // should a document with no terms be at top or bottom?
-      // this puts them at the top - if it is changed, FieldDocSortedHitQueue
-      // needs to change as well.
-      mterms[t++] = null;
-
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-
-          // store term text
-          // we expect that there is at most one term per document
-          if (t >= mterms.length) throw new RuntimeException ("there are more terms than " +
-                  "documents in field \"" + field + "\", but it's impossible to sort on " +
-                  "tokenized fields");
-          mterms[t] = term.text();
-
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = t;
-          }
-
-          t++;
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-
-      if (t == 0) {
-        // if there are no terms, make the term array
-        // have a single null entry
-        mterms = new String[1];
-      } else if (t < mterms.length) {
-        // if there are less terms than documents,
-        // trim off the dead array space
-        String[] terms = new String[t];
-        System.arraycopy (mterms, 0, terms, 0, t);
-        mterms = terms;
-      }
-
-      StringIndex value = new StringIndex (retArray, mterms);
-      return value;
-    }
-  };
-
-  /** The pattern used to detect integer values in a field */
-  /** removed for java 1.3 compatibility
-   protected static final Pattern pIntegers = Pattern.compile ("[0-9\\-]+");
-   **/
-
-  /** The pattern used to detect float values in a field */
-  /**
-   * removed for java 1.3 compatibility
-   * protected static final Object pFloats = Pattern.compile ("[0-9+\\-\\.eEfFdD]+");
-   */
-
 	// inherit javadocs
   public Object getAuto(IndexReader reader, String field) throws IOException {
-    return autoCache.get(reader, field);
-  }
 
-  Cache autoCache = new Cache() {
+    field = ((String)field).intern();
+    TermEnum enumerator = reader.terms (new Term (field, ""));
+    try {
+      Term term = enumerator.term();
+      if (term == null) {
+        throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
+      }
+      CacheKey ret = null;
+      if (term.field() == field) {
+        String termtext = term.text().trim();
 
-    protected Object createValue(IndexReader reader, Object fieldKey)
-        throws IOException {
-      String field = ((String)fieldKey).intern();
-      TermEnum enumerator = reader.terms (new Term (field, ""));
-      try {
-        Term term = enumerator.term();
-        if (term == null) {
-          throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
-        }
-        Object ret = null;
-        if (term.field() == field) {
-          String termtext = term.text().trim();
+        /**
+         * Java 1.4 level code:
 
-          /**
-           * Java 1.4 level code:
+         if (pIntegers.matcher(termtext).matches())
+         return IntegerSortedHitQueue.comparator (reader, enumerator, field);
 
-           if (pIntegers.matcher(termtext).matches())
-           return IntegerSortedHitQueue.comparator (reader, enumerator, field);
+         else if (pFloats.matcher(termtext).matches())
+         return FloatSortedHitQueue.comparator (reader, enumerator, field);
+         */
 
-           else if (pFloats.matcher(termtext).matches())
-           return FloatSortedHitQueue.comparator (reader, enumerator, field);
-           */
-
-          // Java 1.3 level code:
+        // Java 1.3 level code:
+        try {
+          Integer.parseInt (termtext);
+          ret = new IntArrayCacheKey(field);
+        } catch (NumberFormatException nfe1) {
           try {
-            Integer.parseInt (termtext);
-            ret = getInts (reader, field);
-          } catch (NumberFormatException nfe1) {
-            try {
-                Float.parseFloat (termtext);
-                ret = getFloats (reader, field);
-              } catch (NumberFormatException nfe3) {
-                ret = getStringIndex (reader, field);
-              }
-          }          
-        } else {
-          throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+              Float.parseFloat (termtext);
+              ret = new FloatArrayCacheKey(field);
+            } catch (NumberFormatException nfe3) {
+              ret = new StringArrayCacheKey(field);
+            }
         }
-        return ret;
-      } finally {
-        enumerator.close();
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
       }
+      return ret;
+    } finally {
+      enumerator.close();
     }
-  };
+  }
 
   // inherit javadocs
   public Comparable[] getCustom(IndexReader reader, String field,
       SortComparator comparator) throws IOException {
-    return (Comparable[]) customCache.get(reader, new Entry(field, comparator));
+    
+         return (Comparable[]) 
+           reader.getCachedData(new SortComparatorCacheKey
+                               (field, comparator)).getPayload();
   }
-
-  Cache customCache = new Cache() {
-
-    protected Object createValue(IndexReader reader, Object entryKey)
-        throws IOException {
-      Entry entry = (Entry) entryKey;
-      String field = entry.field;
-      SortComparator comparator = (SortComparator) entry.custom;
-      final Comparable[] retArray = new Comparable[reader.maxDoc()];
-      TermDocs termDocs = reader.termDocs();
-      TermEnum termEnum = reader.terms (new Term (field, ""));
-      try {
-        do {
-          Term term = termEnum.term();
-          if (term==null || term.field() != field) break;
-          Comparable termval = comparator.getComparable (term.text());
-          termDocs.seek (termEnum);
-          while (termDocs.next()) {
-            retArray[termDocs.doc()] = termval;
-          }
-        } while (termEnum.next());
-      } finally {
-        termDocs.close();
-        termEnum.close();
-      }
-      return retArray;
-    }
-  };
   
 }
 
Index: src/java/org/apache/lucene/search/FieldDocSortedHitQueue.java
===================================================================
--- src/java/org/apache/lucene/search/FieldDocSortedHitQueue.java	(revision 635542)
+++ src/java/org/apache/lucene/search/FieldDocSortedHitQueue.java	(working copy)
@@ -134,6 +134,7 @@
         case SortField.STRING:{
 					String s1 = (String) docA.fields[i];
 					String s2 = (String) docB.fields[i];
+					//TODO: update javadoc
 					// null values need to be sorted first, because of how FieldCache.getStringIndex()
 					// works - in that routine, any documents without a value in the given field are
 					// put first.  If both are null, the next SortField is used
@@ -189,6 +190,7 @@
 					throw new RuntimeException ("invalid SortField type: "+type);
         }
       }
+
 			if (fields[i].getReverse()) {
 				c = -c;
 			}
Index: src/java/org/apache/lucene/search/FieldSortedHitQueue.java
===================================================================
--- src/java/org/apache/lucene/search/FieldSortedHitQueue.java	(revision 635542)
+++ src/java/org/apache/lucene/search/FieldSortedHitQueue.java	(working copy)
@@ -18,8 +18,19 @@
  */
 
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.ByteArrayCacheKey;
+import org.apache.lucene.index.cache.CacheKey;
+import org.apache.lucene.index.cache.DoubleArrayCacheKey;
+import org.apache.lucene.index.cache.FloatArrayCacheKey;
+import org.apache.lucene.index.cache.IntArrayCacheKey;
+import org.apache.lucene.index.cache.LongArrayCacheKey;
+import org.apache.lucene.index.cache.ShortArrayCacheKey;
+import org.apache.lucene.index.cache.StringArrayCacheKey;
+import org.apache.lucene.index.cache.StringIndexCacheKey;
+import org.apache.lucene.index.cache.StringIndexCacheKey.StringIndex;
 import org.apache.lucene.util.PriorityQueue;
 
+
 import java.io.IOException;
 import java.text.Collator;
 import java.util.Locale;
@@ -162,14 +173,47 @@
   throws IOException {
     if (type == SortField.DOC) return ScoreDocComparator.INDEXORDER;
     if (type == SortField.SCORE) return ScoreDocComparator.RELEVANCE;
-    FieldCacheImpl.Entry entry = (factory != null)
-      ? new FieldCacheImpl.Entry (field, factory)
-      : new FieldCacheImpl.Entry (field, type, locale);
-    return (ScoreDocComparator)Comparators.get(reader, entry);
+
+    ScoreDocComparator comparator;
+    switch (type) {
+      case SortField.AUTO:
+        comparator = comparatorAuto (reader, field);
+        break;
+      case SortField.INT:
+        comparator = comparatorInt (reader, field);
+        break;
+      case SortField.FLOAT:
+        comparator = comparatorFloat (reader, field);
+        break;
+      case SortField.LONG:
+        comparator = comparatorLong(reader, field);
+        break;
+      case SortField.DOUBLE:
+        comparator = comparatorDouble(reader, field);
+        break;
+      case SortField.SHORT:
+        comparator = comparatorShort(reader, field);
+        break;
+      case SortField.BYTE:
+        comparator = comparatorByte(reader, field);
+        break;
+      case SortField.STRING:
+        if (locale != null) comparator = comparatorStringLocale (reader, field, locale);
+        else comparator = comparatorString (reader, field);
+        break;
+      case SortField.CUSTOM:
+        comparator = factory.newComparator (reader, field);
+        break;
+      default:
+        throw new RuntimeException ("unknown field type: "+type);
+    }
+    return comparator;
   }
 
   /** Internal cache of comparators. Similar to FieldCache, only
-   *  caches comparators instead of term values. */
+   *  caches comparators instead of term values. 
+   *  @deprecated
+   */
   static final FieldCacheImpl.Cache Comparators = new FieldCacheImpl.Cache() {
 
     protected Object createValue(IndexReader reader, Object entryKey)
@@ -180,6 +224,7 @@
       Locale locale = entry.locale;
       SortComparatorSource factory = (SortComparatorSource) entry.custom;
       ScoreDocComparator comparator;
+      System.out.println("type:" + type);
       switch (type) {
         case SortField.AUTO:
           comparator = comparatorAuto (reader, fieldname);
@@ -219,14 +264,14 @@
    /**
    * Returns a comparator for sorting hits according to a field containing bytes.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg integer values.
+   * @param fieldname  Fieldable containing integer values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorByte(final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final byte[] fieldOrder = FieldCache.DEFAULT.getBytes(reader, field);
+    final byte[] fieldOrder = (byte[]) reader.getCachedData(new ByteArrayCacheKey(field)).getPayload();;
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -250,14 +295,14 @@
   /**
    * Returns a comparator for sorting hits according to a field containing shorts.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg integer values.
+   * @param fieldname  Fieldable containing integer values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorShort(final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final short[] fieldOrder = FieldCache.DEFAULT.getShorts(reader, field);
+    final short[] fieldOrder = (short[]) reader.getCachedData(new ShortArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -281,14 +326,14 @@
   /**
    * Returns a comparator for sorting hits according to a field containing integers.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg integer values.
+   * @param fieldname  Fieldable containing integer values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorInt (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final int[] fieldOrder = FieldCache.DEFAULT.getInts (reader, field);
+    final int[] fieldOrder = (int[]) reader.getCachedData(new IntArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -312,14 +357,14 @@
   /**
    * Returns a comparator for sorting hits according to a field containing integers.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg integer values.
+   * @param fieldname  Fieldable containing integer values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorLong (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final long[] fieldOrder = ExtendedFieldCache.EXT_DEFAULT.getLongs (reader, field);
+    final long[] fieldOrder = (long[]) reader.getCachedData(new LongArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -344,14 +389,15 @@
   /**
    * Returns a comparator for sorting hits according to a field containing floats.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg float values.
+   * @param fieldname  Fieldable containing float values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorFloat (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final float[] fieldOrder = FieldCache.DEFAULT.getFloats (reader, field);
+    final float[] fieldOrder = (float[]) reader.getCachedData(new FloatArrayCacheKey(field)).getPayload();
+
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -375,14 +421,14 @@
   /**
    * Returns a comparator for sorting hits according to a field containing doubles.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg float values.
+   * @param fieldname  Fieldable containing float values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorDouble(final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final double[] fieldOrder = ExtendedFieldCache.EXT_DEFAULT.getDoubles (reader, field);
+    final double[] fieldOrder = (double[]) reader.getCachedData(new DoubleArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -406,14 +452,16 @@
   /**
    * Returns a comparator for sorting hits according to a field containing strings.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg string values.
+   * @param fieldname  Fieldable containing string values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorString (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final FieldCache.StringIndex index = FieldCache.DEFAULT.getStringIndex (reader, field);
+    StringIndexCacheKey.StringIndex data = (StringIndexCacheKey.StringIndex) 
+    reader.getCachedData(new StringIndexCacheKey(field)).getPayload();
+    final StringIndex index = new StringIndex(data.order, data.lookup);
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
@@ -437,7 +485,7 @@
   /**
    * Returns a comparator for sorting hits according to a field containing strings.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg string values.
+   * @param fieldname  Fieldable containing string values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
@@ -445,7 +493,8 @@
   throws IOException {
     final Collator collator = Collator.getInstance (locale);
     final String field = fieldname.intern();
-    final String[] index = FieldCache.DEFAULT.getStrings (reader, field);
+    //TODO: should be StringIndexArrayCacheKey?
+    final String[] index = (String[]) reader.getCachedData(new StringArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
     	public final int compare(final ScoreDoc i, final ScoreDoc j) {
@@ -478,23 +527,24 @@
    * floats or strings.  Once the type is determined, one of the other static methods
    * in this class is called to get the comparator.
    * @param reader  Index to use.
-   * @param fieldname  Fieldable containg values.
+   * @param fieldname  Fieldable containing values.
    * @return  Comparator for sorting hits.
    * @throws IOException If an error occurs reading the index.
    */
   static ScoreDocComparator comparatorAuto (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    Object lookupArray = ExtendedFieldCache.EXT_DEFAULT.getAuto (reader, field);
-    if (lookupArray instanceof FieldCache.StringIndex) {
+    Object lookupArray = CacheKey.getAutoCacheKey(reader, field);
+    System.out.println(lookupArray.getClass().getCanonicalName());
+    if (lookupArray instanceof StringIndexCacheKey) {
       return comparatorString (reader, field);
-    } else if (lookupArray instanceof int[]) {
+    } else if (lookupArray instanceof IntArrayCacheKey) {
       return comparatorInt (reader, field);
-    } else if (lookupArray instanceof long[]) {
+    } else if (lookupArray instanceof LongArrayCacheKey) {
       return comparatorLong (reader, field);
-    } else if (lookupArray instanceof float[]) {
+    } else if (lookupArray instanceof FloatArrayCacheKey) {
       return comparatorFloat (reader, field);
-    } else if (lookupArray instanceof String[]) {
+    } else if (lookupArray instanceof StringArrayCacheKey) {
       return comparatorString (reader, field);
     } else {
       throw new RuntimeException ("unknown data type in field '"+field+"'");
Index: src/java/org/apache/lucene/search/SortComparator.java
===================================================================
--- src/java/org/apache/lucene/search/SortComparator.java	(revision 635542)
+++ src/java/org/apache/lucene/search/SortComparator.java	(working copy)
@@ -46,7 +46,9 @@
   public ScoreDocComparator newComparator (final IndexReader reader, final String fieldname)
   throws IOException {
     final String field = fieldname.intern();
-    final Comparable[] cachedValues = FieldCache.DEFAULT.getCustom (reader, field, SortComparator.this);
+    final Comparable[] cachedValues =  (Comparable[]) 
+    reader.getCachedData(new SortComparatorCacheKey
+        (field, SortComparator.this)).getPayload();
     
     return new ScoreDocComparator() {
 
Index: src/java/org/apache/lucene/search/SortComparatorCacheKey.java
===================================================================
--- src/java/org/apache/lucene/search/SortComparatorCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/search/SortComparatorCacheKey.java	(revision 0)
@@ -0,0 +1,93 @@
+package org.apache.lucene.search;
+
+/**
+ * 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 org.apache.lucene.index.cache.*;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.TermEnum;
+
+import org.apache.lucene.search.SortComparator;
+
+import java.io.IOException;
+
+/* :TODO: javadocs 
+ *
+ * :TODO: this class must live in o.a.l.search because of access level of comparator.getComparable
+ */
+public class SortComparatorCacheKey extends CacheKey {
+
+  String field;
+  SortComparator comparator;
+    
+  public SortComparatorCacheKey(String f, SortComparator c) {
+    field = f.intern();
+    comparator = c;
+  }
+  
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((comparator == null) ? 0 : comparator.hashCode());
+    result = prime * result + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final SortComparatorCacheKey other = (SortComparatorCacheKey) obj;
+    if (comparator == null) {
+      if (other.comparator != null)
+        return false;
+    } else if (!comparator.equals(other.comparator))
+      return false;
+    if (field == null) {
+      if (other.field != null)
+        return false;
+    } else if (!field.equals(other.field))
+      return false;
+    return true;
+  }
+
+
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+      
+    final Comparable[] retArray = new Comparable[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms (new Term (field, ""));
+    try {
+      do {
+        Term term = termEnum.term();
+        if (term==null || term.field() != field) break;
+        Comparable termval = comparator.getComparable (term.text());
+        termDocs.seek (termEnum);
+        while (termDocs.next()) {
+          retArray[termDocs.doc()] = termval;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+      }
+    return new CacheData(retArray);
+  }
+}
