Index: src/java/org/apache/lucene/search/TopFieldCollector.java
===================================================================
--- src/java/org/apache/lucene/search/TopFieldCollector.java	(revision 0)
+++ src/java/org/apache/lucene/search/TopFieldCollector.java	(revision 0)
@@ -0,0 +1,204 @@
+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.search.FieldValueHitQueue.Entry;
+
+/**
+ * A {@link HitCollector} that sorts by {@link SortField} using 
+ * {@link FieldComparator}s.
+ *
+ * <b>NOTE:</b> This API is experimental and might change in
+ * incompatible ways in the next release.
+ */
+public class TopFieldCollector extends MultiReaderHitCollector {
+
+  private final FieldValueHitQueue queue;
+
+  private final FieldComparator[] comparators;
+  private FieldComparator comparator1;
+  private final int numComparators;
+  private int[] reverseMul;
+  private int reverseMul1 = 0;
+
+  private int subReaderIndex = -1;
+
+  private final int numHits;
+  private int totalHits;
+  private FieldValueHitQueue.Entry weakest = null;
+
+  /** Stores the maximum score value encountered, needed for normalizing. */
+  private float maxScore = Float.NEGATIVE_INFINITY;
+
+  private boolean queueFull;
+
+  private boolean fillFields;
+
+  private ComparatorPolicy[] comparatorPolicies;
+  
+
+  public TopFieldCollector(Sort sort, int numHits,  IndexReader[] subReaders, boolean fillFields)
+      throws IOException {
+    queue = new FieldValueHitQueue(sort.fields, numHits, subReaders);
+    comparators = queue.getComparators(); 
+    comparatorPolicies = queue.getComparatorPolicies();
+    reverseMul = queue.getReverseMul(); 
+    numComparators = comparatorPolicies.length;
+
+      
+    this.numHits = numHits;
+    
+    this.fillFields = fillFields;
+
+  }
+
+  static boolean first = true;
+
+  // javadoc inherited
+  public void setNextReader(IndexReader reader, int docBase) throws IOException {
+    final int numSlotsFull;
+    if (queueFull)
+      numSlotsFull = numHits;
+    else
+      numSlotsFull = totalHits;
+
+    subReaderIndex++;
+    for (int i = 0; i < numComparators; i++) {
+      FieldComparator comparator = comparatorPolicies[i].nextComparator(comparators[i], reader, numHits, numSlotsFull);
+      comparator.setNextReader(reader, docBase, numSlotsFull);
+      // nocommit
+      if (first) {
+        System.out.println("COMP=" + comparator);
+        first = false;
+      }
+      comparators[i] = comparator;
+    }
+    if (numComparators == 1) {
+      comparator1 = comparators[0];
+      reverseMul1 = reverseMul[0];
+    } else {
+      comparator1 = null;
+      reverseMul1 = 0;
+    }
+    queue.setComparator1(comparator1);
+    queue.setReverseMul1(reverseMul1);
+  }
+
+  // javadoc inherited
+  public void collect(int doc, float score) {
+    if (score > 0.0f) {
+      
+      maxScore = Math.max(maxScore, score);
+      totalHits++;
+
+      if (queueFull) {
+        // Fastmatch: return if this hit is not competitive
+        if (numComparators == 1) {
+          // Common case
+          if (reverseMul1*comparator1.compareBottom(doc, score) <= 0) {
+            // Definitely not competitive
+            return;
+          }
+
+          // This hit is competitive -- replace weakest
+          // element in queue & adjustTop
+          comparator1.copy(weakest.slot, doc, score);
+        } else {
+          for(int i=0;;i++) {
+            final int c = reverseMul[i] * comparators[i].compare(weakest.slot, doc, score);
+            if (c < 0) {
+              // Definitely not competitive
+              return;
+            } else if (c > 0) {
+              // Definitely competitive
+              break;
+            } else if (i == numComparators-1) {
+              // This is the equals case.  We don't need to
+              // fallback to docID comparison because lower
+              // docIDs always win, and this docID must be
+              // greater than all docIDs in the queue now,
+              // so this hit is not competitive.
+              return;
+            }
+          }
+
+          // This hit is competitive -- replace weakest
+          // element in queue & adjustTop
+          for (int i = 0; i < numComparators; i++) {
+            comparators[i].copy(weakest.slot, doc, score);
+          }
+        }
+
+        weakest.subReaderIndex = subReaderIndex;
+        weakest.subDocID = doc;
+        weakest.score = score;
+        queue.adjustTop();
+        weakest = (FieldValueHitQueue.Entry) queue.top();
+
+        comparators[0].setBottom(weakest.slot);
+      } else {
+        // Startup transient: queue hasn't gathered numHits
+        // yet
+
+        final int slot = totalHits-1;
+
+        // Copy hit into queue
+        if (numComparators == 1) {
+          // Common case
+          comparator1.copy(slot, doc, score);
+        } else {
+          for (int i = 0; i < numComparators; i++) {
+            comparators[i].copy(slot, doc, score);
+          }
+        }
+
+        queue.put(new FieldValueHitQueue.Entry(slot,
+                                               subReaderIndex, doc, score));
+
+        weakest = (FieldValueHitQueue.Entry) queue.top();
+        queueFull = totalHits == numHits;
+
+        if (queueFull) {
+          comparators[0].setBottom(weakest.slot);
+        }
+      }
+    }
+  }
+
+  // javadoc inherited
+  public TopDocs topDocs(int[] starts) {
+    ScoreDoc[] scoreDocs = new ScoreDoc[queue.size()];
+    if (fillFields) {
+      for (int i = queue.size() - 1; i >= 0; i--) {
+        scoreDocs[i] = queue.fillFields((FieldValueHitQueue.Entry) queue.pop(),
+            starts);
+      }
+    } else {
+      Entry entry = (FieldValueHitQueue.Entry) queue.pop();
+      for (int i = queue.size() - 1; i >= 0; i--) {
+        scoreDocs[i] = new FieldDoc(entry.subDocID + starts[entry.subReaderIndex],
+            entry.score);
+      }
+    }
+
+    return new TopFieldDocs(totalHits, scoreDocs, queue.getFields(), maxScore);
+  }
+}

