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.solr.common.SolrException;
  import org.apache.lucene.util.OpenBitSet;
  import org.apache.lucene.search.DocIdSet;
  import org.apache.lucene.search.Filter;
  import org.apache.lucene.search.DocIdSetIterator;
  import org.apache.lucene.index.IndexReader;
  
  import java.io.IOException;
  
  /**
   * <code>DocSet</code> represents an unordered set of Lucene Document Ids.
   *
   * <p>
   * WARNING: Any DocSet returned from SolrIndexSearcher should <b>not</b> be modified as it may have been retrieved from
   * a cache and could be shared.
   * </p>
   *
   * @version $Id: DocSet.java 1065312 2011-01-30 16:08:25Z rmuir $
   * @since solr 0.9
   */
  public interface DocSet /* extends Collection<Integer> */ {
    
    /**
     * Adds the specified document if it is not currently in the DocSet
     * (optional operation).
     *
     * @see #addUnique
     * @throws SolrException if the implementation does not allow modifications
     */
    public void add(int doc);
  
    /**
     * Adds a document the caller knows is not currently in the DocSet
     * (optional operation).
     *
     * <p>
     * This method may be faster then <code>add(doc)</code> in some
     * implementaions provided the caller is certain of the precondition.
     * </p>
     *
     * @see #add
     * @throws SolrException if the implementation does not allow modifications
     */
    public void addUnique(int doc);
  
    /**
     * Returns the number of documents in the set.
     */
    public int size();
  
    /**
     * Returns true if a document is in the DocSet.
     */
    public boolean exists(int docid);
  
    /**
     * Returns an iterator that may be used to iterate over all of the documents in the set.
     *
     * <p>
     * The order of the documents returned by this iterator is
     * non-deterministic, and any scoring information is meaningless
     * </p>
     */
    public DocIterator iterator();
  
    /**
     * Returns a BitSet view of the DocSet.  Any changes to this BitSet <b>may</b>
     * be reflected in the DocSet, hence if the DocSet is shared or was returned from
     * a SolrIndexSearcher method, it's not safe to modify the BitSet.
     *
     * @return
     * An OpenBitSet with the bit number of every docid set in the set.
     * 
     * @deprecated Use {@link #iterator()} to access all docs instead.
     */
    @Deprecated
    public OpenBitSet getBits();
  
    /**
     * Returns the approximate amount of memory taken by this DocSet.
     * This is only an approximation and doesn't take into account java object overhead.
     *
     * @return
     * the approximate memory consumption in bytes
     */
    public long memSize();
  
    /**
     * Returns the intersection of this set with another set.  Neither set is modified - a new DocSet is
     * created and returned.
     * @return a DocSet representing the intersection
     */
    public DocSet intersection(DocSet other);
  
    /**
     * Returns the number of documents of the intersection of this set with another set.
     * May be more efficient than actually creating the intersection and then getting it's size.
     */
    public int intersectionSize(DocSet other);
  
    /**
     * Returns the union of this set with another set.  Neither set is modified - a new DocSet is
     * created and returned.
     * @return a DocSet representing the union
     */
    public DocSet union(DocSet other);
  
    /**
     * Returns the number of documents of the union of this set with another set.
     * May be more efficient than actually creating the union and then getting it's size.
     */
    public int unionSize(DocSet other);
  
    /**
     * Returns the documents in this set that are not in the other set. Neither set is modified - a new DocSet is
     * created and returned.
     * @return a DocSet representing this AND NOT other
     */
    public DocSet andNot(DocSet other);
  
    /**
     * Returns the number of documents in this set that are not in the other set.
     */
    public int andNotSize(DocSet other);
  
    /**
     * Returns a Filter for use in Lucene search methods, assuming this DocSet
     * was generated from the top-level MultiReader that the Lucene search
     * methods will be invoked with.
     */
    public Filter getTopFilter();
  }
  
  /** A base class that may be usefull for implementing DocSets */
  abstract class DocSetBase implements DocSet {
  
    // Not implemented efficiently... for testing purposes only
    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof DocSet)) return false;
      DocSet other = (DocSet)obj;
      if (this.size() != other.size()) return false;
  
      if (this instanceof DocList && other instanceof DocList) {
        // compare ordering
        DocIterator i1=this.iterator();
        DocIterator i2=other.iterator();
        while(i1.hasNext() && i2.hasNext()) {
          if (i1.nextDoc() != i2.nextDoc()) return false;
        }
        return true;
        // don't compare matches
      }
  
      // if (this.size() != other.size()) return false;
      return this.getBits().equals(other.getBits());
    }
  
    /**
     * @throws SolrException Base implementation does not allow modifications
     */
    public void add(int doc) {
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Unsupported Operation");
    }
  
  
    /**
     * @throws SolrException Base implementation does not allow modifications
     */
    public void addUnique(int doc) {
      throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,"Unsupported Operation");
    }
  
    /**
     * Inefficient base implementation.
     *
     * @see BitDocSet#getBits
     */
    public OpenBitSet getBits() {
      OpenBitSet bits = new OpenBitSet();
      for (DocIterator iter = iterator(); iter.hasNext();) {
        bits.set(iter.nextDoc());
      }
      return bits;
    };
  
    public DocSet intersection(DocSet other) {
      // intersection is overloaded in the smaller DocSets to be more
      // efficient, so dispatch off of it instead.
      if (!(other instanceof BitDocSet)) {
        return other.intersection(this);
      }
  
      // Default... handle with bitsets.
      OpenBitSet newbits = (OpenBitSet)(this.getBits().clone());
      newbits.and(other.getBits());
      return new BitDocSet(newbits);
    }
  
    public DocSet union(DocSet other) {
      OpenBitSet newbits = (OpenBitSet)(this.getBits().clone());
      newbits.or(other.getBits());
      return new BitDocSet(newbits);
    }
  
    public int intersectionSize(DocSet other) {
      // intersection is overloaded in the smaller DocSets to be more
      // efficient, so dispatch off of it instead.
      if (!(other instanceof BitDocSet)) {
        return other.intersectionSize(this);
      }
      // less efficient way: do the intersection then get it's size
      return intersection(other).size();
    }
  
    public int unionSize(DocSet other) {
      return this.size() + other.size() - this.intersectionSize(other);
    }
  
    public DocSet andNot(DocSet other) {
      OpenBitSet newbits = (OpenBitSet)(this.getBits().clone());
      newbits.andNot(other.getBits());
      return new BitDocSet(newbits);
    }
  
    public int andNotSize(DocSet other) {
      return this.size() - this.intersectionSize(other);
    }
  
    public Filter getTopFilter() {
      final OpenBitSet bs = getBits();
  
      return new Filter() {
        @Override
        public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
          int offset = 0;
          SolrIndexReader r = (SolrIndexReader)reader;
          while (r.getParent() != null) {
            offset += r.getBase();
            r = r.getParent();
          }
  
          if (r==reader) return bs;
  
          final int base = offset;
          final int maxDoc = reader.maxDoc();
          final int max = base + maxDoc;   // one past the max doc in this segment.
  
          return new DocIdSet() {
            @Override
            public DocIdSetIterator iterator() throws IOException {
              return new DocIdSetIterator() {
                int pos=base-1;
                int adjustedDoc=-1;
  
                @Override
                public int docID() {
                  return adjustedDoc;
                }
  
                @Override
                public int nextDoc() throws IOException {
                  pos = bs.nextSetBit(pos+1);
                  return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
                }
  
                @Override
                public int advance(int target) throws IOException {
                  if (target==NO_MORE_DOCS) return adjustedDoc=NO_MORE_DOCS;
                  pos = bs.nextSetBit(target+base);
                  return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
                }
              };
            }
  
            @Override
            public boolean isCacheable() {
              return true;
            }
  
          };
        }
      };
    }
  }
  
  
  
  
  
  