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,158 @@
+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 bytes.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will
+ * merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key
+ * that returns a ObjectArray payload.
+ */
+public class ByteArrayCacheKey extends CacheKey {
+
+  private static final ByteParser DEFAULT_PARSER = new ByteParser() {
+    public byte parseByte(String value) {
+      return Byte.parseByte(value);
+    }
+  };
+
+  private String field;
+  private ByteParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public ByteArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ByteArrayCacheKey(String f, ByteParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getByteArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected byte[] getByteArray(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 retArray;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse bytes from document fields.
+   */
+  public interface ByteParser {
+    /** Return a byte representation of this field's value. */
+    public byte parseByte(String string);
+  }
+
+}
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,156 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class FloatArrayCacheKey extends CacheKey {
+
+  private static final FloatParser DEFAULT_PARSER = new FloatParser() {
+    public float parseFloat(String value) {
+      return Float.parseFloat(value);
+    }
+  };
+
+  private String field;
+  private FloatParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public FloatArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public FloatArrayCacheKey(String f, FloatParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getFloatArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected float[] getFloatArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse floats from document fields.
+   */
+  public interface FloatParser {
+    /** Return a float representation of this field's value. */
+    public float parseFloat(String string);
+  }
+
+}
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,116 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class StringArrayCacheKey extends CacheKey {
+
+  private String field;
+
+  /**
+   * @param f
+   */
+  public StringArrayCacheKey(String f) {
+    field = f.intern();
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  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);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+}
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,60 @@
+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 a Cache.
+ * 
+ * @see CacheKey
+ * @see CacheData
+ * @see CacheFactory
+ */
+public interface Cache {
+
+  /**
+   * Default CacheFactory impl.
+   */
+  public static CacheFactory FACTORY = new CacheFactory() {
+    public Cache getCache(IndexReader r) {
+      return new SimpleMapCache();
+    }
+  };
+
+  /**
+   * Called when this Cache will no longer be used anymore, so that it can free any external resources it may have.
+   */
+  public void close();
+
+  /**
+   * @returns true if the Cache contains data for the specified key.
+   */
+  public boolean containsKey(CacheKey key);
+
+  /**
+   * 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);
+
+}
Index: src/java/org/apache/lucene/index/cache/ObjectArray.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ObjectArray.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ObjectArray.java	(revision 0)
@@ -0,0 +1,40 @@
+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.
+ */
+
+/**
+ * Gives access to an array of Objects by doc id. Optionally provides access to a secondary array.
+ * 
+ * Using an ObjectArray payload allows value requests against a Multi/Segment/IndexReader to be directed to the correct IndexReader rather than merging the
+ * IndexReader's caches for the Multi/Segment/IndexReader. IndexReader#reopen can be much faster as a result.
+ * 
+ * @see CacheKey
+ */
+public abstract class ObjectArray {
+
+  /**
+   * @param doc
+   * @return
+   */
+  public abstract Object get(int doc);
+  
+  public Object get2(int doc) throws UnsupportedOperationException {
+    throw new UnsupportedOperationException();
+  }
+
+}
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,157 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will
+ * merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key
+ * that returns a ObjectArray payload.
+ */
+public class DoubleArrayCacheKey extends CacheKey {
+
+  private static final DoubleParser DEFAULT_PARSER = new DoubleParser() {
+    public double parseDouble(String value) {
+      return Double.parseDouble(value);
+    }
+  };
+
+  private String field;
+  private DoubleParser parser = DEFAULT_PARSER;
+  
+  /**
+   * @param f
+   */
+  public DoubleArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public DoubleArrayCacheKey(String f, DoubleParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getDoubleArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected double[] getDoubleArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse doubles from document fields.
+   */
+  public interface DoubleParser {
+    /** Return a double representation of this field's value. */
+    public double parseDouble(String string);
+  }
+
+}
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;
+
+/**
+ * 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 boolean containsKey(CacheKey key) {
+    return data.containsKey(key);
+  }
+
+  public synchronized CacheData get(CacheKey key) {
+    return (CacheData) data.get(key);
+  }
+
+  public synchronized Set keySet() {
+    return Collections.unmodifiableSet(data.keySet());
+  }
+
+  public synchronized void put(CacheKey k, CacheData v) {
+    data.put(k, v);
+  }
+
+}
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,147 @@
+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 {
+
+  /** Checks the internal cache for an appropriate entry, and if
+   * none is found reads <code>field</code> to see if it contains integers, floats
+   * or strings, and then calls one of the other methods in this class to get the
+   * values.  For string values, a StringIndex is returned.  After
+   * calling this method, there is an entry in the cache for both
+   * type <code>AUTO</code> and the actual found type.
+   * @param reader  Used to get field values.
+   * @param field   Which field contains the values.
+   * @return CacheKey based on field type.
+   * @throws IOException  If any error occurs.
+   */
+  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();
+
+        /**
+         * 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 IntObjectArrayCacheKey(field);
+        } catch (NumberFormatException nfe1) {
+          try {
+            Long.parseLong(termtext);
+            ret = new LongObjectArrayCacheKey(field);
+          } catch (NumberFormatException nfe2) {
+            try {
+              Float.parseFloat (termtext);
+              ret = new FloatObjectArrayCacheKey(field);
+            } catch (NumberFormatException nfe3) {
+              ret = new StringObjectArrayCacheKey(field);
+            }
+          }
+        }
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+      }
+      return ret;
+    } finally {
+      enumerator.close();
+    }
+  
+  }
+  /**
+   * Builds up the CacheData associated with this Key for the 
+   * specified IndexReader.
+   */
+  public abstract CacheData buildData(IndexReader r) throws IOException;
+
+  public abstract boolean equals(Object o);
+
+  public abstract int hashCode();
+
+  /**
+   * @return true if mergeData is a supported method for this CacheKey
+   * @see #mergeData
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /**
+   * 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");
+  }
+  
+  /** 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]+");
+   */
+  
+  
+  /**
+   * @return true if payload is instance of ObjectArray.
+   * @see ObjectArray
+   */
+  public boolean usesObjectArray() {
+    return false;
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable ints.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class IntObjectArrayCacheKey extends IntArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public IntObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public IntObjectArrayCacheKey(String f, IntParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final int[] retArray = getIntArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Integer(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesObjectArray()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable ints.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class ShortObjectArrayCacheKey extends ShortArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public ShortObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ShortObjectArrayCacheKey(String f, ShortParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ShortArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final short[] retArray = getShortArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Short(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ShortArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,84 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable longs.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class LongObjectArrayCacheKey extends LongArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public LongObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public LongObjectArrayCacheKey(String f, LongParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.LongArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final long[] retArray = getLongArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Long(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.LongArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+}
Index: src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,81 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * 
+ */
+public class ByteObjectArrayCacheKey extends ByteArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public ByteObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ByteObjectArrayCacheKey(String f, ByteParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ByteArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final byte[] retArray = getByteArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int doc) {
+        return new Byte(retArray[doc]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ByteArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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,158 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class IntArrayCacheKey extends CacheKey {
+
+  private static final IntParser DEFAULT_PARSER = new IntParser() {
+    public int parseInt(String value) {
+      return Integer.parseInt(value);
+    }
+  };
+
+  private String field;
+  private IntParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public IntArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public IntArrayCacheKey(String f, IntParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getIntArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected int[] getIntArray(IndexReader reader) throws IOException {
+    final int[] retArray = new int[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms(new Term(field, ""));
+    int cnt = 0;
+    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;
+          cnt++;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+
+    return retArray;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  public CacheData mergeData(int[] starts, CacheData[] data) throws UnsupportedOperationException {
+    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);
+  }
+
+  /**
+   * Interface to parse ints from document fields.
+   */
+  public interface IntParser {
+    /** Return an integer representation of this field's value. */
+    public int parseInt(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,83 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable floats.
+ * 
+ * CacheKey instances provide all of the functionality for generating CacheData based on an IndexReader instance.
+ */
+public class FloatObjectArrayCacheKey extends FloatArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public FloatObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public FloatObjectArrayCacheKey(String f, FloatParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.FloatArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final float[] retArray = getFloatArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Float(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.FloatArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,82 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable Strings. Caches all unique Strings as well as lookup indexes.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class StringObjectArrayCacheKey extends StringIndexCacheKey {
+
+  /**
+   * @param f
+   */
+  public StringObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.StringIndexCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+
+    final StringIndex stringIndex = getStringIndex(reader);
+
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Integer(stringIndex.order[index]);
+      }
+      
+      public Object get2(int index) {
+        return stringIndex.lookup[stringIndex.order[index]];
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.StringIndexCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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,157 @@
+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.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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class LongArrayCacheKey extends CacheKey {
+
+  private static final LongParser DEFAULT_PARSER = new LongParser() {
+    public long parseLong(String value) {
+      return Long.parseLong(value);
+    }
+  };
+
+  private String field;
+  private LongParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public LongArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public LongArrayCacheKey(String f, LongParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getLongArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected long[] getLongArray(IndexReader reader) throws IOException {
+
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse longs from document fields.
+   */
+  public interface LongParser {
+    /** Return a long representation of this field's value. */
+    public long parseLong(String string);
+  }
+
+}
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,37 @@
+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;
+
+
+/**
+ * Allows you to get a cache for an IndexReader.
+ */
+public interface CacheFactory {
+
+  /**
+   * @param r optionally used IndexReader
+   * @return  cache instance for r
+   * @throws IOException
+   */
+  public Cache getCache(IndexReader r) throws IOException;
+
+}
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,155 @@
+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.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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class ShortArrayCacheKey extends CacheKey {
+  private static final ShortParser DEFAULT_PARSER = new ShortParser() {
+    public short parseShort(String value) {
+      return Short.parseShort(value);
+    }
+  };
+
+  private String field;
+  private ShortParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public ShortArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ShortArrayCacheKey(String f, ShortParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getShortArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected short[] getShortArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse shorts from document fields.
+   */
+  public interface ShortParser {
+    /** Return a short representation of this field's value. */
+    public short parseShort(String string);
+  }
+
+}
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,153 @@
+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.
+ * 
+ * This key returns a StringIndex payload, which means that IndexReader#reopen will will not merge underlying IndexReader caches - instead, the full cache will
+ * have to be populated again. Use a StringObjectArrayCacheKey for faster {@link IndexReader#reopen()}.
+ */
+public class StringIndexCacheKey extends CacheKey {
+
+  protected String field;
+
+  /**
+   * @param f
+   */
+  public StringIndexCacheKey(String f) {
+    field = f.intern();
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getStringIndex(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected StringIndex getStringIndex(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 StringIndex(retArray, mterms);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  /**
+   * A String array and indexes into the array.
+   */
+  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;
+    }
+  }
+
+}
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,40 @@
+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 {
+  
+  private Object payload;
+  
+  /**
+   * @param payload cache data
+   */
+  public CacheData(Object payload) {
+    this.payload = payload;
+  }
+  
+  /**
+   * @return payload
+   */
+  public Object getPayload() { return payload; }
+}
Index: src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable doubles.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class DoubleObjectArrayCacheKey extends DoubleArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public DoubleObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public DoubleObjectArrayCacheKey(String f, DoubleParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.DoubleArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final double[] retArray = getDoubleArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Double(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.DoubleArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+
+}
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,158 @@
+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 bytes.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will
+ * merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key
+ * that returns a ObjectArray payload.
+ */
+public class ByteArrayCacheKey extends CacheKey {
+
+  private static final ByteParser DEFAULT_PARSER = new ByteParser() {
+    public byte parseByte(String value) {
+      return Byte.parseByte(value);
+    }
+  };
+
+  private String field;
+  private ByteParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public ByteArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ByteArrayCacheKey(String f, ByteParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getByteArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected byte[] getByteArray(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 retArray;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse bytes from document fields.
+   */
+  public interface ByteParser {
+    /** Return a byte representation of this field's value. */
+    public byte parseByte(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ByteObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,81 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * 
+ */
+public class ByteObjectArrayCacheKey extends ByteArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public ByteObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ByteObjectArrayCacheKey(String f, ByteParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ByteArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final byte[] retArray = getByteArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int doc) {
+        return new Byte(retArray[doc]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ByteArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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,60 @@
+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 a Cache.
+ * 
+ * @see CacheKey
+ * @see CacheData
+ * @see CacheFactory
+ */
+public interface Cache {
+
+  /**
+   * Default CacheFactory impl.
+   */
+  public static CacheFactory FACTORY = new CacheFactory() {
+    public Cache getCache(IndexReader r) {
+      return new SimpleMapCache();
+    }
+  };
+
+  /**
+   * Called when this Cache will no longer be used anymore, so that it can free any external resources it may have.
+   */
+  public void close();
+
+  /**
+   * @returns true if the Cache contains data for the specified key.
+   */
+  public boolean containsKey(CacheKey key);
+
+  /**
+   * 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);
+
+}
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,40 @@
+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 {
+  
+  private Object payload;
+  
+  /**
+   * @param payload cache data
+   */
+  public CacheData(Object payload) {
+    this.payload = payload;
+  }
+  
+  /**
+   * @return 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,37 @@
+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;
+
+
+/**
+ * Allows you to get a cache for an IndexReader.
+ */
+public interface CacheFactory {
+
+  /**
+   * @param r optionally used IndexReader
+   * @return  cache instance for r
+   * @throws IOException
+   */
+  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,147 @@
+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 {
+
+  /** Checks the internal cache for an appropriate entry, and if
+   * none is found reads <code>field</code> to see if it contains integers, floats
+   * or strings, and then calls one of the other methods in this class to get the
+   * values.  For string values, a StringIndex is returned.  After
+   * calling this method, there is an entry in the cache for both
+   * type <code>AUTO</code> and the actual found type.
+   * @param reader  Used to get field values.
+   * @param field   Which field contains the values.
+   * @return CacheKey based on field type.
+   * @throws IOException  If any error occurs.
+   */
+  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();
+
+        /**
+         * 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 IntObjectArrayCacheKey(field);
+        } catch (NumberFormatException nfe1) {
+          try {
+            Long.parseLong(termtext);
+            ret = new LongObjectArrayCacheKey(field);
+          } catch (NumberFormatException nfe2) {
+            try {
+              Float.parseFloat (termtext);
+              ret = new FloatObjectArrayCacheKey(field);
+            } catch (NumberFormatException nfe3) {
+              ret = new StringObjectArrayCacheKey(field);
+            }
+          }
+        }
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+      }
+      return ret;
+    } finally {
+      enumerator.close();
+    }
+  
+  }
+  /**
+   * Builds up the CacheData associated with this Key for the 
+   * specified IndexReader.
+   */
+  public abstract CacheData buildData(IndexReader r) throws IOException;
+
+  public abstract boolean equals(Object o);
+
+  public abstract int hashCode();
+
+  /**
+   * @return true if mergeData is a supported method for this CacheKey
+   * @see #mergeData
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /**
+   * 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");
+  }
+  
+  /** 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]+");
+   */
+  
+  
+  /**
+   * @return true if payload is instance of ObjectArray.
+   * @see ObjectArray
+   */
+  public boolean usesObjectArray() {
+    return false;
+  }
+
+}
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,157 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will
+ * merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key
+ * that returns a ObjectArray payload.
+ */
+public class DoubleArrayCacheKey extends CacheKey {
+
+  private static final DoubleParser DEFAULT_PARSER = new DoubleParser() {
+    public double parseDouble(String value) {
+      return Double.parseDouble(value);
+    }
+  };
+
+  private String field;
+  private DoubleParser parser = DEFAULT_PARSER;
+  
+  /**
+   * @param f
+   */
+  public DoubleArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public DoubleArrayCacheKey(String f, DoubleParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getDoubleArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected double[] getDoubleArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse doubles from document fields.
+   */
+  public interface DoubleParser {
+    /** Return a double representation of this field's value. */
+    public double parseDouble(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/DoubleObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable doubles.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class DoubleObjectArrayCacheKey extends DoubleArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public DoubleObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public DoubleObjectArrayCacheKey(String f, DoubleParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.DoubleArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final double[] retArray = getDoubleArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Double(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.DoubleArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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,156 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class FloatArrayCacheKey extends CacheKey {
+
+  private static final FloatParser DEFAULT_PARSER = new FloatParser() {
+    public float parseFloat(String value) {
+      return Float.parseFloat(value);
+    }
+  };
+
+  private String field;
+  private FloatParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public FloatArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public FloatArrayCacheKey(String f, FloatParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getFloatArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected float[] getFloatArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse floats from document fields.
+   */
+  public interface FloatParser {
+    /** Return a float representation of this field's value. */
+    public float parseFloat(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/FloatObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,83 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable floats.
+ * 
+ * CacheKey instances provide all of the functionality for generating CacheData based on an IndexReader instance.
+ */
+public class FloatObjectArrayCacheKey extends FloatArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public FloatObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public FloatObjectArrayCacheKey(String f, FloatParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.FloatArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final float[] retArray = getFloatArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Float(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.FloatArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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,158 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class IntArrayCacheKey extends CacheKey {
+
+  private static final IntParser DEFAULT_PARSER = new IntParser() {
+    public int parseInt(String value) {
+      return Integer.parseInt(value);
+    }
+  };
+
+  private String field;
+  private IntParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public IntArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public IntArrayCacheKey(String f, IntParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getIntArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected int[] getIntArray(IndexReader reader) throws IOException {
+    final int[] retArray = new int[reader.maxDoc()];
+    TermDocs termDocs = reader.termDocs();
+    TermEnum termEnum = reader.terms(new Term(field, ""));
+    int cnt = 0;
+    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;
+          cnt++;
+        }
+      } while (termEnum.next());
+    } finally {
+      termDocs.close();
+      termEnum.close();
+    }
+
+    return retArray;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  public CacheData mergeData(int[] starts, CacheData[] data) throws UnsupportedOperationException {
+    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);
+  }
+
+  /**
+   * Interface to parse ints from document fields.
+   */
+  public interface IntParser {
+    /** Return an integer representation of this field's value. */
+    public int parseInt(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/IntObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable ints.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class IntObjectArrayCacheKey extends IntArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public IntObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public IntObjectArrayCacheKey(String f, IntParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final int[] retArray = getIntArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Integer(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesObjectArray()
+   */
+  public boolean usesObjectArray() {
+    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,157 @@
+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.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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class LongArrayCacheKey extends CacheKey {
+
+  private static final LongParser DEFAULT_PARSER = new LongParser() {
+    public long parseLong(String value) {
+      return Long.parseLong(value);
+    }
+  };
+
+  private String field;
+  private LongParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public LongArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public LongArrayCacheKey(String f, LongParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getLongArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected long[] getLongArray(IndexReader reader) throws IOException {
+
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse longs from document fields.
+   */
+  public interface LongParser {
+    /** Return a long representation of this field's value. */
+    public long parseLong(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/LongObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,84 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable longs.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class LongObjectArrayCacheKey extends LongArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public LongObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public LongObjectArrayCacheKey(String f, LongParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.LongArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final long[] retArray = getLongArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Long(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.LongArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+}
Index: src/java/org/apache/lucene/index/cache/ObjectArray.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ObjectArray.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ObjectArray.java	(revision 0)
@@ -0,0 +1,40 @@
+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.
+ */
+
+/**
+ * Gives access to an array of Objects by doc id. Optionally provides access to a secondary array.
+ * 
+ * Using an ObjectArray payload allows value requests against a Multi/Segment/IndexReader to be directed to the correct IndexReader rather than merging the
+ * IndexReader's caches for the Multi/Segment/IndexReader. IndexReader#reopen can be much faster as a result.
+ * 
+ * @see CacheKey
+ */
+public abstract class ObjectArray {
+
+  /**
+   * @param doc
+   * @return
+   */
+  public abstract Object get(int doc);
+  
+  public Object get2(int doc) throws UnsupportedOperationException {
+    throw new UnsupportedOperationException();
+  }
+
+}
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,155 @@
+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.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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class ShortArrayCacheKey extends CacheKey {
+  private static final ShortParser DEFAULT_PARSER = new ShortParser() {
+    public short parseShort(String value) {
+      return Short.parseShort(value);
+    }
+  };
+
+  private String field;
+  private ShortParser parser = DEFAULT_PARSER;
+
+  /**
+   * @param f
+   */
+  public ShortArrayCacheKey(String f) {
+    field = f;
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ShortArrayCacheKey(String f, ShortParser p) {
+    this(f);
+    if (null != p)
+      parser = p;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getShortArray(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected short[] getShortArray(IndexReader reader) throws IOException {
+    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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+  /**
+   * Interface to parse shorts from document fields.
+   */
+  public interface ShortParser {
+    /** Return a short representation of this field's value. */
+    public short parseShort(String string);
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/ShortObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,85 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable ints.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class ShortObjectArrayCacheKey extends ShortArrayCacheKey {
+
+  /**
+   * @param f
+   */
+  public ShortObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /**
+   * @param f
+   * @param p
+   */
+  public ShortObjectArrayCacheKey(String f, ShortParser p) {
+    super(f, p);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ShortArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final short[] retArray = getShortArray(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Short(retArray[index]);
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.ShortArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    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;
+
+/**
+ * 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 boolean containsKey(CacheKey key) {
+    return data.containsKey(key);
+  }
+
+  public synchronized CacheData get(CacheKey key) {
+    return (CacheData) data.get(key);
+  }
+
+  public synchronized Set keySet() {
+    return Collections.unmodifiableSet(data.keySet());
+  }
+
+  public synchronized void put(CacheKey k, CacheData v) {
+    data.put(k, v);
+  }
+
+}
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,116 @@
+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.
+ * 
+ * This key returns an array payload which means that IndexReader#reopen will merge caches for Multi/Segment/IndexReaders. To avoid merging, use a key that
+ * returns a ObjectArray payload.
+ */
+public class StringArrayCacheKey extends CacheKey {
+
+  private String field;
+
+  /**
+   * @param f
+   */
+  public StringArrayCacheKey(String f) {
+    field = f.intern();
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  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);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return true;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#mergeData(int[], org.apache.lucene.index.cache.CacheData[])
+   */
+  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);
+  }
+
+}
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,153 @@
+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.
+ * 
+ * This key returns a StringIndex payload, which means that IndexReader#reopen will will not merge underlying IndexReader caches - instead, the full cache will
+ * have to be populated again. Use a StringObjectArrayCacheKey for faster {@link IndexReader#reopen()}.
+ */
+public class StringIndexCacheKey extends CacheKey {
+
+  protected String field;
+
+  /**
+   * @param f
+   */
+  public StringIndexCacheKey(String f) {
+    field = f.intern();
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getStringIndex(reader));
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#equals(java.lang.Object)
+   */
+  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;
+  }
+
+  /**
+   * @param reader
+   * @return
+   * @throws IOException
+   */
+  protected StringIndex getStringIndex(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 StringIndex(retArray, mterms);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#hashCode()
+   */
+  public int hashCode() {
+    final int prime = 31;
+    int result = prime + ((field == null) ? 0 : field.hashCode());
+    return result;
+  }
+
+  /**
+   * A String array and indexes into the array.
+   */
+  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;
+    }
+  }
+
+}
Index: src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/index/cache/StringObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,82 @@
+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.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A Key for identifying cacheable Strings. Caches all unique Strings as well as lookup indexes.
+ * 
+ * Returns a ObjectArray payload.
+ * 
+ * @see ObjectArray
+ */
+public class StringObjectArrayCacheKey extends StringIndexCacheKey {
+
+  /**
+   * @param f
+   */
+  public StringObjectArrayCacheKey(String f) {
+    super(f);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.StringIndexCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+
+    final StringIndex stringIndex = getStringIndex(reader);
+
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return new Integer(stringIndex.order[index]);
+      }
+      
+      public Object get2(int index) {
+        return stringIndex.lookup[stringIndex.order[index]];
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.StringIndexCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.IntArrayCacheKey#isMergable()
+   */
+  public boolean isMergable() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesFieldSource()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+}
Index: src/java/org/apache/lucene/index/DirectoryIndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryIndexReader.java	(revision 642612)
+++ 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/IndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/IndexReader.java	(revision 642612)
+++ 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,23 @@
   protected IndexReader(Directory directory) {
     this();
     this.directory = directory;
+    //TODO: find best set points
+    try {
+      cache = Cache.FACTORY.getCache(this);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
   }
   
-  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?
+      throw new RuntimeException(e);
+    }
   }
   
   /**
@@ -783,7 +798,61 @@
     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;
+  }
+  
+  public boolean isMulti() {
+    return false;
+  }
+
+  private static final class CacheCreationPlaceholder {
+    CacheData value;
+  }
+
+
   /** Implements actual undeleteAll() in subclass. */
   protected abstract void doUndeleteAll() throws CorruptIndexException, IOException;
 
@@ -830,6 +899,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 642612)
+++ src/java/org/apache/lucene/index/MultiReader.java	(working copy)
@@ -27,6 +27,11 @@
 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;
+import org.apache.lucene.index.cache.ObjectArray;
 
 /** An IndexReader which reads multiple indexes, appending their content.
  *
@@ -85,6 +90,13 @@
         hasDeletions = true;
     }
     starts[subReaders.length] = maxDoc;
+    //TODO: find best spots for this
+    try {
+      useCacheFactory(Cache.FACTORY);
+    } catch (IOException e) {
+     //TODO: what to do about this?
+      throw new RuntimeException(e);
+    }
   }
 
   /**
@@ -161,6 +173,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 +361,52 @@
     }
   }
   
+  /**
+   * 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(final CacheKey key) throws IOException {
+    if( ! key.usesObjectArray()) {
+      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);
+    }
+    
+    ObjectArray multiFieldValues = new ObjectArray() {
+      CacheData[] data = new CacheData[subReaders.length];
+
+      {
+        for (int i = 0; i < subReaders.length; i++) {
+          data[i] = subReaders[i].getCachedData(key);
+        }
+      }
+
+      public Object get(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray)data[i].getPayload()).get(doc - starts[i]); // dispatch to segment reader
+      }
+      
+      public Object get2(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get2(doc - starts[i]); // dispatch to segment reader
+      }
+
+    };
+    
+    return new CacheData(multiFieldValues);
+  }
+  
+  public boolean isMulti() {
+    return true;
+  }
+  
   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 642612)
+++ src/java/org/apache/lucene/index/MultiSegmentReader.java	(working copy)
@@ -19,6 +19,10 @@
 
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.index.cache.Cache;
+import org.apache.lucene.index.cache.CacheData;
+import org.apache.lucene.index.cache.CacheKey;
+import org.apache.lucene.index.cache.ObjectArray;
 import org.apache.lucene.store.Directory;
 
 import java.io.IOException;
@@ -410,7 +414,53 @@
     // maybe close directory
     super.doClose();
   }
+  
+  /**
+   * 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(final CacheKey key) throws IOException {
+    if( ! key.usesObjectArray()) {
+      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);
+    }
+    
+    ObjectArray multiFieldValues = new ObjectArray() {
+      CacheData[] data = new CacheData[subReaders.length];
+
+      {
+        for (int i = 0; i < subReaders.length; i++) {
+          data[i] = subReaders[i].getCachedData(key);
+        }
+      }
+
+      public Object get(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray)data[i].getPayload()).get(doc - starts[i]); // dispatch to segment reader
+      }
+      
+      public Object get2(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get2(doc - starts[i]); // dispatch to segment reader
+      }
+
+    };
+    
+    return new CacheData(multiFieldValues);
+  }
+  
+  public boolean isMulti() {
+    return true;
+  }
+
   public Collection getFieldNames (IndexReader.FieldOption fieldNames) {
     ensureOpen();
     return getFieldNames(fieldNames, this.subReaders);
Index: src/java/org/apache/lucene/search/ExtendedFieldCache.java
===================================================================
--- src/java/org/apache/lucene/search/ExtendedFieldCache.java	(revision 642612)
+++ 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 642612)
+++ 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,10 @@
   // inherit javadocs
   public long[] getLongs(IndexReader reader, String field, LongParser parser)
       throws IOException {
-    return (long[]) longsCache.get(reader, new Entry(field, parser));
+    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 +45,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 642612)
+++ 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 642612)
+++ 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 SortComparatorArrayCacheKey
+                               (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 642612)
+++ 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 642612)
+++ src/java/org/apache/lucene/search/FieldSortedHitQueue.java	(working copy)
@@ -18,15 +18,26 @@
  */
 
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.ByteObjectArrayCacheKey;
+import org.apache.lucene.index.cache.CacheData;
+import org.apache.lucene.index.cache.CacheKey;
+import org.apache.lucene.index.cache.DoubleObjectArrayCacheKey;
+import org.apache.lucene.index.cache.ObjectArray;
+import org.apache.lucene.index.cache.FloatObjectArrayCacheKey;
+import org.apache.lucene.index.cache.IntObjectArrayCacheKey;
+import org.apache.lucene.index.cache.LongObjectArrayCacheKey;
+import org.apache.lucene.index.cache.ShortObjectArrayCacheKey;
+import org.apache.lucene.index.cache.StringObjectArrayCacheKey;
 import org.apache.lucene.util.PriorityQueue;
 
+
 import java.io.IOException;
 import java.text.Collator;
 import java.util.Locale;
 
 /**
  * Expert: A hit queue for sorting by hits by terms in more than one field.
- * Uses <code>FieldCache.DEFAULT</code> for maintaining internal term lookup tables.
+ * Uses <code>Cache</code> for maintaining internal term lookup tables.
  *
  * <p>Created: Dec 8, 2003 12:56:03 PM
  *
@@ -34,7 +45,7 @@
  * @since   lucene 1.4
  * @version $Id$
  * @see Searcher#search(Query,Filter,int,Sort)
- * @see FieldCache
+ * @see Cache
  */
 public class FieldSortedHitQueue
 extends PriorityQueue {
@@ -158,28 +169,130 @@
     return fields;
   }
   
+  static public class CacheEntryKey extends CacheKey {
+    private String field;
+    private int type;
+    private Object custom;
+    private Locale locale;
+
+    CacheEntryKey(String field, int type, Locale locale) {
+      this.field = field.intern();
+      this.type = type;
+      this.custom = null;
+      this.locale = locale;
+    }
+
+    /** Creates one of these objects for a custom comparator. */
+    CacheEntryKey(String field, Object custom) {
+      this.field = field.intern();
+      this.type = SortField.CUSTOM;
+      this.custom = custom;
+      this.locale = null;
+    }
+
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((custom == null) ? 0 : custom.hashCode());
+      result = prime * result + ((field == null) ? 0 : field.hashCode());
+      result = prime * result + ((locale == null) ? 0 : locale.hashCode());
+      result = prime * result + type;
+      return result;
+    }
+
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      final CacheEntryKey other = (CacheEntryKey) obj;
+      if (custom == null) {
+        if (other.custom != null)
+          return false;
+      } else if (!custom.equals(other.custom))
+        return false;
+      if (field == null) {
+        if (other.field != null)
+          return false;
+      } else if (!field.equals(other.field))
+        return false;
+      if (locale == null) {
+        if (other.locale != null)
+          return false;
+      } else if (!locale.equals(other.locale))
+        return false;
+      if (type != other.type)
+        return false;
+      return true;
+    }
+
+    public CacheData buildData(IndexReader reader) throws IOException {
+      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 = ((SortComparatorSource)custom).newComparator (reader, field);
+          break;
+        default:
+          throw new RuntimeException ("unknown field type: "+type);
+      }
+      return new CacheData(comparator);
+    }
+  }
+  
   static ScoreDocComparator getCachedComparator (IndexReader reader, String field, int type, Locale locale, SortComparatorSource factory)
   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);
+    CacheKey key = (factory != null)
+    ? new CacheEntryKey(field, factory)
+    : new CacheEntryKey(field, type, locale);
+    CacheData data = reader.getCachedData(key);
+    return (ScoreDocComparator) data.getPayload();
   }
 
   /** 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)
         throws IOException {
+      //System.out.println("creating new cache entry for reader:" + reader + " with " + entryKey.getClass().getCanonicalName());
       FieldCacheImpl.Entry entry = (FieldCacheImpl.Entry) entryKey;
       String fieldname = entry.field;
       int type = entry.type;
       Locale locale = entry.locale;
       SortComparatorSource factory = (SortComparatorSource) entry.custom;
       ScoreDocComparator comparator;
+
       switch (type) {
         case SortField.AUTO:
           comparator = comparatorAuto (reader, fieldname);
@@ -219,26 +332,26 @@
    /**
    * 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 ObjectArray fieldOrder = (ObjectArray) reader.getCachedData(new ByteObjectArrayCacheKey(field)).getPayload();;
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final int fi = fieldOrder[i.doc];
-        final int fj = fieldOrder[j.doc];
+        final int fi = ((Byte)fieldOrder.get(i.doc)).byteValue();
+        final int fj = ((Byte)fieldOrder.get(j.doc)).byteValue();
         if (fi < fj) return -1;
         if (fi > fj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Byte(fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -250,26 +363,26 @@
   /**
    * 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 ObjectArray fieldOrder = (ObjectArray) reader.getCachedData(new ShortObjectArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final int fi = fieldOrder[i.doc];
-        final int fj = fieldOrder[j.doc];
+        final short fi = ((Short)fieldOrder.get(i.doc)).shortValue();
+        final short fj = ((Short)fieldOrder.get(j.doc)).shortValue();
         if (fi < fj) return -1;
         if (fi > fj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Short(fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -281,26 +394,26 @@
   /**
    * 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 ObjectArray fieldOrder = (ObjectArray) reader.getCachedData(new IntObjectArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final int fi = fieldOrder[i.doc];
-        final int fj = fieldOrder[j.doc];
+        final int fi = ((Integer)fieldOrder.get(i.doc)).intValue();
+        final int fj = ((Integer)fieldOrder.get(j.doc)).intValue();
         if (fi < fj) return -1;
         if (fi > fj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Integer (fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -312,26 +425,26 @@
   /**
    * 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 ObjectArray fieldOrder = (ObjectArray) reader.getCachedData(new LongObjectArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final long li = fieldOrder[i.doc];
-        final long lj = fieldOrder[j.doc];
+        final long li = ((Long)fieldOrder.get(i.doc)).longValue();
+        final long lj = ((Long)fieldOrder.get(j.doc)).longValue();
         if (li < lj) return -1;
         if (li > lj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Long(fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -344,26 +457,27 @@
   /**
    * 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 ObjectArray fieldOrder = (ObjectArray)  reader.getCachedData(new FloatObjectArrayCacheKey(field)).getPayload();
+
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final float fi = fieldOrder[i.doc];
-        final float fj = fieldOrder[j.doc];
+        final float fi = ((Float)fieldOrder.get(i.doc)).floatValue();
+        final float fj = ((Float)fieldOrder.get(j.doc)).floatValue();
         if (fi < fj) return -1;
         if (fi > fj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Float (fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -375,26 +489,26 @@
   /**
    * 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 ObjectArray fieldOrder = (ObjectArray) reader.getCachedData(new DoubleObjectArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final double di = fieldOrder[i.doc];
-        final double dj = fieldOrder[j.doc];
+        final double di = ((Double)fieldOrder.get(i.doc)).doubleValue();
+        final double dj = ((Double)fieldOrder.get(j.doc)).doubleValue();
         if (di < dj) return -1;
         if (di > dj) return 1;
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return new Double (fieldOrder[i.doc]);
+        return (Comparable) fieldOrder.get(i.doc);
       }
 
       public int sortType() {
@@ -406,26 +520,56 @@
   /**
    * 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);
+    final ObjectArray data = (ObjectArray) 
+    reader.getCachedData(new StringObjectArrayCacheKey(field)).getPayload();
+ 
+    if(reader.isMulti()) {
+      return new ScoreDocComparator () {
+  
+        public final int compare (final ScoreDoc i, final ScoreDoc j) {
+          final String fi = (String)data.get2(i.doc);
+          final String fj = (String)data.get2(j.doc);
+          if (fi == fj) {
+            return 0;
+          } else if (fi == null) {
+            return -1;
+          } else if (fj == null) {
+            return 1;
+          }
+          
+          return fi.compareTo(fj);
+        }
+  
+        public Comparable sortValue (final ScoreDoc i) {
+          return (String)data.get2(i.doc);
+        }
+  
+        public int sortType() {
+          return SortField.STRING;
+        }
+      };
+    }
+    
     return new ScoreDocComparator () {
 
       public final int compare (final ScoreDoc i, final ScoreDoc j) {
-        final int fi = index.order[i.doc];
-        final int fj = index.order[j.doc];
+        final int fi = ((Integer)data.get(i.doc)).intValue();
+        final int fj = ((Integer)data.get(j.doc)).intValue();
         if (fi < fj) return -1;
         if (fi > fj) return 1;
+        
         return 0;
       }
 
       public Comparable sortValue (final ScoreDoc i) {
-        return index.lookup[index.order[i.doc]];
+        return (String)data.get2(i.doc);
       }
 
       public int sortType() {
@@ -437,7 +581,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,25 +589,26 @@
   throws IOException {
     final Collator collator = Collator.getInstance (locale);
     final String field = fieldname.intern();
-    final String[] index = FieldCache.DEFAULT.getStrings (reader, field);
+
+    final ObjectArray data = (ObjectArray)  reader.getCachedData(new StringObjectArrayCacheKey(field)).getPayload();
     return new ScoreDocComparator() {
 
     	public final int compare(final ScoreDoc i, final ScoreDoc j) {
-			String is = index[i.doc];
-			String js = index[j.doc];
-			if (is == js) {
-				return 0;
-			} else if (is == null) {
-				return -1;
-			} else if (js == null) {
-				return 1;
-			} else {
-				return collator.compare(is, js);
-			}
-		}
+    	  String is = ((String)data.get2(i.doc));
+    	  String js = ((String)data.get2(j.doc));
+    	  if (is == js) {
+    	    return 0;
+    	  } else if (is == null) {
+    	    return -1;
+    	  } else if (js == null) {
+    	    return 1;
+    	  } else {
+    	    return collator.compare(is, js);
+    	  }
+    	}
 
       public Comparable sortValue (final ScoreDoc i) {
-        return index[i.doc];
+        return (Comparable) data.get2(i.doc);
       }
 
       public int sortType() {
@@ -478,24 +623,23 @@
    * 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);
+
+    if (lookupArray instanceof StringObjectArrayCacheKey) {
       return comparatorString (reader, field);
-    } else if (lookupArray instanceof int[]) {
+    } else if (lookupArray instanceof IntObjectArrayCacheKey) {
       return comparatorInt (reader, field);
-    } else if (lookupArray instanceof long[]) {
+    } else if (lookupArray instanceof LongObjectArrayCacheKey) {
       return comparatorLong (reader, field);
-    } else if (lookupArray instanceof float[]) {
+    } else if (lookupArray instanceof FloatObjectArrayCacheKey) {
       return comparatorFloat (reader, field);
-    } else if (lookupArray instanceof String[]) {
-      return comparatorString (reader, field);
     } else {
       throw new RuntimeException ("unknown data type in field '"+field+"'");
     }
Index: src/java/org/apache/lucene/search/function/ByteFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ByteFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/ByteFieldSource.java	(working copy)
@@ -36,6 +36,7 @@
  * 
  * @see org.apache.lucene.search.function.FieldCacheSource for requirements 
  * on the field. 
+ * TODO: deprecated methods/classes used
  */
 public class ByteFieldSource extends FieldCacheSource {
   private FieldCache.ByteParser parser;
@@ -48,7 +49,8 @@
   }
 
   /**
-   * Create a cached byte field source with a specific string-to-byte parser. 
+   * Create a cached byte field source with a specific string-to-byte parser.
+   * @deprecated
    */
   public ByteFieldSource(String field, FieldCache.ByteParser parser) {
     super(field);
Index: src/java/org/apache/lucene/search/function/FieldCacheSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/FieldCacheSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/FieldCacheSource.java	(working copy)
@@ -66,6 +66,7 @@
    * @param cache FieldCache so that values of a field are loaded once per reader (RAM allowing)
    * @param field Field for which values are required.
    * @see ValueSource
+   * @deprecated
    */
   public abstract DocValues getCachedFieldValues(FieldCache cache, String field, IndexReader reader) throws IOException;
 
Index: src/java/org/apache/lucene/search/function/FloatFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/FloatFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/FloatFieldSource.java	(working copy)
@@ -38,6 +38,8 @@
  * on the field.
  *  
  * @author yonik
+ * 
+ * TODO: deprecated methods/classes used
  */
 public class FloatFieldSource extends FieldCacheSource {
   private FieldCache.FloatParser parser;
@@ -51,6 +53,7 @@
 
   /**
    * Create a cached float field source with a specific string-to-float parser. 
+   * @deprecated
    */
   public FloatFieldSource(String field, FieldCache.FloatParser parser) {
     super(field);
Index: src/java/org/apache/lucene/search/function/IntFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/IntFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/IntFieldSource.java	(working copy)
@@ -37,7 +37,7 @@
  * @see org.apache.lucene.search.function.FieldCacheSource for requirements 
  * on the field.
  *
- *
+ *  TODO: deprecated methods/classes used
  */
 public class IntFieldSource extends FieldCacheSource {
   private FieldCache.IntParser parser;
Index: src/java/org/apache/lucene/search/function/OrdFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/OrdFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/OrdFieldSource.java	(working copy)
@@ -45,6 +45,8 @@
  * supported anymore in such a case.</font>
  * 
  * @author yonik
+ * 
+ * TODO: deprecated methods/classes used
  */
 
 public class OrdFieldSource extends ValueSource {
Index: src/java/org/apache/lucene/search/function/ReverseOrdFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ReverseOrdFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/ReverseOrdFieldSource.java	(working copy)
@@ -46,6 +46,7 @@
  * supported anymore in such a case.</font>
  * 
  * @author yonik
+ * TODO: deprecated methods/classes used
  */
 
 public class ReverseOrdFieldSource extends ValueSource {
Index: src/java/org/apache/lucene/search/function/ShortFieldSource.java
===================================================================
--- src/java/org/apache/lucene/search/function/ShortFieldSource.java	(revision 642612)
+++ src/java/org/apache/lucene/search/function/ShortFieldSource.java	(working copy)
@@ -36,6 +36,7 @@
  * 
  * @see org.apache.lucene.search.function.FieldCacheSource for requirements 
  * on the field.
+ * TODO: deprecated methods/classes used
  */
 public class ShortFieldSource extends FieldCacheSource {
   private FieldCache.ShortParser parser;
Index: src/java/org/apache/lucene/search/SortComparator.java
===================================================================
--- src/java/org/apache/lucene/search/SortComparator.java	(revision 642612)
+++ src/java/org/apache/lucene/search/SortComparator.java	(working copy)
@@ -18,6 +18,7 @@
  */
 
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.ObjectArray;
 
 import java.io.IOException;
 
@@ -42,20 +43,25 @@
 public abstract class SortComparator
 implements SortComparatorSource {
 
-  // inherit javadocs
+  
+  /* (non-Javadoc)
+   * @see org.apache.lucene.search.SortComparatorSource#newComparator(org.apache.lucene.index.IndexReader, java.lang.String)
+   */
   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 ObjectArray cachedValues =  (ObjectArray) 
+    reader.getCachedData(new SortComparatorObjectArrayCacheKey
+        (field, SortComparator.this)).getPayload();
     
     return new ScoreDocComparator() {
 
       public int compare (ScoreDoc i, ScoreDoc j) {
-        return cachedValues[i.doc].compareTo (cachedValues[j.doc]);
+        return ((Comparable)cachedValues.get(i.doc)).compareTo (cachedValues.get(j.doc));
       }
 
       public Comparable sortValue (ScoreDoc i) {
-        return cachedValues[i.doc];
+        return (Comparable) cachedValues.get(i.doc);
       }
 
       public int sortType(){
Index: src/java/org/apache/lucene/search/SortComparatorArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/search/SortComparatorArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/search/SortComparatorArrayCacheKey.java	(revision 0)
@@ -0,0 +1,96 @@
+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: setup with ObjectArray for faster reopen
+ * :TODO: this class must live in o.a.l.search because of access level of comparator.getComparable
+ */
+public class SortComparatorArrayCacheKey extends CacheKey {
+
+  private String field;
+  private SortComparator comparator;
+
+  public SortComparatorArrayCacheKey(String f, SortComparator c) {
+    field = f.intern();
+    comparator = c;
+  }
+
+  public CacheData buildData(IndexReader reader) throws IOException {
+    return new CacheData(getComparables(reader));
+  }
+
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final SortComparatorArrayCacheKey other = (SortComparatorArrayCacheKey) 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;
+  }
+
+  protected Comparable[] getComparables(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 retArray;
+  }
+
+  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;
+  }
+}
Index: src/java/org/apache/lucene/search/SortComparatorObjectArrayCacheKey.java
===================================================================
--- src/java/org/apache/lucene/search/SortComparatorObjectArrayCacheKey.java	(revision 0)
+++ src/java/org/apache/lucene/search/SortComparatorObjectArrayCacheKey.java	(revision 0)
@@ -0,0 +1,69 @@
+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 java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.cache.CacheData;
+import org.apache.lucene.index.cache.ObjectArray;
+
+/* :TODO: javadocs 
+ * :TODO: setup with ObjectArray for faster reopen
+ * :TODO: this class must live in o.a.l.search because of access level of comparator.getComparable
+ */
+public class SortComparatorObjectArrayCacheKey extends SortComparatorArrayCacheKey {
+
+  /**
+   * @param f
+   * @param c
+   */
+  public SortComparatorObjectArrayCacheKey(String f, SortComparator c) {
+    super(f, c);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.search.SortComparatorArrayCacheKey#buildData(org.apache.lucene.index.IndexReader)
+   */
+  public CacheData buildData(IndexReader reader) throws IOException {
+    final Comparable[] retArray = getComparables(reader);
+    ObjectArray fieldValues = new ObjectArray() {
+
+      public Object get(int index) {
+        return retArray[index];
+      }
+
+    };
+
+    return new CacheData(fieldValues);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.search.SortComparatorArrayCacheKey#hashCode()
+   */
+  public int hashCode() {
+    return super.hashCode() * 31;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.lucene.index.cache.CacheKey#usesObjectArray()
+   */
+  public boolean usesObjectArray() {
+    return true;
+  }
+}
Index: src/test/org/apache/lucene/index/cache/TestCacheKey.java
===================================================================
--- src/test/org/apache/lucene/index/cache/TestCacheKey.java	(revision 0)
+++ src/test/org/apache/lucene/index/cache/TestCacheKey.java	(revision 0)
@@ -0,0 +1,373 @@
+package org.apache.lucene.index.cache;
+
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.LuceneTestCase;
+
+public class TestCacheKey extends LuceneTestCase {
+  private final static String FIELD = "f";
+  private final static int NUM_DOCS_PER_READER = 10;
+  private int cnt = 0;
+
+  private RAMDirectory getRamDir() throws Exception {
+
+    RAMDirectory rd1 = new RAMDirectory();
+    IndexWriter writer = new IndexWriter(rd1, new WhitespaceAnalyzer(), MaxFieldLength.LIMITED);
+    writer.setRAMBufferSizeMB(.01);
+    for (int i = 0; i < NUM_DOCS_PER_READER; i++) {
+      Document doc = new Document();
+      doc.add(new Field(FIELD, Integer.toString(i + cnt), Store.YES, Index.UN_TOKENIZED));
+      writer.addDocument(doc);
+    }
+    writer.close();
+    cnt += NUM_DOCS_PER_READER;
+
+    return rd1;
+  }
+
+  public void setUp() throws Exception {
+    super.setUp();
+    cnt = 0;
+  }
+
+  /**
+   * Tests merging for each of the mergable CachKeys.
+   * 
+   * @throws Exception
+   */
+  public void testMergeArrayCacheKeys() throws Exception {
+    Directory dir1 = getRamDir();
+    Directory dir2 = getRamDir();
+    IndexReader reader1 = IndexReader.open(dir1);
+    IndexReader reader2 = IndexReader.open(dir2);
+
+    int[] starts = new int[] { 0, 10, 20 };
+
+    // IntArrayCacheKey
+    IntArrayCacheKey intArrayCacheKey = new IntArrayCacheKey("f");
+    assertEquals(true, intArrayCacheKey.isMergable());
+    assertEquals(false, intArrayCacheKey.usesObjectArray());
+
+    CacheData data1 = intArrayCacheKey.buildData(reader1);
+    CacheData data2 = intArrayCacheKey.buildData(reader2);
+
+    CacheData[] data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    CacheData mergedData = intArrayCacheKey.mergeData(starts, data);
+
+    int[] ints = (int[]) mergedData.getPayload();
+
+    for (int i = 0; i < ints.length; i++) {
+      assertEquals(ints[i], i);
+    }
+
+    // LongArrayCacheKey
+    LongArrayCacheKey longArrayCacheKey = new LongArrayCacheKey("f");
+    assertEquals(true, longArrayCacheKey.isMergable());
+    assertEquals(false, longArrayCacheKey.usesObjectArray());
+
+    data1 = longArrayCacheKey.buildData(reader1);
+    data2 = longArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = longArrayCacheKey.mergeData(starts, data);
+
+    long[] longs = (long[]) mergedData.getPayload();
+
+    for (int i = 0; i < longs.length; i++) {
+      assertEquals(longs[i], i);
+    }
+
+    // ByteArrayCacheKey
+    ByteArrayCacheKey byteArrayCacheKey = new ByteArrayCacheKey("f");
+    assertEquals(true, byteArrayCacheKey.isMergable());
+    assertEquals(false, byteArrayCacheKey.usesObjectArray());
+
+    data1 = byteArrayCacheKey.buildData(reader1);
+    data2 = byteArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = byteArrayCacheKey.mergeData(starts, data);
+
+    byte[] bytes = (byte[]) mergedData.getPayload();
+
+    for (int i = 0; i < bytes.length; i++) {
+      assertEquals(bytes[i], i);
+    }
+
+    // ShortArrayCacheKey
+    ShortArrayCacheKey shortArrayCacheKey = new ShortArrayCacheKey("f");
+    assertEquals(true, shortArrayCacheKey.isMergable());
+    assertEquals(false, shortArrayCacheKey.usesObjectArray());
+
+    data1 = shortArrayCacheKey.buildData(reader1);
+    data2 = shortArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = shortArrayCacheKey.mergeData(starts, data);
+
+    short[] shorts = (short[]) mergedData.getPayload();
+
+    for (int i = 0; i < shorts.length; i++) {
+      assertEquals(shorts[i], i);
+    }
+
+    // DoubleArrayCacheKey
+    DoubleArrayCacheKey doubleArrayCacheKey = new DoubleArrayCacheKey("f");
+    assertEquals(true, doubleArrayCacheKey.isMergable());
+    assertEquals(false, doubleArrayCacheKey.usesObjectArray());
+
+    data1 = doubleArrayCacheKey.buildData(reader1);
+    data2 = doubleArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = doubleArrayCacheKey.mergeData(starts, data);
+
+    double[] doubles = (double[]) mergedData.getPayload();
+
+    for (int i = 0; i < doubles.length; i++) {
+      assertTrue(doubles[i] == i);
+    }
+
+    // FloatArrayCacheKey
+    FloatArrayCacheKey floatArrayCacheKey = new FloatArrayCacheKey("f");
+    assertEquals(true, floatArrayCacheKey.isMergable());
+    assertEquals(false, floatArrayCacheKey.usesObjectArray());
+
+    data1 = floatArrayCacheKey.buildData(reader1);
+    data2 = floatArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = floatArrayCacheKey.mergeData(starts, data);
+
+    float[] floats = (float[]) mergedData.getPayload();
+
+    for (int i = 0; i < floats.length; i++) {
+      assertTrue(floats[i] == i);
+    }
+
+    // StringArrayCacheKey
+    StringArrayCacheKey StringArrayCacheKey = new StringArrayCacheKey("f");
+    assertEquals(true, StringArrayCacheKey.isMergable());
+    assertEquals(false, StringArrayCacheKey.usesObjectArray());
+
+    data1 = StringArrayCacheKey.buildData(reader1);
+    data2 = StringArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = StringArrayCacheKey.mergeData(starts, data);
+
+    String[] strings = (String[]) mergedData.getPayload();
+
+    for (int i = 0; i < strings.length; i++) {
+      assertEquals(strings[i], Integer.toString(i));
+    }
+    
+    reader1.close();
+    reader2.close();
+    dir1.close();
+    dir2.close();
+
+  }
+
+  /**
+   * Tests ObjectArray for each of the ObjectArray CachKeys.
+   * 
+   * @throws Exception
+   */
+  public void testObjectArrayCacheKeys() throws Exception {
+    Directory dir1 = getRamDir();
+    Directory dir2 = getRamDir();
+    IndexReader reader1 = IndexReader.open(dir1);
+    IndexReader reader2 = IndexReader.open(dir2);
+
+    final int[] starts = new int[] { 0, 10, 20 };
+
+    // IntObjectArrayCacheKey
+    IntObjectArrayCacheKey intArrayObjectCacheKey = new IntObjectArrayCacheKey("f");
+    assertEquals(false, intArrayObjectCacheKey.isMergable());
+    assertEquals(true, intArrayObjectCacheKey.usesObjectArray());
+
+    CacheData data1 = intArrayObjectCacheKey.buildData(reader1);
+    CacheData data2 = intArrayObjectCacheKey.buildData(reader2);
+
+    CacheData[] data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    ObjectArray multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Integer) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // ByteObjectArrayCacheKey
+    ByteObjectArrayCacheKey byteArrayObjectCacheKey = new ByteObjectArrayCacheKey("f");
+    assertEquals(false, byteArrayObjectCacheKey.isMergable());
+    assertEquals(true, byteArrayObjectCacheKey.usesObjectArray());
+
+    data1 = byteArrayObjectCacheKey.buildData(reader1);
+    data2 = byteArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (byte i = 0; i < 20; i++) {
+      assertEquals(((Byte) multiFieldValues.get(i)).byteValue(), i);
+    }
+
+    
+    // DoubleObjectArrayCacheKey
+    DoubleObjectArrayCacheKey doubleArrayObjectCacheKey = new DoubleObjectArrayCacheKey("f");
+    assertEquals(false, doubleArrayObjectCacheKey.isMergable());
+    assertEquals(true, doubleArrayObjectCacheKey.usesObjectArray());
+
+    data1 = doubleArrayObjectCacheKey.buildData(reader1);
+    data2 = doubleArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Double) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // FloatObjectArrayCacheKey
+    FloatObjectArrayCacheKey floatArrayObjectCacheKey = new FloatObjectArrayCacheKey("f");
+    assertEquals(false, floatArrayObjectCacheKey.isMergable());
+    assertEquals(true, floatArrayObjectCacheKey.usesObjectArray());
+
+    data1 = floatArrayObjectCacheKey.buildData(reader1);
+    data2 = floatArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Float) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // LongObjectArrayCacheKey
+    LongObjectArrayCacheKey longArrayObjectCacheKey = new LongObjectArrayCacheKey("f");
+    assertEquals(false, longArrayObjectCacheKey.isMergable());
+    assertEquals(true, longArrayObjectCacheKey.usesObjectArray());
+
+    data1 = longArrayObjectCacheKey.buildData(reader1);
+    data2 = longArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Long) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // ShortObjectArrayCacheKey
+    ShortObjectArrayCacheKey shortArrayObjectCacheKey = new ShortObjectArrayCacheKey("f");
+    assertEquals(false, shortArrayObjectCacheKey.isMergable());
+    assertEquals(true, shortArrayObjectCacheKey.usesObjectArray());
+
+    data1 = shortArrayObjectCacheKey.buildData(reader1);
+    data2 = shortArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Short) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // StringObjectArrayCacheKey
+    StringObjectArrayCacheKey StringArrayObjectCacheKey = new StringObjectArrayCacheKey("f");
+    assertEquals(false, StringArrayObjectCacheKey.isMergable());
+    assertEquals(true, StringArrayObjectCacheKey.usesObjectArray());
+
+    data1 = StringArrayObjectCacheKey.buildData(reader1);
+    data2 = StringArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+    
+    for (int i = 0; i < 20; i++) {
+      assertTrue(((String) multiFieldValues.get2(i)).equals(Integer.toString(i)));
+    }
+    
+    reader1.close();
+    reader2.close();
+    dir1.close();
+    dir2.close();
+    
+  }
+
+  private ObjectArray getMultiObjectArray(final CacheData[] data, final int[] starts) {
+    ObjectArray multiFieldValues = new ObjectArray() {
+
+      public Object get(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get(doc - starts[i]); // dispatch to segment reader
+      }
+      
+      public Object get2(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get2(doc - starts[i]); // dispatch to segment reader
+      }
+
+      private int readerIndex(int doc) {
+        if (doc > 9) {
+          return 1;
+        }
+        return 0;
+      }
+
+    };
+
+    return multiFieldValues;
+  }
+
+}
Index: src/test/org/apache/lucene/index/cache/TestCacheKey.java
===================================================================
--- src/test/org/apache/lucene/index/cache/TestCacheKey.java	(revision 0)
+++ src/test/org/apache/lucene/index/cache/TestCacheKey.java	(revision 0)
@@ -0,0 +1,373 @@
+package org.apache.lucene.index.cache;
+
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.LuceneTestCase;
+
+public class TestCacheKey extends LuceneTestCase {
+  private final static String FIELD = "f";
+  private final static int NUM_DOCS_PER_READER = 10;
+  private int cnt = 0;
+
+  private RAMDirectory getRamDir() throws Exception {
+
+    RAMDirectory rd1 = new RAMDirectory();
+    IndexWriter writer = new IndexWriter(rd1, new WhitespaceAnalyzer(), MaxFieldLength.LIMITED);
+    writer.setRAMBufferSizeMB(.01);
+    for (int i = 0; i < NUM_DOCS_PER_READER; i++) {
+      Document doc = new Document();
+      doc.add(new Field(FIELD, Integer.toString(i + cnt), Store.YES, Index.UN_TOKENIZED));
+      writer.addDocument(doc);
+    }
+    writer.close();
+    cnt += NUM_DOCS_PER_READER;
+
+    return rd1;
+  }
+
+  public void setUp() throws Exception {
+    super.setUp();
+    cnt = 0;
+  }
+
+  /**
+   * Tests merging for each of the mergable CachKeys.
+   * 
+   * @throws Exception
+   */
+  public void testMergeArrayCacheKeys() throws Exception {
+    Directory dir1 = getRamDir();
+    Directory dir2 = getRamDir();
+    IndexReader reader1 = IndexReader.open(dir1);
+    IndexReader reader2 = IndexReader.open(dir2);
+
+    int[] starts = new int[] { 0, 10, 20 };
+
+    // IntArrayCacheKey
+    IntArrayCacheKey intArrayCacheKey = new IntArrayCacheKey("f");
+    assertEquals(true, intArrayCacheKey.isMergable());
+    assertEquals(false, intArrayCacheKey.usesObjectArray());
+
+    CacheData data1 = intArrayCacheKey.buildData(reader1);
+    CacheData data2 = intArrayCacheKey.buildData(reader2);
+
+    CacheData[] data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    CacheData mergedData = intArrayCacheKey.mergeData(starts, data);
+
+    int[] ints = (int[]) mergedData.getPayload();
+
+    for (int i = 0; i < ints.length; i++) {
+      assertEquals(ints[i], i);
+    }
+
+    // LongArrayCacheKey
+    LongArrayCacheKey longArrayCacheKey = new LongArrayCacheKey("f");
+    assertEquals(true, longArrayCacheKey.isMergable());
+    assertEquals(false, longArrayCacheKey.usesObjectArray());
+
+    data1 = longArrayCacheKey.buildData(reader1);
+    data2 = longArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = longArrayCacheKey.mergeData(starts, data);
+
+    long[] longs = (long[]) mergedData.getPayload();
+
+    for (int i = 0; i < longs.length; i++) {
+      assertEquals(longs[i], i);
+    }
+
+    // ByteArrayCacheKey
+    ByteArrayCacheKey byteArrayCacheKey = new ByteArrayCacheKey("f");
+    assertEquals(true, byteArrayCacheKey.isMergable());
+    assertEquals(false, byteArrayCacheKey.usesObjectArray());
+
+    data1 = byteArrayCacheKey.buildData(reader1);
+    data2 = byteArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = byteArrayCacheKey.mergeData(starts, data);
+
+    byte[] bytes = (byte[]) mergedData.getPayload();
+
+    for (int i = 0; i < bytes.length; i++) {
+      assertEquals(bytes[i], i);
+    }
+
+    // ShortArrayCacheKey
+    ShortArrayCacheKey shortArrayCacheKey = new ShortArrayCacheKey("f");
+    assertEquals(true, shortArrayCacheKey.isMergable());
+    assertEquals(false, shortArrayCacheKey.usesObjectArray());
+
+    data1 = shortArrayCacheKey.buildData(reader1);
+    data2 = shortArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = shortArrayCacheKey.mergeData(starts, data);
+
+    short[] shorts = (short[]) mergedData.getPayload();
+
+    for (int i = 0; i < shorts.length; i++) {
+      assertEquals(shorts[i], i);
+    }
+
+    // DoubleArrayCacheKey
+    DoubleArrayCacheKey doubleArrayCacheKey = new DoubleArrayCacheKey("f");
+    assertEquals(true, doubleArrayCacheKey.isMergable());
+    assertEquals(false, doubleArrayCacheKey.usesObjectArray());
+
+    data1 = doubleArrayCacheKey.buildData(reader1);
+    data2 = doubleArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = doubleArrayCacheKey.mergeData(starts, data);
+
+    double[] doubles = (double[]) mergedData.getPayload();
+
+    for (int i = 0; i < doubles.length; i++) {
+      assertTrue(doubles[i] == i);
+    }
+
+    // FloatArrayCacheKey
+    FloatArrayCacheKey floatArrayCacheKey = new FloatArrayCacheKey("f");
+    assertEquals(true, floatArrayCacheKey.isMergable());
+    assertEquals(false, floatArrayCacheKey.usesObjectArray());
+
+    data1 = floatArrayCacheKey.buildData(reader1);
+    data2 = floatArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = floatArrayCacheKey.mergeData(starts, data);
+
+    float[] floats = (float[]) mergedData.getPayload();
+
+    for (int i = 0; i < floats.length; i++) {
+      assertTrue(floats[i] == i);
+    }
+
+    // StringArrayCacheKey
+    StringArrayCacheKey StringArrayCacheKey = new StringArrayCacheKey("f");
+    assertEquals(true, StringArrayCacheKey.isMergable());
+    assertEquals(false, StringArrayCacheKey.usesObjectArray());
+
+    data1 = StringArrayCacheKey.buildData(reader1);
+    data2 = StringArrayCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    mergedData = StringArrayCacheKey.mergeData(starts, data);
+
+    String[] strings = (String[]) mergedData.getPayload();
+
+    for (int i = 0; i < strings.length; i++) {
+      assertEquals(strings[i], Integer.toString(i));
+    }
+    
+    reader1.close();
+    reader2.close();
+    dir1.close();
+    dir2.close();
+
+  }
+
+  /**
+   * Tests ObjectArray for each of the ObjectArray CachKeys.
+   * 
+   * @throws Exception
+   */
+  public void testObjectArrayCacheKeys() throws Exception {
+    Directory dir1 = getRamDir();
+    Directory dir2 = getRamDir();
+    IndexReader reader1 = IndexReader.open(dir1);
+    IndexReader reader2 = IndexReader.open(dir2);
+
+    final int[] starts = new int[] { 0, 10, 20 };
+
+    // IntObjectArrayCacheKey
+    IntObjectArrayCacheKey intArrayObjectCacheKey = new IntObjectArrayCacheKey("f");
+    assertEquals(false, intArrayObjectCacheKey.isMergable());
+    assertEquals(true, intArrayObjectCacheKey.usesObjectArray());
+
+    CacheData data1 = intArrayObjectCacheKey.buildData(reader1);
+    CacheData data2 = intArrayObjectCacheKey.buildData(reader2);
+
+    CacheData[] data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    ObjectArray multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Integer) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // ByteObjectArrayCacheKey
+    ByteObjectArrayCacheKey byteArrayObjectCacheKey = new ByteObjectArrayCacheKey("f");
+    assertEquals(false, byteArrayObjectCacheKey.isMergable());
+    assertEquals(true, byteArrayObjectCacheKey.usesObjectArray());
+
+    data1 = byteArrayObjectCacheKey.buildData(reader1);
+    data2 = byteArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (byte i = 0; i < 20; i++) {
+      assertEquals(((Byte) multiFieldValues.get(i)).byteValue(), i);
+    }
+
+    
+    // DoubleObjectArrayCacheKey
+    DoubleObjectArrayCacheKey doubleArrayObjectCacheKey = new DoubleObjectArrayCacheKey("f");
+    assertEquals(false, doubleArrayObjectCacheKey.isMergable());
+    assertEquals(true, doubleArrayObjectCacheKey.usesObjectArray());
+
+    data1 = doubleArrayObjectCacheKey.buildData(reader1);
+    data2 = doubleArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Double) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // FloatObjectArrayCacheKey
+    FloatObjectArrayCacheKey floatArrayObjectCacheKey = new FloatObjectArrayCacheKey("f");
+    assertEquals(false, floatArrayObjectCacheKey.isMergable());
+    assertEquals(true, floatArrayObjectCacheKey.usesObjectArray());
+
+    data1 = floatArrayObjectCacheKey.buildData(reader1);
+    data2 = floatArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Float) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // LongObjectArrayCacheKey
+    LongObjectArrayCacheKey longArrayObjectCacheKey = new LongObjectArrayCacheKey("f");
+    assertEquals(false, longArrayObjectCacheKey.isMergable());
+    assertEquals(true, longArrayObjectCacheKey.usesObjectArray());
+
+    data1 = longArrayObjectCacheKey.buildData(reader1);
+    data2 = longArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Long) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // ShortObjectArrayCacheKey
+    ShortObjectArrayCacheKey shortArrayObjectCacheKey = new ShortObjectArrayCacheKey("f");
+    assertEquals(false, shortArrayObjectCacheKey.isMergable());
+    assertEquals(true, shortArrayObjectCacheKey.usesObjectArray());
+
+    data1 = shortArrayObjectCacheKey.buildData(reader1);
+    data2 = shortArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+
+    for (int i = 0; i < 20; i++) {
+      assertEquals(((Short) multiFieldValues.get(i)).intValue(), i);
+    }
+    
+    // StringObjectArrayCacheKey
+    StringObjectArrayCacheKey StringArrayObjectCacheKey = new StringObjectArrayCacheKey("f");
+    assertEquals(false, StringArrayObjectCacheKey.isMergable());
+    assertEquals(true, StringArrayObjectCacheKey.usesObjectArray());
+
+    data1 = StringArrayObjectCacheKey.buildData(reader1);
+    data2 = StringArrayObjectCacheKey.buildData(reader2);
+
+    data = new CacheData[2];
+    data[0] = data1;
+    data[1] = data2;
+
+    multiFieldValues = getMultiObjectArray(data, starts);
+    
+    for (int i = 0; i < 20; i++) {
+      assertTrue(((String) multiFieldValues.get2(i)).equals(Integer.toString(i)));
+    }
+    
+    reader1.close();
+    reader2.close();
+    dir1.close();
+    dir2.close();
+    
+  }
+
+  private ObjectArray getMultiObjectArray(final CacheData[] data, final int[] starts) {
+    ObjectArray multiFieldValues = new ObjectArray() {
+
+      public Object get(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get(doc - starts[i]); // dispatch to segment reader
+      }
+      
+      public Object get2(int doc) {
+        int i = readerIndex(doc); // find segment num
+        return ((ObjectArray) data[i].getPayload()).get2(doc - starts[i]); // dispatch to segment reader
+      }
+
+      private int readerIndex(int doc) {
+        if (doc > 9) {
+          return 1;
+        }
+        return 0;
+      }
+
+    };
+
+    return multiFieldValues;
+  }
+
+}
Index: src/test/org/apache/lucene/search/SampleComparable.java
===================================================================
--- src/test/org/apache/lucene/search/SampleComparable.java	(revision 642612)
+++ src/test/org/apache/lucene/search/SampleComparable.java	(working copy)
@@ -65,6 +65,7 @@
     return i;
   }
 
+  // TODO: update to ObjectArray
   public static SortComparatorSource getComparatorSource () {
     return new SortComparatorSource () {
       public ScoreDocComparator newComparator (final IndexReader reader, String fieldname)