Property changes on: src/java/org/apache/lucene/search/TopFieldCollector.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/java/org/apache/lucene/search/FieldComparator.java
===================================================================
--- src/java/org/apache/lucene/search/FieldComparator.java	(revision 0)
+++ src/java/org/apache/lucene/search/FieldComparator.java	(revision 0)
@@ -0,0 +1,1256 @@
+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 java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.ExtendedFieldCache.DoubleParser;
+import org.apache.lucene.search.ExtendedFieldCache.LongParser;
+import org.apache.lucene.search.FieldCache.ByteParser;
+import org.apache.lucene.search.FieldCache.FloatParser;
+import org.apache.lucene.search.FieldCache.IntParser;
+import org.apache.lucene.search.FieldCache.ShortParser;
+import org.apache.lucene.search.FieldCache.StringIndex;
+
+/**
+ * A FieldComparator compares hits across multiple IndexReaders.
+ * 
+ * A comparator can compare a hit at hit 'slot a' with hit 'slot b',
+ * compare a hit on 'doc i' with hit 'slot a', or copy a hit at 'doc i'
+ * to 'slot a'. Each slot refers to a hit while each doc refers to the
+ * current IndexReader.
+ *
+ * <b>NOTE:</b> This API is experimental and might change in
+ * incompatible ways in the next release.
+ */
+public abstract class FieldComparator {
+
+  public static final class ByteComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final byte[] values;
+    private byte[] currentReaderValues;
+    private final String field;
+    private ByteParser parser;
+
+    ByteComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new byte[numHits];
+      this.field = field;
+      this.parser = (ByteParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      return values[slot1] - values[slot2];
+    }
+
+    public int compare(int slot, int doc, float score) {
+      return (values[slot] - currentReaderValues[doc]);
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
+          .getBytes(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
+          .getBytes(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.BYTE;
+    }
+
+    public Comparable value(int slot) {
+      return new Byte(values[slot]);
+    }
+  };
+
+  public static final class DocComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final int[] docIDs;
+    private int docBase;
+
+    DocComparator(int numHits) {
+      docIDs = new int[numHits];
+    }
+
+    public int compare(int slot1, int slot2) {
+      return docIDs[slot1] - docIDs[slot2];
+    }
+
+    public int compare(int slot, int doc, float score) {
+      return docIDs[slot] - docBase - doc;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      docIDs[slot] = docBase + doc;
+    }
+
+    public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) {
+      // TODO: can we "map" our docIDs to the current
+      // reader? saves having to then subtract on every
+      // compare call
+      this.docBase = docBase;
+    }
+
+    public int sortType() {
+      return SortField.DOC;
+    }
+
+    public Comparable value(int slot) {
+      return new Integer(docIDs[slot]);
+    }
+  };
+
+  public static final class DoubleComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final double[] values;
+    private double[] currentReaderValues;
+    private final String field;
+    private DoubleParser parser;
+
+    DoubleComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new double[numHits];
+      this.field = field;
+      this.parser = (DoubleParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (values[slot1] > values[slot2])
+        return 1;
+      if (values[slot1] < values[slot2])
+        return -1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      if (values[slot] > currentReaderValues[doc])
+        return 1;
+      if (values[slot] < currentReaderValues[doc])
+        return -1;
+      return 0;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
+          .getDoubles(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
+          .getDoubles(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.DOUBLE;
+    }
+
+    public Comparable value(int slot) {
+      return new Double(values[slot]);
+    }
+  };
+
+  public static final class FloatComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final float[] values;
+    private float[] currentReaderValues;
+    private final String field;
+    private FloatParser parser;
+
+    FloatComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new float[numHits];
+      this.field = field;
+      this.parser = (FloatParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (values[slot1] > values[slot2])
+        return 1;
+      if (values[slot1] < values[slot2])
+        return -1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      float val = currentReaderValues[doc];
+      if (values[slot] > val)
+        return 1;
+      if (values[slot] < val)
+        return -1;
+      return 0;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? FieldCache.DEFAULT.getFloats(
+          reader, field, parser) : FieldCache.DEFAULT.getFloats(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.FLOAT;
+    }
+
+    public Comparable value(int slot) {
+      return new Float(values[slot]);
+    }
+  };
+
+  public static final class IntComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final int[] values;
+    private int[] currentReaderValues;
+    private final String field;
+    private IntParser parser;
+
+    IntComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new int[numHits];
+      this.field = field;
+      this.parser = (IntParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (values[slot1] > values[slot2])
+        return 1;
+      if (values[slot1] < values[slot2])
+        return -1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      return values[slot] - currentReaderValues[doc];
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? FieldCache.DEFAULT.getInts(reader,
+          field, parser) : FieldCache.DEFAULT.getInts(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.INT;
+    }
+
+    public Comparable value(int slot) {
+      return new Integer(values[slot]);
+    }
+  };
+
+  public static final class LongComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final long[] values;
+    private long[] currentReaderValues;
+    private final String field;
+    private LongParser parser;
+
+    LongComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new long[numHits];
+      this.field = field;
+      this.parser = (LongParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (values[slot1] > values[slot2])
+        return 1;
+      if (values[slot1] < values[slot2])
+        return -1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      if (values[slot] > currentReaderValues[doc])
+        return 1;
+      if (values[slot] < currentReaderValues[doc])
+        return -1;
+      return 0;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
+          .getLongs(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
+          .getLongs(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.LONG;
+    }
+
+    public Comparable value(int slot) {
+      return new Long(values[slot]);
+    }
+  };
+
+  public static final class RelevanceComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final float[] scores;
+
+    RelevanceComparator(int numHits) {
+      scores = new float[numHits];
+    }
+
+    public int compare(int slot1, int slot2) {
+      float score1 = scores[slot1];
+      float score2 = scores[slot2];
+      if (score1 > score2)
+        return -1;
+      if (score1 < score2)
+        return 1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      float slotScore = scores[slot];
+      if (slotScore > score)
+        return -1;
+      if (slotScore < score)
+        return 1;
+      return 0;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      scores[slot] = score;
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) {
+    }
+
+    public int sortType() {
+      return SortField.SCORE;
+    }
+
+    public Comparable value(int slot) {
+      return new Float(scores[slot]);
+    }
+  };
+
+  public static final class ShortComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+
+    private final short[] values;
+    private short[] currentReaderValues;
+    private final String field;
+    private ShortParser parser;
+
+    ShortComparator(int numHits, String field, FieldCache.Parser parser) {
+      values = new short[numHits];
+      this.field = field;
+      this.parser = (ShortParser) parser;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (values[slot1] > values[slot2])
+        return 1;
+      if (values[slot1] < values[slot2])
+        return -1;
+      return 0;
+    }
+
+    public int compare(int slot, int doc, float score) {
+      if (values[slot] > currentReaderValues[doc])
+        return 1;
+      if (values[slot] < currentReaderValues[doc])
+        return -1;
+      return 0;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
+          .getShorts(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
+          .getShorts(reader, field);
+    }
+
+    public int sortType() {
+      return SortField.BYTE;
+    }
+
+    public Comparable value(int slot) {
+      return new Short(values[slot]);
+    }
+  };
+
+  public static final class StringComparatorLocale extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private final String[] values;
+    private String[] currentReaderValues;
+    private final String field;
+    final Collator collator;
+
+    StringComparatorLocale(int numHits, String field, Locale locale) {
+      values = new String[numHits];
+      this.field = field;
+      collator = Collator.getInstance(locale);
+    }
+
+    public int compare(int slot1, int slot2) {
+      final String val1 = values[slot1];
+      final String val2 = values[slot2];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return collator.compare(val1, val2);
+    }
+
+    public int compare(int slot, int doc, float score) {
+      return values[slot].compareTo(currentReaderValues[doc]);
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader,
+          field);
+    }
+
+    public int sortType() {
+      return SortField.STRING;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+  };
+  
+  /**
+   * This Comparator can only be used a single IndexReader or the first IndexReader in a series
+   * of IndexReaders.
+   */
+  public static final class StringOrdComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private int[] subords;
+    final int[] ords;
+    String[] values;
+    String[] lookup;
+    private int[] order;
+    final String field;
+
+    StringOrdComparator(String field, String[] values, int[] ords, int[] subords) {
+      this.field = field;
+      this.values = values;
+      this.ords = ords;
+      this.subords = subords;
+    }
+    
+    StringOrdComparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      subords = new int[numHits];
+      this.field = field;
+    }
+
+    public int compare(int slot1, int slot2) {
+      return ords[slot1] - ords[slot2];
+    }
+
+    public int compare(int slot, int doc, float score) {
+      int ord = order[doc];
+      return ords[slot] - ord;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      subords[slot] = 0;
+      values[slot] = lookup[ord];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+
+      lookup = currentReaderValues.lookup;
+      order = currentReaderValues.order;
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+  }
+
+  /**
+   * This Comparator can only be used a single IndexReader or the first IndexReader in a series
+   * of IndexReaders.
+   */
+  public static final class StringOrd2Comparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private int[] subords;
+    final int[] ords;
+    String[] values;
+    String[] lookup;
+    private int[] order;
+    final String field;
+
+    StringOrd2Comparator(String field, String[] values, int[] ords, int[] subords) {
+      this.field = field;
+      this.values = values;
+      this.ords = ords;
+      this.subords = subords;
+    }
+    
+    StringOrd2Comparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      subords = new int[numHits];
+      this.field = field;
+    }
+
+    private int bottomOrd;
+
+    public void setBottom(int slot) {
+      bottomOrd = ords[slot];
+    }
+
+    public int compareBottom(int doc, float score) {
+      return bottomOrd - order[doc];
+    }
+
+    public int compare(int slot1, int slot2) {
+      return ords[slot1] - ords[slot2];
+    }
+
+    public int compare(int slot, int doc, float score) {
+      int ord = order[doc];
+      return ords[slot] - ord;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      subords[slot] = 0;
+      values[slot] = lookup[ord];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+
+      lookup = currentReaderValues.lookup;
+      order = currentReaderValues.order;
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+  }
+
+  public static final class StringOrdSubOrdComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private int[] subords;
+    final int[] ords;
+    String[] values;
+    String[] lookup;
+    private int[] order;
+    final String field;
+
+    StringOrdSubOrdComparator(String field, String[] values, int[] ords, int[] subords) {
+      this.field = field;
+      this.values = values;
+      this.ords = ords;
+      this.subords = subords;
+    }
+    
+    StringOrdSubOrdComparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      subords = new int[numHits];
+      this.field = field;
+    }
+
+    public int compare(int slot1, int slot2) {
+      int cmp = ords[slot1] - ords[slot2];
+      if (cmp != 0) {
+        return cmp;
+      }
+      return subords[slot1] - subords[slot2];
+    }
+
+    public int compare(int slot, int doc, float score) {
+      int ord = order[doc];
+      final int cmp = ords[slot] - ord;
+      if (cmp != 0) { return cmp;}
+      return 1;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      subords[slot] = 0;
+      values[slot] = lookup[ord];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      // Map ords in queue to ords in reader
+
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+
+      lookup = currentReaderValues.lookup;
+      order = currentReaderValues.order;
+
+      if (lookup.length == 0) {
+        return;
+      }
+      Map map = new HashMap();
+      for (int i = 0; i < numSlotsFull; i++) {
+        String val = values[i];
+        if (val == null) {
+          subords[i] = 0;
+          ords[i] = 0;
+          continue;
+        }
+
+        int index = binarySearch(lookup, val);
+
+        if (index < 0) {
+          index = -index - 2;
+
+          Integer intOrd = Integer.valueOf(index);
+          List slotVals = (List) map.get(intOrd);
+          if (slotVals == null) {
+            slotVals = new ArrayList();
+            slotVals.add(new SlotValPair(i, val));
+            map.put(intOrd, slotVals);
+          } else {
+            slotVals.add(new SlotValPair(i, val));
+          }
+
+        } else {
+          subords[i] = 0;
+        }
+        ords[i] = index;
+      }
+
+      Iterator dit = map.values().iterator();
+      while (dit.hasNext()) {
+        List list = (List) dit.next();
+        if (list.size() > 1) {
+          Collections.sort(list);
+          int sz = list.size();
+          int cnt = 1;
+          SlotValPair svp = (SlotValPair) list.get(0);
+          String last = svp.val;
+          for (int i = 1; i < sz; i++) {
+            if(last.equals(svp.val) ) {
+              subords[svp.i] = cnt;
+            } else {
+              subords[svp.i] = ++cnt;
+            }
+            last = svp.val;
+            svp = (SlotValPair) list.get(i);
+          }
+          if(last.equals(svp.val) ) {
+            subords[svp.i] = cnt;
+          } else {
+            subords[svp.i] = ++cnt;
+          }
+        } else {
+          SlotValPair svp = (SlotValPair) list.get(0);
+          subords[svp.i] = 1;
+        }
+      }
+      
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+
+    class SlotValPair implements Comparable {
+      public SlotValPair(int i, String val) {
+        this.val = val;
+        this.i = i;
+      }
+
+      String val;
+      int i;
+
+      public int compareTo(Object o) {
+        SlotValPair odoub = (SlotValPair) o;
+        return val.compareTo(odoub.val);
+      }
+
+      public String toString() {
+        return "i:" + i + " val:" + val;
+      }
+    }
+  }
+
+  public static final class StringOrdValOnDemComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private final int[] ords;
+    String[] values;
+    private final int[] readerIndex;
+    private int currentReaderIndex = -1;
+    private String[] lookup;
+    private int length;
+    private int[] order;
+    private final String field;
+    
+    public StringOrdValOnDemComparator(String field, String[] values, int[] ords, int currentReaderIndex) {
+      this.field = field;
+      this.values = values;
+      this.ords = ords;
+      readerIndex = new int[values.length];
+    }
+
+    StringOrdValOnDemComparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      readerIndex = new int[numHits];
+      this.field = field;
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (readerIndex[slot1] != currentReaderIndex) {
+        convert(slot1);
+      }
+      if (readerIndex[slot2] != currentReaderIndex) {
+        convert(slot2);
+      }
+      int cmp = ords[slot1] - ords[slot2];
+      if (cmp != 0) {
+        return cmp;
+      }
+      final String val1 = values[slot1];
+      final String val2 = values[slot2];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+
+      return val1.compareTo(val2);
+    }
+
+    public int compare(int slot, int doc, float score) {
+      if (readerIndex[slot] != currentReaderIndex) {
+        convert(slot);
+      }
+      int ord = ords[slot];
+      int order = this.order[doc];
+      final int cmp = ord - order;
+      if (cmp != 0) {
+        return cmp;
+      }
+
+      final String val1 = values[slot];
+      final String val2 = lookup[order];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return val1.compareTo(val2);
+    }
+
+    private void convert(int slot) {
+      readerIndex[slot] = currentReaderIndex;
+      if (length == 0) {
+        return;
+      }
+
+      int index = 0;
+      String value = values[slot];
+      if (value == null) {
+        ords[slot] = 0;
+        return;
+      }
+      index = binarySearch(lookup, value);
+      if (index < 0) {
+        index = -index - 2;
+      }
+      ords[slot] = index;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      values[slot] = lookup[ord];
+      readerIndex[slot] = currentReaderIndex;
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+      order = currentReaderValues.order;
+      lookup = currentReaderValues.lookup;
+      currentReaderIndex++;
+      length = lookup.length;
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD_VAL;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+
+  };
+
+  public static final class StringOrdValOnDem2Comparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private final int[] ords;
+    String[] values;
+    private final int[] readerIndex;
+    private int currentReaderIndex = -1;
+    private String[] lookup;
+    private int length;
+    private int[] order;
+    private final String field;
+
+    private int bottomOrd;
+    private String bottomVal;
+    private int bottomSlot;
+    
+    public StringOrdValOnDem2Comparator(String field, String[] values, int[] ords, int currentReaderIndex) {
+      this.field = field;
+      this.values = values;
+      this.ords = ords;
+      readerIndex = new int[values.length];
+    }
+
+    StringOrdValOnDem2Comparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      readerIndex = new int[numHits];
+      this.field = field;
+    }
+
+    public void setBottom(int slot) {
+      assert readerIndex[slot] == currentReaderIndex;
+      bottomOrd = ords[slot];
+      bottomVal = values[slot];
+      bottomSlot = slot;
+    }
+
+    public int compareBottom(int doc, float score) {
+      final int order = this.order[doc];      
+      int cmp = bottomOrd - order;
+      if (cmp != 0) {
+        return cmp;
+      }
+
+      final String val1 = bottomVal;
+      final String val2 = lookup[order];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return val1.compareTo(val2);
+    }
+
+    public int compare(int slot1, int slot2) {
+      if (readerIndex[slot1] != currentReaderIndex) {
+        convert(slot1);
+      }
+      if (readerIndex[slot2] != currentReaderIndex) {
+        convert(slot2);
+      }
+      int cmp = ords[slot1] - ords[slot2];
+      if (cmp != 0) {
+        return cmp;
+      }
+      final String val1 = values[slot1];
+      final String val2 = values[slot2];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+
+      return val1.compareTo(val2);
+    }
+
+    public int compare(int slot, int doc, float score) {
+      if (readerIndex[slot] != currentReaderIndex) {
+        convert(slot);
+      }
+      int ord = ords[slot];
+      int order = this.order[doc];
+      final int cmp = ord - order;
+      if (cmp != 0) {
+        return cmp;
+      }
+
+      final String val1 = values[slot];
+      final String val2 = lookup[order];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return val1.compareTo(val2);
+    }
+
+    private void convert(int slot) {
+      readerIndex[slot] = currentReaderIndex;
+      if (length == 0) {
+        return;
+      }
+
+      int index = 0;
+      String value = values[slot];
+      if (value == null) {
+        ords[slot] = 0;
+        return;
+      }
+      index = binarySearch(lookup, value);
+      if (index < 0) {
+        index = -index - 2;
+      }
+      ords[slot] = index;
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      values[slot] = lookup[ord];
+      readerIndex[slot] = currentReaderIndex;
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+      order = currentReaderValues.order;
+      lookup = currentReaderValues.lookup;
+      currentReaderIndex++;
+      length = lookup.length;
+      bottomOrd = ords[bottomSlot];
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD_VAL;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+
+  };
+
+  public static final class StringOrdValComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private final int[] ords;
+    private final String[] values;
+    private String[] lookup;
+    private int[] order;
+    private final String field;
+
+    StringOrdValComparator(int numHits, String field) {
+      ords = new int[numHits];
+      values = new String[numHits];
+      this.field = field;
+    }
+
+    public int compare(int slot1, int slot2) {
+      int cmp = ords[slot1] - ords[slot2];
+      if (cmp != 0) { return cmp;}
+      final String val1 = values[slot1];
+      final String val2 = values[slot2];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return val1.compareTo(val2);
+    }
+
+    public int compare(int slot, int doc, float score) {
+      final int ord = order[doc];
+      int cmp = ords[slot] - ord;
+      if (cmp != 0) { return cmp;}
+      final String val1 = values[slot];
+      final String val2 = lookup[ord];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+
+      return val1.compareTo(val2);
+    }
+
+    public void copy(int slot, int doc, float score) {
+      int ord = order[doc];
+      ords[slot] = ord;
+      values[slot] = lookup[ord];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase,  int numSlotsFull) throws IOException {
+      StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT
+          .getStringIndex(reader, field);
+
+      order = currentReaderValues.order;
+      lookup = currentReaderValues.lookup;
+
+      int index = 0;
+      for (int i = 0; i < numSlotsFull; i++) {
+        String value = values[i];
+        if (value == null) {
+          continue;
+        }
+
+        index = binarySearch(lookup, value);
+
+        if (index < 0) {
+          index = -index - 2;
+        }
+        ords[i] = index;
+      }
+    }
+
+    public int sortType() {
+      return SortField.STRING_ORD_VAL;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+
+  };
+
+  public static final class StringValComparator extends FieldComparator {
+
+    // nocommit -- maybe "setcurrentscoredoc"?
+    private String[] values;
+    private String[] currentReaderValues;
+    private final String field;
+
+    StringValComparator(int numHits, String field) {
+      values = new String[numHits];
+      this.field = field;
+    }
+
+    public int compare(int slot1, int slot2) {
+      final String val1 = values[slot1];
+      final String val2 = values[slot2];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+
+      return val1.compareTo(val2);
+    }
+
+    public int compare(int slot, int doc, float score) {
+      final String val1 = values[slot];
+      final String val2 = currentReaderValues[doc];
+      if (val1 == null) {
+        if (val2 == null) {
+          return 0;
+        }
+        return -1;
+      } else if (val2 == null) {
+        return 1;
+      }
+      return val1.compareTo(val2);
+    }
+
+    public void copy(int slot, int doc, float score) {
+      values[slot] = currentReaderValues[doc];
+    }
+
+    public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
+      currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader,
+          field);
+    }
+
+    public int sortType() {
+      return SortField.STRING_VAL;
+    }
+
+    public Comparable value(int slot) {
+      return values[slot];
+    }
+  };
+
+  private static int binarySearch(String[] a, String key) {
+    int low = 0;
+    int high = a.length - 1;
+
+    while (low <= high) {
+      int mid = (low + high) >>> 1;
+      String midVal = a[mid];
+      int cmp;
+      if (midVal != null) {
+        cmp = midVal.compareTo(key);
+      } else {
+        cmp = -1;
+      }
+
+      if (cmp < 0)
+        low = mid + 1;
+      else if (cmp > 0)
+        high = mid - 1;
+      else
+        return mid;
+    }
+    return -(low + 1);
+  };
+
+  /**
+   * Compare hit at slot1 with hit at slot2.
+   * 
+   * @param slot1
+   * @param slot2
+   * @return
+   */
+  public abstract int compare(int slot1, int slot2);
+
+  // nocommit -- allowed to return -X or only -1 (and X, 1)???
+  /**
+   * Compare hit at slot with hit (doc,score).
+   * 
+   * @param slot  hit slot
+   * @param doc doc in currentIndexReader
+   * @param score hit score
+   * @return
+   */
+  public abstract int compare(int slot, int doc, float score);
+
+  /**
+   * Copy hit (doc,score) to hit slot.
+   * 
+   * @param slot
+   * @param doc
+   * @param score
+   */
+  public abstract void copy(int slot, int doc, float score);
+
+  /**
+   * Set a new Reader. All doc correspond to the current Reader.
+   * 
+   * @param reader
+   * @param objects 
+   * @throws IOException
+   * @throws IOException
+   */
+  public abstract void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException;
+
+  /**
+   * @return SortField.TYPE
+   */
+  public abstract int sortType();
+
+  /**
+   * Return the actual value at slot.
+   * 
+   * @param slot the value
+   * @return
+   */
+  public abstract Comparable value(int slot);
+
+  private int bottomSlot;
+
+  public void setBottom(int slot) {
+    bottomSlot = slot;
+  }
+
+  public int compareBottom(int doc, float score) {
+    return compare(bottomSlot, doc, score);
+  }
+}

