content: /**
   * 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.
   */
  
  package org.apache.solr.search;
  
  import org.apache.lucene.search.*;
  import org.apache.lucene.index.IndexReader;
  
  import java.io.IOException;
  
  
  public class MissingStringLastComparatorSource extends FieldComparatorSource {
    public static final String bigString="\uffff\uffff\uffff\uffff\uffff\uffff\uffff\uffffNULL_VAL";
  
    private final String missingValueProxy;
  
    public MissingStringLastComparatorSource() {
      this(bigString);
    }
  
    /** Creates a {@link FieldComparatorSource} that uses <tt>missingValueProxy</tt> as the value to return from ScoreDocComparator.sortValue()
     * which is only used my multisearchers to determine how to collate results from their searchers.
     *
     * @param missingValueProxy   The value returned when sortValue() is called for a document missing the sort field.
     * This value is *not* normally used for sorting, but used to create
     */
    public MissingStringLastComparatorSource(String missingValueProxy) {
      this.missingValueProxy=missingValueProxy;
    }
  
    @Override
    public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
      return new MissingLastOrdComparator(numHits, fieldname, sortPos, reversed, missingValueProxy);
    }
  
  }
  
  // Copied from Lucene and modified since the Lucene version couldn't
  // be extended or have it's values accessed.
   class MissingLastOrdComparator extends FieldComparator<String> {
      private static final int NULL_ORD = Integer.MAX_VALUE;
      private final String nullVal; 
  
      private final int[] ords;
      private final String[] values;
      private final int[] readerGen;
  
      private int currentReaderGen = -1;
      private String[] lookup;
      private int[] order;
      private final String field;
  
      private int bottomSlot = -1;
      private int bottomOrd;
      private String bottomValue;
      private final boolean reversed;
      private final int sortPos;
  
     public MissingLastOrdComparator(int numHits, String field, int sortPos, boolean reversed, String nullVal) {
        ords = new int[numHits];
        values = new String[numHits];
        readerGen = new int[numHits];
        this.sortPos = sortPos;
        this.reversed = reversed;
        this.field = field;
        this.nullVal = nullVal;
      }
  
      @Override
      public int compare(int slot1, int slot2) {
        if (readerGen[slot1] == readerGen[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);
      }
  
      @Override
      public int compareValues(String first, String second) {
        if (first == null) {
          if (second == null) {
            return 0;
          } else {
            return 1;
          }
        } else if (second == null) {
          return -1;
        } else {
          return first.compareTo(second);
        }
      }
  
      @Override
      public int compareBottom(int doc) {
        assert bottomSlot != -1;
        int order = this.order[doc];
        int ord = (order == 0) ? NULL_ORD : order;
        final int cmp = bottomOrd - ord;
        if (cmp != 0) {
          return cmp;
        }
  
        final String val2 = lookup[order];
  
        // take care of the case where both vals are null
        if (bottomValue == val2) return 0;
   
        return bottomValue.compareTo(val2);
      }
  
      private void convert(int slot) {
        readerGen[slot] = currentReaderGen;
        int index = 0;
        String value = values[slot];
        if (value == null) {
          // should already be done
          // ords[slot] = NULL_ORD;
          return;
        }
  
        if (sortPos == 0 && bottomSlot != -1 && bottomSlot != slot) {
          // Since we are the primary sort, the entries in the
          // queue are bounded by bottomOrd:
          assert bottomOrd < lookup.length;
          if (reversed) {
            index = binarySearch(lookup, value, bottomOrd, lookup.length-1);
          } else {
            index = binarySearch(lookup, value, 0, bottomOrd);
          }
        } else {
          // Full binary search
          index = binarySearch(lookup, value);
        }
  
        if (index < 0) {
          index = -index - 2;
        }
        ords[slot] = index;
      }
  
      @Override
      public void copy(int slot, int doc) {
        final int ord = order[doc];
        ords[slot] = ord == 0 ? NULL_ORD : ord;
        assert ord >= 0;
        values[slot] = lookup[ord];
        readerGen[slot] = currentReaderGen;
      }
  
      @Override
      public void setNextReader(IndexReader reader, int docBase) throws IOException {
        FieldCache.StringIndex currentReaderValues = FieldCache.DEFAULT.getStringIndex(reader, field);
        currentReaderGen++;
        order = currentReaderValues.order;
        lookup = currentReaderValues.lookup;
        assert lookup.length > 0;
        if (bottomSlot != -1) {
          convert(bottomSlot);
          bottomOrd = ords[bottomSlot];
        }
      }
  
      @Override
      public void setBottom(final int bottom) {
        bottomSlot = bottom;
        if (readerGen[bottom] != currentReaderGen) {
          convert(bottomSlot);
        }
        bottomOrd = ords[bottom];
        assert bottomOrd >= 0;
        // assert bottomOrd < lookup.length;
        bottomValue = values[bottom];
      }
  
      @Override
      public String value(int slot) {
        String v = values[slot];
        return v==null ? nullVal : v;
      }
      
      @Override
      public double docValue(int doc) {
          return 0.0f;
      }
      public String[] getValues() {
        return values;
      }
  
      public int getBottomSlot() {
        return bottomSlot;
      }
  
      public String getField() {
        return field;
      }
    }
  