Property changes on: src/java/org/apache/lucene/search/FieldComparator.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/java/org/apache/lucene/search/SortField.java
===================================================================
--- src/java/org/apache/lucene/search/SortField.java	(revision 732086)
+++ src/java/org/apache/lucene/search/SortField.java	(working copy)
@@ -17,9 +17,14 @@
  * limitations under the License.
  */
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.Locale;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldComparator.StringOrdValOnDemComparator;
+import org.apache.lucene.search.FieldComparator.StringOrdValOnDem2Comparator;
+
 /**
  * Stores information about how to sort documents by terms in an individual
  * field.  Fields must be indexed in order to sort by them.
@@ -80,8 +85,23 @@
    * Sort using term values as encoded bytes.  Sort values are bytes and lower values are at the front
    */
   public static final int BYTE = 10;
+  
+  public static final int STRING_ORD = 11;
+  
+  public static final int STRING_VAL = 12;
+  
+  public static final int STRING_ORD_VAL = 13;
+  
+  public static final int STRING_ORD_VAL_DEM = 14;
+  
+  public static final int STRING_ORD_VAL_DEM_WIN = 15;
 
+  public static final int STRING_ORD_SUBORD = 16;
 
+  public static final int STRING_ORD_VAL_DEM2 = 17;
+
+  public static final int STRING_ORD2 = 18;
+
   // IMPLEMENTATION NOTE: the FieldCache.STRING_INDEX is in the same "namespace"
   // as the above static int values.  Any new values must not have the same value
   // as FieldCache.STRING_INDEX.
@@ -101,6 +121,10 @@
   private SortComparatorSource factory;
   private FieldCache.Parser parser;
 
+  private FieldComparatorSource comparatorSource;
+
+  private boolean useLegacy = false; // remove in Lucene 3.0
+
   /** Creates a sort by terms in the given field where the type of term value
    * is determined dynamically ({@link #AUTO AUTO}).
    * @param field Name of field to sort by, cannot be <code>null</code>.
@@ -206,22 +230,44 @@
   /** Creates a sort with a custom comparison function.
    * @param field Name of field to sort by; cannot be <code>null</code>.
    * @param comparator Returns a comparator for sorting hits.
+   * @deprecated use SortField (String field, FieldComparatorSource comparator)
    */
   public SortField (String field, SortComparatorSource comparator) {
     initFieldType(field, CUSTOM);
     this.factory = comparator;
   }
+  
+  /** Creates a sort with a custom comparison function.
+   * @param field Name of field to sort by; cannot be <code>null</code>.
+   * @param comparator Returns a comparator for sorting hits.
+   */
+  public SortField (String field, FieldComparatorSource comparator) {
+    initFieldType(field, CUSTOM);
+    this.comparatorSource = comparator;
+  }
 
   /** Creates a sort, possibly in reverse, with a custom comparison function.
    * @param field Name of field to sort by; cannot be <code>null</code>.
    * @param comparator Returns a comparator for sorting hits.
    * @param reverse True if natural order should be reversed.
+   * @deprecated use SortField (String field, FieldComparatorSource comparator, boolean reverse)
    */
   public SortField (String field, SortComparatorSource comparator, boolean reverse) {
     initFieldType(field, CUSTOM);
     this.reverse = reverse;
     this.factory = comparator;
   }
+  
+  /** Creates a sort, possibly in reverse, with a custom comparison function.
+   * @param field Name of field to sort by; cannot be <code>null</code>.
+   * @param comparator Returns a comparator for sorting hits.
+   * @param reverse True if natural order should be reversed.
+   */
+  public SortField (String field, FieldComparatorSource comparator, boolean reverse) {
+    initFieldType(field, CUSTOM);
+    this.reverse = reverse;
+    this.comparatorSource = comparator;
+  }
 
   // Sets field & type, and ensures field is not NULL unless
   // type is SCORE or DOC
@@ -273,9 +319,36 @@
     return reverse;
   }
 
+  /**
+   * @deprecated use {@link #getComparatorSource()}
+   */
   public SortComparatorSource getFactory() {
     return factory;
   }
+  
+  public FieldComparatorSource getComparatorSource() {
+    return comparatorSource;
+  }
+  
+  /**
+   * Use legacy IndexSearch semantics: search with a MultiSegmentReader rather
+   * than passing a single hit collector to multiple SegmentReaders.
+   * 
+   * @param legacy true for legacy behavior
+   * @deprecated will be removed in Lucene 3.0.
+   */
+  public void setUseLegacySearch(boolean legacy) {
+    this.useLegacy = legacy;
+  }
+  
+  /**
+   * @return if true, IndexSearch will use legacy sorting search semantics.
+   * eg. multiple Priority Queues.
+   * @deprecated will be removed in Lucene 3.0.
+   */
+  public boolean getUseLegacySearch() {
+    return this.useLegacy;
+  }
 
   public String toString() {
     StringBuffer buffer = new StringBuffer();
@@ -333,4 +406,200 @@
     if (parser != null) hash += parser.hashCode()^0x3aaf56ff;
     return hash;
   }
+  
+  ComparatorPolicy getComparatorPolicy(IndexReader[] subReaders, final int numHits) throws IOException {
+    switch (type) {
+    case SortField.SCORE:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.RelevanceComparator(numHits);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.DOC:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.DocComparator(numHits);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.INT:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.IntComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.FLOAT:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.FloatComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.LONG:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.LongComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.DOUBLE:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.DoubleComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.BYTE:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.ByteComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.SHORT:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.ShortComparator(numHits, field, parser);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+    case SortField.CUSTOM:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = comparatorSource.newComparator(field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+
+    case SortField.STRING:
+      if (locale != null) {
+        return new ComparatorPolicy(){
+          private FieldComparator comparator = new FieldComparator.StringComparatorLocale(numHits, field, locale);
+          public FieldComparator nextComparator(FieldComparator oldComparator,
+              IndexReader reader, int numHits, int numSlotsFull)
+              throws IOException {
+            return comparator;
+          }};
+      }
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdSubOrdComparator(numHits, field);;
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+        
+    case SortField.STRING_VAL:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringValComparator(numHits, field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+
+    case SortField.STRING_ORD:
+      if (locale != null) {
+        return new ComparatorPolicy(){
+          private FieldComparator comparator = new FieldComparator.StringComparatorLocale(numHits, field, locale);
+          public FieldComparator nextComparator(FieldComparator oldComparator,
+              IndexReader reader, int numHits, int numSlotsFull)
+              throws IOException {
+            return comparator;
+          }};
+      }
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdComparator(numHits, field);
+        private boolean first = true;
+        private boolean second = true;
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          if(first) {
+            first = false;
+            return comparator;
+          } else if(second){
+            StringOrdValOnDemComparator comp = new FieldComparator.StringOrdValOnDemComparator(numHits, field);
+            comp.values = ((FieldComparator.StringOrdComparator)comparator).values;
+            comparator = comp;
+            second = false;
+            return comp;
+          }
+          return comparator;
+        }};
+        
+    case SortField.STRING_ORD2:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrd2Comparator(numHits, field);
+        private boolean first = true;
+        private boolean second = true;
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          if(first) {
+            first = false;
+            return comparator;
+          } else if(second){
+            StringOrdValOnDem2Comparator comp = new FieldComparator.StringOrdValOnDem2Comparator(numHits, field);
+            comp.values = ((FieldComparator.StringOrd2Comparator)comparator).values;
+            comparator = comp;
+            second = false;
+            return comp;
+          }
+          return comparator;
+        }};
+        
+    case SortField.STRING_ORD_SUBORD:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdSubOrdComparator(numHits, field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+        
+    case SortField.STRING_ORD_VAL:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdValComparator(numHits, field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader, int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+        
+    case SortField.STRING_ORD_VAL_DEM:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdValOnDemComparator(numHits, field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader,  int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+
+    case SortField.STRING_ORD_VAL_DEM2:
+      return new ComparatorPolicy(){
+        private FieldComparator comparator = new FieldComparator.StringOrdValOnDem2Comparator(numHits, field);
+        public FieldComparator nextComparator(FieldComparator oldComparator,
+            IndexReader reader,  int numHits, int numSlotsFull)
+            throws IOException {
+          return comparator;
+        }};
+
+    default:
+      throw new IllegalStateException("Illegal sort type: " + type);
+    }
+  
+  }
+  
 }

Index: src/java/org/apache/lucene/search/FieldValueHitQueue.java
===================================================================
--- src/java/org/apache/lucene/search/FieldValueHitQueue.java	(revision 0)
+++ src/java/org/apache/lucene/search/FieldValueHitQueue.java	(revision 0)
@@ -0,0 +1,228 @@
+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.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.util.PriorityQueue;
+
+import java.io.IOException;;
+
+/**
+ * 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.
+ *
+ * <b>NOTE:</b> This API is experimental and might change in
+ * incompatible ways in the next release.
+ *
+ * @since   lucene 2.9
+ * @version $Id:
+ * @see Searcher#search(Query,Filter,int,Sort)
+ * @see FieldCache
+ */
+public class FieldValueHitQueue extends PriorityQueue {
+
+  final static class Entry {
+    int slot;
+    int subReaderIndex;
+    int subDocID;
+    float score;
+    Entry(int slot, int subReaderIndex, int subDocID, float score) {
+      this.slot = slot;
+      this.subReaderIndex = subReaderIndex;
+      this.subDocID = subDocID;
+      this.score = score;
+    }
+    
+    public String toString() {
+      return "slot:" + slot + " subR:" + subReaderIndex + " subid:" + subDocID;
+    }
+  }
+
+  // nocommit
+  private static boolean first = true;
+
+  /**
+   * Creates a hit queue sorted by the given list of fields.
+   * @param reader 
+   * @param fields Fieldable names, in priority order (highest priority first).  Cannot be <code>null</code> or empty.
+   * @param size  The number of hits to retain.  Must be greater than zero.
+   * @throws IOException
+   */
+  public FieldValueHitQueue(SortField[] fields, int size, IndexReader[] subReaders) throws IOException {
+    numComparators = fields.length;
+    comparators = new FieldComparator[numComparators];
+    comparatorPolicies = new ComparatorPolicy[numComparators];
+    reverseMul = new int[numComparators];
+
+    this.fields = fields;
+    for (int i=0; i<numComparators; ++i) {
+      SortField field = fields[i];
+      reverseMul[i] = field.reverse ? -1 : 1;
+      comparatorPolicies[i] = field.getComparatorPolicy(subReaders, size);
+    }
+
+    initialize(size);
+  }
+  
+  /** Stores a comparator corresponding to each field being sorted by */
+  private final FieldComparator[] comparators;
+  private final ComparatorPolicy[]  comparatorPolicies;
+  private FieldComparator comparator1 = null;
+  private final int numComparators;
+  private final int[] reverseMul;
+  private int reverseMul1 = 0;
+
+  FieldComparator[] getComparators() {
+    return comparators;
+  }
+
+  int[] getReverseMul() {
+    return reverseMul;
+  }
+
+  /** Stores the sort criteria being used. */
+  private final SortField[] fields;
+
+  /**
+   * Returns whether <code>a</code> is less relevant than <code>b</code>.
+   * @param a ScoreDoc
+   * @param b ScoreDoc
+   * @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
+   */
+  protected boolean lessThan (final Object a, final Object b) {
+    final Entry hitA = (Entry) a;
+    final Entry hitB = (Entry) b;
+
+    if (numComparators == 1) {
+      // Common case
+      final int c = reverseMul1 * comparator1.compare (hitA.slot, hitB.slot);
+      if (c != 0) {
+        return c > 0;
+      }
+    } else {
+      // run comparators
+      for (int i=0; i<numComparators; ++i) {
+        final int c = reverseMul[i] * comparators[i].compare (hitA.slot, hitB.slot);
+        if (c != 0) {
+          // Short circuit
+          return c > 0;
+        }
+      }
+    }
+
+    // avoid random sort order that could lead to duplicates (bug #31241):
+    return hitA.subReaderIndex > hitB.subReaderIndex ||
+      (hitA.subReaderIndex == hitB.subReaderIndex &&
+       hitA.subDocID > hitB.subDocID);
+  }
+
+
+  /**
+   * Given a FieldDoc object, stores the values used
+   * to sort the given document.  These values are not the raw
+   * values out of the index, but the internal representation
+   * of them.  This is so the given search hit can be collated
+   * by a MultiSearcher with other search hits.
+   * @param  doc  The FieldDoc to store sort values into.
+   * @return  The same FieldDoc passed in.
+   * @see Searchable#search(Weight,Filter,int,Sort)
+   */
+  FieldDoc fillFields (final Entry entry, int[] starts) {
+    final int n = comparators.length;
+    final Comparable[] fields = new Comparable[n];
+    for (int i=0; i<n; ++i)
+      fields[i] = comparators[i].value(entry.slot);
+    //if (maxscore > 1.0f) doc.score /= maxscore;   // normalize scores
+    return new FieldDoc(entry.subDocID + starts[entry.subReaderIndex],
+                        entry.score,
+                        fields);
+  }
+
+
+  /** Returns the SortFields being used by this hit queue. */
+  SortField[] getFields() {
+    return fields;
+  }
+  
+  /**
+   * Attempts to detect the given field type for an IndexReader.
+   */
+  static int detectFieldType(IndexReader reader, String 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");
+      }
+      int ret = 0;
+      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 = SortField.INT;
+        } catch (NumberFormatException nfe1) {
+          try {
+            Long.parseLong(termtext);
+            ret = SortField.LONG;
+          } catch (NumberFormatException nfe2) {
+            try {
+              Float.parseFloat (termtext);
+              ret = SortField.FLOAT;
+            } catch (NumberFormatException nfe3) {
+              ret = SortField.STRING;
+            }
+          }
+        }         
+      } else {
+        throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
+      }
+      return ret;
+    } finally {
+      enumerator.close();
+    }
+  }
+
+  ComparatorPolicy[] getComparatorPolicies() {
+    return comparatorPolicies;
+  }
+  
+  //void setReversMulti1(int rev) {
+  void setReverseMul1(int rev) {
+    this.reverseMul1 = rev;
+  }
+  
+  void setComparator1(FieldComparator comp) {
+    this.comparator1 = comp;
+  }
+}

Property changes on: src/java/org/apache/lucene/search/FieldValueHitQueue.java
___________________________________________________________________
Name: svn:eol-style
   + native

