You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2015/04/01 03:10:13 UTC

svn commit: r1670533 [2/4] - in /lucene/dev/branches/lucene6271: ./ dev-tools/ lucene/ lucene/analysis/ lucene/analysis/common/ lucene/backward-codecs/ lucene/benchmark/ lucene/classification/ lucene/codecs/ lucene/core/ lucene/core/src/java/org/apache...

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java Wed Apr  1 01:10:11 2015
@@ -30,9 +30,11 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
+import java.util.Objects;
 
-/** Removes matches which overlap with another SpanQuery or 
- * within a x tokens before or y tokens after another SpanQuery. */
+/** Removes matches which overlap with another SpanQuery or which are
+ * within x tokens before or y tokens after another SpanQuery.
+ */
 public class SpanNotQuery extends SpanQuery implements Cloneable {
   private SpanQuery include;
   private SpanQuery exclude;
@@ -45,20 +47,20 @@ public class SpanNotQuery extends SpanQu
      this(include, exclude, 0, 0);
   }
 
-  
+
   /** Construct a SpanNotQuery matching spans from <code>include</code> which
-   * have no overlap with spans from <code>exclude</code> within 
+   * have no overlap with spans from <code>exclude</code> within
    * <code>dist</code> tokens of <code>include</code>. */
   public SpanNotQuery(SpanQuery include, SpanQuery exclude, int dist) {
      this(include, exclude, dist, dist);
   }
-  
+
   /** Construct a SpanNotQuery matching spans from <code>include</code> which
-   * have no overlap with spans from <code>exclude</code> within 
+   * have no overlap with spans from <code>exclude</code> within
    * <code>pre</code> tokens before or <code>post</code> tokens of <code>include</code>. */
   public SpanNotQuery(SpanQuery include, SpanQuery exclude, int pre, int post) {
-    this.include = include;
-    this.exclude = exclude;
+    this.include = Objects.requireNonNull(include);
+    this.exclude = Objects.requireNonNull(exclude);
     this.pre = (pre >=0) ? pre : 0;
     this.post = (post >= 0) ? post : 0;
 
@@ -96,81 +98,153 @@ public class SpanNotQuery extends SpanQu
 
   @Override
   public SpanNotQuery clone() {
-    SpanNotQuery spanNotQuery = new SpanNotQuery((SpanQuery)include.clone(),
-          (SpanQuery) exclude.clone(), pre, post);
+    SpanNotQuery spanNotQuery = new SpanNotQuery((SpanQuery) include.clone(),
+                                                                (SpanQuery) exclude.clone(), pre, post);
     spanNotQuery.setBoost(getBoost());
-    return  spanNotQuery;
+    return spanNotQuery;
   }
 
   @Override
   public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, final Map<Term,TermContext> termContexts) throws IOException {
-    return new Spans() {
-        private Spans includeSpans = include.getSpans(context, acceptDocs, termContexts);
-        private boolean moreInclude = true;
+    Spans includeSpans = include.getSpans(context, acceptDocs, termContexts);
+    if (includeSpans == null) {
+      return null;
+    }
 
-        private Spans excludeSpans = exclude.getSpans(context, acceptDocs, termContexts);
-        private boolean moreExclude = excludeSpans.next();
+    Spans excludeSpans = exclude.getSpans(context, acceptDocs, termContexts);
+    if (excludeSpans == null) {
+      return includeSpans;
+    }
 
-        @Override
-        public boolean next() throws IOException {
-          if (moreInclude)                        // move to next include
-            moreInclude = includeSpans.next();
-
-          while (moreInclude && moreExclude) {
-
-            if (includeSpans.doc() > excludeSpans.doc()) // skip exclude
-              moreExclude = excludeSpans.skipTo(includeSpans.doc());
-
-            while (moreExclude                    // while exclude is before
-                   && includeSpans.doc() == excludeSpans.doc()
-                   && excludeSpans.end() <= includeSpans.start() - pre) {
-              moreExclude = excludeSpans.next();  // increment exclude
-            }
+    return new Spans() {
+      private boolean moreInclude = true;
+      private int includeStart = -1;
+      private int includeEnd = -1;
+      private boolean atFirstInCurrentDoc = false;
+
+      private boolean moreExclude = excludeSpans.nextDoc() != NO_MORE_DOCS;
+      private int excludeStart = moreExclude ? excludeSpans.nextStartPosition() : NO_MORE_POSITIONS;
 
-            if (!moreExclude                      // if no intersection
-                || includeSpans.doc() != excludeSpans.doc()
-                || includeSpans.end()+post <= excludeSpans.start())
-              break;                              // we found a match
 
-            moreInclude = includeSpans.next();    // intersected: keep scanning
+      @Override
+      public int nextDoc() throws IOException {
+        if (moreInclude) {
+          moreInclude = includeSpans.nextDoc() != NO_MORE_DOCS;
+          if (moreInclude) {
+            atFirstInCurrentDoc = true;
+            includeStart = includeSpans.nextStartPosition();
+            assert includeStart != NO_MORE_POSITIONS;
           }
-          return moreInclude;
         }
+        toNextIncluded();
+        int res = moreInclude ? includeSpans.docID() : NO_MORE_DOCS;
+        return res;
+      }
 
-        @Override
-        public boolean skipTo(int target) throws IOException {
-          if (moreInclude)                        // skip include
-            moreInclude = includeSpans.skipTo(target);
+      private void toNextIncluded() throws IOException {
+        while (moreInclude && moreExclude) {
+          if (includeSpans.docID() > excludeSpans.docID()) {
+            moreExclude = excludeSpans.advance(includeSpans.docID()) != NO_MORE_DOCS;
+            if (moreExclude) {
+              excludeStart = -1; // only use exclude positions at same doc
+            }
+          }
+          if (excludeForwardInCurrentDocAndAtMatch()) {
+            break; // at match.
+          }
 
-          if (!moreInclude)
-            return false;
+          // else intersected: keep scanning, to next doc if needed
+          includeStart = includeSpans.nextStartPosition();
+          if (includeStart == NO_MORE_POSITIONS) {
+            moreInclude = includeSpans.nextDoc() != NO_MORE_DOCS;
+            if (moreInclude) {
+              atFirstInCurrentDoc = true;
+              includeStart = includeSpans.nextStartPosition();
+              assert includeStart != NO_MORE_POSITIONS;
+            }
+          }
+        }
+      }
 
-          if (moreExclude                         // skip exclude
-              && includeSpans.doc() > excludeSpans.doc())
-            moreExclude = excludeSpans.skipTo(includeSpans.doc());
+      private boolean excludeForwardInCurrentDocAndAtMatch() throws IOException {
+        assert moreInclude;
+        assert includeStart != NO_MORE_POSITIONS;
+        if (! moreExclude) {
+          return true;
+        }
+        if (includeSpans.docID() != excludeSpans.docID()) {
+          return true;
+        }
+        // at same doc
+        if (excludeStart == -1) { // init exclude start position if needed
+          excludeStart = excludeSpans.nextStartPosition();
+          assert excludeStart != NO_MORE_POSITIONS;
+        }
+        while (excludeSpans.endPosition() <= includeStart - pre) {
+          // exclude end position is before a possible exclusion
+          excludeStart = excludeSpans.nextStartPosition();
+          if (excludeStart == NO_MORE_POSITIONS) {
+            return true; // no more exclude at current doc.
+          }
+        }
+        // exclude end position far enough in current doc, check start position:
+        boolean res = includeSpans.endPosition() + post <= excludeStart;
+        return res;
+      }
 
-          while (moreExclude                      // while exclude is before
-                 && includeSpans.doc() == excludeSpans.doc()
-                 && excludeSpans.end() <= includeSpans.start()-pre) {
-            moreExclude = excludeSpans.next();    // increment exclude
+      @Override
+      public int advance(int target) throws IOException {
+        if (moreInclude) {
+          assert target > includeSpans.docID() : "target="+target+", includeSpans.docID()="+includeSpans.docID();
+          moreInclude = includeSpans.advance(target) != NO_MORE_DOCS;
+          if (moreInclude) {
+            atFirstInCurrentDoc = true;
+            includeStart = includeSpans.nextStartPosition();
+            assert includeStart != NO_MORE_POSITIONS;
           }
+        }
+        toNextIncluded();
+        int res = moreInclude ? includeSpans.docID() : NO_MORE_DOCS;
+        return res;
+      }
 
-          if (!moreExclude                      // if no intersection
-                || includeSpans.doc() != excludeSpans.doc()
-                || includeSpans.end()+post <= excludeSpans.start())
-            return true;                          // we found a match
+      @Override
+      public int docID() {
+        int res = includeSpans.docID();
+        return res;
+      }
+
+      @Override
+      public int nextStartPosition() throws IOException {
+        assert moreInclude;
 
-          return next();                          // scan to next match
+        if (atFirstInCurrentDoc) {
+          atFirstInCurrentDoc = false;
+          assert includeStart != NO_MORE_POSITIONS;
+          return includeStart;
         }
 
-        @Override
-        public int doc() { return includeSpans.doc(); }
-        @Override
-        public int start() { return includeSpans.start(); }
-        @Override
-        public int end() { return includeSpans.end(); }
+        includeStart = includeSpans.nextStartPosition();
+        while ((includeStart != NO_MORE_POSITIONS)
+            && (! excludeForwardInCurrentDocAndAtMatch()))
+        {
+          includeStart = includeSpans.nextStartPosition();
+        }
+
+        return includeStart;
+      }
+
+      @Override
+      public int startPosition() {
+        assert includeStart == includeSpans.startPosition();
+        return atFirstInCurrentDoc ? -1 : includeStart;
+      }
+
+      @Override
+      public int endPosition() {
+        return atFirstInCurrentDoc ? -1 : includeSpans.endPosition();
+      }
 
-      // TODO: Remove warning after API has been finalized
       @Override
       public Collection<byte[]> getPayload() throws IOException {
         ArrayList<byte[]> result = null;
@@ -180,7 +254,6 @@ public class SpanNotQuery extends SpanQu
         return result;
       }
 
-      // TODO: Remove warning after API has been finalized
       @Override
       public boolean isPayloadAvailable() throws IOException {
         return includeSpans.isPayloadAvailable();
@@ -193,10 +266,9 @@ public class SpanNotQuery extends SpanQu
 
       @Override
       public String toString() {
-          return "spans(" + SpanNotQuery.this.toString() + ")";
-        }
-
-      };
+        return "spans(" + SpanNotQuery.this.toString() + ")";
+      }
+    };
   }
 
   @Override
@@ -230,7 +302,7 @@ public class SpanNotQuery extends SpanQu
     SpanNotQuery other = (SpanNotQuery)o;
     return this.include.equals(other.include)
             && this.exclude.equals(other.exclude)
-            && this.pre == other.pre 
+            && this.pre == other.pre
             && this.post == other.post;
   }
 

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java Wed Apr  1 01:10:11 2015
@@ -35,18 +35,19 @@ import org.apache.lucene.util.PriorityQu
 import org.apache.lucene.util.ToStringUtils;
 import org.apache.lucene.search.Query;
 
-/** Matches the union of its clauses.*/
+/** Matches the union of its clauses.
+ */
 public class SpanOrQuery extends SpanQuery implements Cloneable {
   private List<SpanQuery> clauses;
   private String field;
 
-  /** Construct a SpanOrQuery merging the provided clauses. */
+  /** Construct a SpanOrQuery merging the provided clauses.
+   * All clauses must have the same field. 
+   */
   public SpanOrQuery(SpanQuery... clauses) {
-
-    // copy clauses array into an ArrayList
     this.clauses = new ArrayList<>(clauses.length);
-    for (int i = 0; i < clauses.length; i++) {
-      addClause(clauses[i]);
+    for (SpanQuery seq : clauses) {
+      addClause(seq);
     }
   }
 
@@ -59,7 +60,7 @@ public class SpanOrQuery extends SpanQue
     }
     this.clauses.add(clause);
   }
-  
+
   /** Return the clauses whose spans are matched. */
   public SpanQuery[] getClauses() {
     return clauses.toArray(new SpanQuery[clauses.size()]);
@@ -74,7 +75,7 @@ public class SpanOrQuery extends SpanQue
       clause.extractTerms(terms);
     }
   }
-  
+
   @Override
   public SpanOrQuery clone() {
     int sz = clauses.size();
@@ -152,90 +153,120 @@ public class SpanOrQuery extends SpanQue
 
     @Override
     protected final boolean lessThan(Spans spans1, Spans spans2) {
-      if (spans1.doc() == spans2.doc()) {
-        if (spans1.start() == spans2.start()) {
-          return spans1.end() < spans2.end();
+      if (spans1.docID() == spans2.docID()) {
+        if (spans1.startPosition() == spans2.startPosition()) {
+          return spans1.endPosition() < spans2.endPosition();
         } else {
-          return spans1.start() < spans2.start();
+          return spans1.startPosition() < spans2.startPosition();
         }
       } else {
-        return spans1.doc() < spans2.doc();
+        return spans1.docID() < spans2.docID();
       }
     }
   }
 
   @Override
-  public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, final Map<Term,TermContext> termContexts) throws IOException {
-    if (clauses.size() == 1)                      // optimize 1-clause case
-      return (clauses.get(0)).getSpans(context, acceptDocs, termContexts);
+  public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, final Map<Term,TermContext> termContexts)
+  throws IOException {
+
+    ArrayList<Spans> subSpans = new ArrayList<>(clauses.size());
+
+    for (SpanQuery seq : clauses) {
+      Spans subSpan = seq.getSpans(context, acceptDocs, termContexts);
+      if (subSpan != null) {
+        subSpans.add(subSpan);
+      }
+    }
+
+    if (subSpans.size() == 0) {
+      return null;
+    } else if (subSpans.size() == 1) {
+      return subSpans.get(0);
+    }
+
+    SpanQueue queue = new SpanQueue(clauses.size());
+    for (Spans spans : subSpans) {
+      queue.add(spans);
+    }
 
     return new Spans() {
-        private SpanQueue queue = null;
-        private long cost;
 
-        private boolean initSpanQueue(int target) throws IOException {
-          queue = new SpanQueue(clauses.size());
-          Iterator<SpanQuery> i = clauses.iterator();
-          while (i.hasNext()) {
-            Spans spans = i.next().getSpans(context, acceptDocs, termContexts);
-            cost += spans.cost();
-            if (   ((target == -1) && spans.next())
-                || ((target != -1) && spans.skipTo(target))) {
-              queue.add(spans);
-            }
-          }
-          return queue.size() != 0;
+      @Override
+      public int nextDoc() throws IOException {
+        if (queue.size() == 0) { // all done
+          return NO_MORE_DOCS;
         }
 
-        @Override
-        public boolean next() throws IOException {
-          if (queue == null) {
-            return initSpanQueue(-1);
-          }
+        int currentDoc = top().docID();
 
-          if (queue.size() == 0) { // all done
-            return false;
-          }
+        if (currentDoc == -1) { // initially
+          return advance(0);
+        }
 
-          if (top().next()) { // move to next
+        do {
+          if (top().nextDoc() != NO_MORE_DOCS) { // move top to next doc
             queue.updateTop();
-            return true;
+          } else {
+            queue.pop(); // exhausted a clause
+            if (queue.size() == 0) {
+              return NO_MORE_DOCS;
+            }
           }
+          // assert queue.size() > 0;
+          int doc = top().docID();
+          if (doc > currentDoc) {
+            return doc;
+          }
+        } while (true);
+      }
 
-          queue.pop();  // exhausted a clause
-          return queue.size() != 0;
-        }
+      private Spans top() {
+        return queue.top();
+      }
 
-        private Spans top() { return queue.top(); }
+      @Override
+      public int advance(int target) throws IOException {
 
-        @Override
-        public boolean skipTo(int target) throws IOException {
-          if (queue == null) {
-            return initSpanQueue(target);
-          }
-  
-          boolean skipCalled = false;
-          while (queue.size() != 0 && top().doc() < target) {
-            if (top().skipTo(target)) {
-              queue.updateTop();
-            } else {
-              queue.pop();
-            }
-            skipCalled = true;
-          }
-  
-          if (skipCalled) {
-            return queue.size() != 0;
+        while ((queue.size() > 0) && (top().docID() < target)) {
+          if (top().advance(target) != NO_MORE_DOCS) {
+            queue.updateTop();
+          } else {
+            queue.pop();
           }
-          return next();
         }
 
-        @Override
-        public int doc() { return top().doc(); }
-        @Override
-        public int start() { return top().start(); }
-        @Override
-        public int end() { return top().end(); }
+        return (queue.size() > 0) ? top().docID() : NO_MORE_DOCS;
+      }
+
+      @Override
+      public int docID() {
+        return (queue == null) ? -1
+              : (queue.size() > 0) ? top().docID()
+              : NO_MORE_DOCS;
+      }
+
+      @Override
+      public int nextStartPosition() throws IOException {
+        top().nextStartPosition();
+        queue.updateTop();
+        int startPos = top().startPosition();
+        while (startPos == -1) { // initially at this doc
+          top().nextStartPosition();
+          queue.updateTop();
+          startPos = top().startPosition();
+        }
+        return startPos;
+      }
+
+      @Override
+      public int startPosition() {
+        return top().startPosition();
+      }
+
+      @Override
+      public int endPosition() {
+        return top().endPosition();
+      }
 
       @Override
       public Collection<byte[]> getPayload() throws IOException {
@@ -257,15 +288,23 @@ public class SpanOrQuery extends SpanQue
       public String toString() {
           return "spans("+SpanOrQuery.this+")@"+
             ((queue == null)?"START"
-             :(queue.size()>0?(doc()+":"+start()+"-"+end()):"END"));
-        }
+             :(queue.size()>0?(docID()+": "+top().startPosition()+" - "+top().endPosition()):"END"));
+      }
+
+      private long cost = -1;
 
       @Override
       public long cost() {
+        if (cost == -1) {
+          cost = 0;
+          for (Spans spans : subSpans) {
+            cost += spans.cost();
+          }
+        }
         return cost;
       }
-      
-      };
+
+    };
   }
 
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPayloadCheckQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPayloadCheckQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPayloadCheckQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPayloadCheckQuery.java Wed Apr  1 01:10:11 2015
@@ -28,15 +28,14 @@ import java.util.Iterator;
  * Only return those matches that have a specific payload at
  * the given position.
  * <p>
- * Do not use this with an SpanQuery that contains a {@link org.apache.lucene.search.spans.SpanNearQuery}.  Instead, use
- * {@link SpanNearPayloadCheckQuery} since it properly handles the fact that payloads
+ * Do not use this with a SpanQuery that contains a {@link org.apache.lucene.search.spans.SpanNearQuery}.
+ * Instead, use {@link SpanNearPayloadCheckQuery} since it properly handles the fact that payloads
  * aren't ordered by {@link org.apache.lucene.search.spans.SpanNearQuery}.
  */
-public class SpanPayloadCheckQuery extends SpanPositionCheckQuery{
+public class SpanPayloadCheckQuery extends SpanPositionCheckQuery {
   protected final Collection<byte[]> payloadToMatch;
 
   /**
-   *
    * @param match The underlying {@link org.apache.lucene.search.spans.SpanQuery} to check
    * @param payloadToMatch The {@link java.util.Collection} of payloads to match
    */
@@ -71,7 +70,7 @@ public class SpanPayloadCheckQuery exten
       }
     }
     return AcceptStatus.YES;
-  } 
+  }
 
   @Override
   public String toString(String field) {
@@ -108,7 +107,7 @@ public class SpanPayloadCheckQuery exten
 
   @Override
   public int hashCode() {
-    int h = match.hashCode();
+    int h = match.hashCode() ^ getClass().hashCode();
     h ^= (h << 8) | (h >>> 25);  // reversible
     //TODO: is this right?
     h ^= payloadToMatch.hashCode();

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java Wed Apr  1 01:10:11 2015
@@ -25,10 +25,9 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.util.Bits;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
+import java.util.Objects;
 
 
 /**
@@ -37,9 +36,8 @@ import java.util.Set;
 public abstract class SpanPositionCheckQuery extends SpanQuery implements Cloneable {
   protected SpanQuery match;
 
-
   public SpanPositionCheckQuery(SpanQuery match) {
-    this.match = match;
+    this.match = Objects.requireNonNull(match);
   }
 
   /**
@@ -60,42 +58,44 @@ public abstract class SpanPositionCheckQ
     match.extractTerms(terms);
   }
 
-  /** 
+  /**
    * Return value for {@link SpanPositionCheckQuery#acceptPosition(Spans)}.
    */
   protected static enum AcceptStatus {
     /** Indicates the match should be accepted */
     YES,
-    
+
     /** Indicates the match should be rejected */
     NO,
-    
-    /** 
-     * Indicates the match should be rejected, and the enumeration should advance
-     * to the next document.
+
+    /**
+     * Indicates the match should be rejected, and the enumeration may continue
+     * with the next document.
      */
-    NO_AND_ADVANCE 
+    NO_MORE_IN_CURRENT_DOC
   };
-  
+
   /**
    * Implementing classes are required to return whether the current position is a match for the passed in
-   * "match" {@link org.apache.lucene.search.spans.SpanQuery}.
+   * "match" {@link SpanQuery}.
+   *
+   * This is only called if the underlying last {@link Spans#nextStartPosition()} for the
+   * match indicated a valid start position.
    *
-   * This is only called if the underlying {@link org.apache.lucene.search.spans.Spans#next()} for the
-   * match is successful
    *
+   * @param spans The {@link Spans} instance, positioned at the spot to check
    *
-   * @param spans The {@link org.apache.lucene.search.spans.Spans} instance, positioned at the spot to check
    * @return whether the match is accepted, rejected, or rejected and should move to the next doc.
    *
-   * @see org.apache.lucene.search.spans.Spans#next()
+   * @see Spans#nextDoc()
    *
    */
   protected abstract AcceptStatus acceptPosition(Spans spans) throws IOException;
 
   @Override
   public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, Map<Term,TermContext> termContexts) throws IOException {
-    return new PositionCheckSpan(context, acceptDocs, termContexts);
+    Spans matchSpans = match.getSpans(context, acceptDocs, termContexts);
+    return (matchSpans == null) ? null : new PositionCheckSpans(matchSpans);
   }
 
 
@@ -116,79 +116,110 @@ public abstract class SpanPositionCheckQ
     }
   }
 
-  protected class PositionCheckSpan extends Spans {
-    private Spans spans;
+  protected class PositionCheckSpans extends FilterSpans {
+
+    private boolean atFirstInCurrentDoc = false;
+    private int startPos = -1;
 
-    public PositionCheckSpan(LeafReaderContext context, Bits acceptDocs, Map<Term,TermContext> termContexts) throws IOException {
-      spans = match.getSpans(context, acceptDocs, termContexts);
+    public PositionCheckSpans(Spans matchSpans) throws IOException {
+      super(matchSpans);
     }
 
     @Override
-    public boolean next() throws IOException {
-      if (!spans.next())
-        return false;
-      
-      return doNext();
+    public int nextDoc() throws IOException {
+      if (in.nextDoc() == NO_MORE_DOCS)
+        return NO_MORE_DOCS;
+
+      return toNextDocWithAllowedPosition();
     }
 
     @Override
-    public boolean skipTo(int target) throws IOException {
-      if (!spans.skipTo(target))
-        return false;
+    public int advance(int target) throws IOException {
+      if (in.advance(target) == NO_MORE_DOCS)
+        return NO_MORE_DOCS;
 
-      return doNext();
+      return toNextDocWithAllowedPosition();
     }
-    
-    protected boolean doNext() throws IOException {
+
+    @SuppressWarnings("fallthrough")
+    protected int toNextDocWithAllowedPosition() throws IOException {
+      startPos = in.nextStartPosition();
+      assert startPos != NO_MORE_POSITIONS;
       for (;;) {
         switch(acceptPosition(this)) {
-          case YES: return true;
-          case NO: 
-            if (!spans.next()) 
-              return false;
-            break;
-          case NO_AND_ADVANCE: 
-            if (!spans.skipTo(spans.doc()+1)) 
-              return false;
+          case YES:
+            atFirstInCurrentDoc = true;
+            return in.docID();
+          case NO:
+            startPos = in.nextStartPosition();
+            if (startPos != NO_MORE_POSITIONS) {
+              break;
+            }
+            // else fallthrough
+          case NO_MORE_IN_CURRENT_DOC:
+            if (in.nextDoc() == NO_MORE_DOCS) {
+              startPos = -1;
+              return NO_MORE_DOCS;
+            }
+            startPos = in.nextStartPosition();
+            assert startPos != NO_MORE_POSITIONS : "no start position at doc="+in.docID();
             break;
         }
       }
     }
 
     @Override
-    public int doc() { return spans.doc(); }
-
-    @Override
-    public int start() { return spans.start(); }
-
-    @Override
-    public int end() { return spans.end(); }
-    // TODO: Remove warning after API has been finalized
+    public int nextStartPosition() throws IOException {
+      if (atFirstInCurrentDoc) {
+        atFirstInCurrentDoc = false;
+        return startPos;
+      }
 
-    @Override
-    public Collection<byte[]> getPayload() throws IOException {
-      ArrayList<byte[]> result = null;
-      if (spans.isPayloadAvailable()) {
-        result = new ArrayList<>(spans.getPayload());
+      for (;;) {
+        startPos = in.nextStartPosition();
+        if (startPos == NO_MORE_POSITIONS) {
+          return NO_MORE_POSITIONS;
+        }
+        switch(acceptPosition(this)) {
+          case YES:
+            return startPos;
+          case NO:
+            break;
+          case NO_MORE_IN_CURRENT_DOC:
+            return startPos = NO_MORE_POSITIONS; // startPos ahead for the current doc.
+        }
       }
-      return result;//TODO: any way to avoid the new construction?
     }
-    // TODO: Remove warning after API has been finalized
 
     @Override
-    public boolean isPayloadAvailable() throws IOException {
-      return spans.isPayloadAvailable();
+    public int startPosition() {
+      return atFirstInCurrentDoc ? -1 : startPos;
     }
 
     @Override
-    public long cost() {
-      return spans.cost();
+    public int endPosition() {
+      return atFirstInCurrentDoc ? -1
+            : (startPos != NO_MORE_POSITIONS) ? in.endPosition() : NO_MORE_POSITIONS;
     }
 
     @Override
     public String toString() {
-        return "spans(" + SpanPositionCheckQuery.this.toString() + ")";
-      }
+      return "spans(" + SpanPositionCheckQuery.this.toString() + ")";
+    }
+  }
 
+  /** Returns true iff <code>o</code> is equal to this. */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null) return false;
+    if (getClass() != o.getClass()) return false;
+    final SpanPositionCheckQuery spcq = (SpanPositionCheckQuery) o;
+    return match.equals(spcq.match);
+  }
+
+  @Override
+  public int hashCode() {
+    return match.hashCode() ^ getClass().hashCode();
   }
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionRangeQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionRangeQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionRangeQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionRangeQuery.java Wed Apr  1 01:10:11 2015
@@ -25,10 +25,10 @@ import java.io.IOException;
 /**
  * Checks to see if the {@link #getMatch()} lies between a start and end position
  *
- * @see org.apache.lucene.search.spans.SpanFirstQuery for a derivation that is optimized for the case where start position is 0
+ * See {@link SpanFirstQuery} for a derivation that is optimized for the case where start position is 0.
  */
 public class SpanPositionRangeQuery extends SpanPositionCheckQuery {
-  protected int start = 0;
+  protected int start;
   protected int end;
 
   public SpanPositionRangeQuery(SpanQuery match, int start, int end) {
@@ -40,13 +40,12 @@ public class SpanPositionRangeQuery exte
 
   @Override
   protected AcceptStatus acceptPosition(Spans spans) throws IOException {
-    assert spans.start() != spans.end();
-    if (spans.start() >= end)
-      return AcceptStatus.NO_AND_ADVANCE;
-    else if (spans.start() >= start && spans.end() <= end)
-      return AcceptStatus.YES;
-    else
-      return AcceptStatus.NO;
+    assert spans.startPosition() != spans.endPosition();
+    AcceptStatus res = (spans.startPosition() >= end)
+                      ? AcceptStatus.NO_MORE_IN_CURRENT_DOC
+                      : (spans.startPosition() >= start && spans.endPosition() <= end)
+                      ? AcceptStatus.YES : AcceptStatus.NO;
+    return res;
   }
 
 
@@ -96,7 +95,7 @@ public class SpanPositionRangeQuery exte
 
   @Override
   public int hashCode() {
-    int h = match.hashCode();
+    int h = match.hashCode() ^ getClass().hashCode();
     h ^= (h << 8) | (h >>> 25);  // reversible
     h ^= Float.floatToRawIntBits(getBoost()) ^ end ^ start;
     return h;

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanQuery.java Wed Apr  1 01:10:11 2015
@@ -25,16 +25,17 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
 
 /** Base class for span-based queries. */
 public abstract class SpanQuery extends Query {
-  /** Expert: Returns the matches for this query in an index.  Used internally
-   * to search for spans. */
+  /** Expert: Returns the matches for this query in an index.  
+   *  Used internally to search for spans.
+   *  This may return null to indicate that the SpanQuery has no results.
+   */
   public abstract Spans getSpans(LeafReaderContext context, Bits acceptDocs, Map<Term,TermContext> termContexts) throws IOException;
 
-  /** 
+  /**
    * Returns the name of the field matched by this query.
    * <p>
    * Note that this may return null if the query matches no terms.
@@ -42,7 +43,7 @@ public abstract class SpanQuery extends
   public abstract String getField();
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+  public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
     return new SpanWeight(this, searcher);
   }
 

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java Wed Apr  1 01:10:11 2015
@@ -18,9 +18,9 @@ package org.apache.lucene.search.spans;
  */
 
 import java.io.IOException;
+import java.util.Objects;
 
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.Similarity;
 
 /**
@@ -29,58 +29,68 @@ import org.apache.lucene.search.similari
 public class SpanScorer extends Scorer {
   protected Spans spans;
 
-  protected boolean more = true;
-
   protected int doc;
   protected float freq;
   protected int numMatches;
   protected final Similarity.SimScorer docScorer;
-  
-  protected SpanScorer(Spans spans, Weight weight, Similarity.SimScorer docScorer)
+
+  protected SpanScorer(Spans spans, SpanWeight weight, Similarity.SimScorer docScorer)
   throws IOException {
     super(weight);
-    this.docScorer = docScorer;
-    this.spans = spans;
-
-    doc = -1;
-    more = spans.next();
+    this.docScorer = Objects.requireNonNull(docScorer);
+    this.spans = Objects.requireNonNull(spans);
+    this.doc = -1;
   }
 
   @Override
   public int nextDoc() throws IOException {
-    if (!setFreqCurrentDoc()) {
-      doc = NO_MORE_DOCS;
+    int prevDoc = doc;
+    doc = spans.nextDoc();
+    if (doc != NO_MORE_DOCS) {
+      setFreqCurrentDoc();
     }
     return doc;
   }
 
   @Override
   public int advance(int target) throws IOException {
-    if (!more) {
-      return doc = NO_MORE_DOCS;
-    }
-    if (spans.doc() < target) { // setFreqCurrentDoc() leaves spans.doc() ahead
-      more = spans.skipTo(target);
-    }
-    if (!setFreqCurrentDoc()) {
-      doc = NO_MORE_DOCS;
+    int prevDoc = doc;
+    doc = spans.advance(target);
+    if (doc != NO_MORE_DOCS) {
+      setFreqCurrentDoc();
     }
     return doc;
   }
-  
+
   protected boolean setFreqCurrentDoc() throws IOException {
-    if (!more) {
-      return false;
-    }
-    doc = spans.doc();
     freq = 0.0f;
     numMatches = 0;
+
+    assert spans.startPosition() == -1 : "incorrect initial start position, spans="+spans;
+    assert spans.endPosition() == -1 : "incorrect initial end position, spans="+spans;
+    int prevStartPos = -1;
+    int prevEndPos = -1;
+
+    int startPos = spans.nextStartPosition();
+    assert startPos != Spans.NO_MORE_POSITIONS : "initial startPos NO_MORE_POSITIONS, spans="+spans;
     do {
-      int matchLength = spans.end() - spans.start();
-      freq += docScorer.computeSlopFactor(matchLength);
+      assert startPos >= prevStartPos;
+      int endPos = spans.endPosition();
+      assert endPos != Spans.NO_MORE_POSITIONS;
+      // This assertion can fail for Or spans on the same term:
+      // assert (startPos != prevStartPos) || (endPos > prevEndPos) : "non increased endPos="+endPos;
+      assert (startPos != prevStartPos) || (endPos >= prevEndPos) : "decreased endPos="+endPos;
       numMatches++;
-      more = spans.next();
-    } while (more && (doc == spans.doc()));
+      int matchLength = endPos - startPos;
+      freq += docScorer.computeSlopFactor(matchLength);
+      prevStartPos = startPos;
+      prevEndPos = endPos;
+      startPos = spans.nextStartPosition();
+    } while (startPos != Spans.NO_MORE_POSITIONS);
+
+    assert spans.startPosition() == Spans.NO_MORE_POSITIONS : "incorrect final start position, spans="+spans;
+    assert spans.endPosition() == Spans.NO_MORE_POSITIONS : "incorrect final end position, spans="+spans;
+
     return true;
   }
 
@@ -89,15 +99,16 @@ public class SpanScorer extends Scorer {
 
   @Override
   public float score() throws IOException {
-    return docScorer.score(doc, freq);
+    float s = docScorer.score(doc, freq);
+    return s;
   }
-  
+
   @Override
   public int freq() throws IOException {
     return numMatches;
   }
 
-  /** Returns the intermediate "sloppy freq" adjusted for edit distance 
+  /** Returns the intermediate "sloppy freq" adjusted for edit distance
    *  @lucene.internal */
   // only public so .payloads can see it.
   public float sloppyFreq() throws IOException {

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java Wed Apr  1 01:10:11 2015
@@ -20,6 +20,7 @@ package org.apache.lucene.search.spans;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Set;
+import java.util.Objects;
 
 import org.apache.lucene.index.PostingsEnum;
 import org.apache.lucene.index.LeafReaderContext;
@@ -31,19 +32,23 @@ import org.apache.lucene.index.TermsEnum
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.ToStringUtils;
 
-/** Matches spans containing a term. */
+/** Matches spans containing a term.
+ * This should not be used for terms that are indexed at position Integer.MAX_VALUE.
+ */
 public class SpanTermQuery extends SpanQuery {
   protected Term term;
 
   /** Construct a SpanTermQuery matching the named term's spans. */
-  public SpanTermQuery(Term term) { this.term = term; }
+  public SpanTermQuery(Term term) {
+    this.term = Objects.requireNonNull(term);
+  }
 
   /** Return the term whose spans are matched. */
   public Term getTerm() { return term; }
 
   @Override
   public String getField() { return term.field(); }
-  
+
   @Override
   public void extractTerms(Set<Term> terms) {
     terms.add(term);
@@ -64,7 +69,7 @@ public class SpanTermQuery extends SpanQ
   public int hashCode() {
     final int prime = 31;
     int result = super.hashCode();
-    result = prime * result + ((term == null) ? 0 : term.hashCode());
+    result = prime * result + term.hashCode();
     return result;
   }
 
@@ -77,12 +82,7 @@ public class SpanTermQuery extends SpanQ
     if (getClass() != obj.getClass())
       return false;
     SpanTermQuery other = (SpanTermQuery) obj;
-    if (term == null) {
-      if (other.term != null)
-        return false;
-    } else if (!term.equals(other.term))
-      return false;
-    return true;
+    return term.equals(other.term);
   }
 
   @Override
@@ -99,7 +99,7 @@ public class SpanTermQuery extends SpanQ
         }
 
         final TermsEnum termsEnum = terms.iterator(null);
-        if (termsEnum.seekExact(term.bytes())) { 
+        if (termsEnum.seekExact(term.bytes())) {
           state = termsEnum.termState();
         } else {
           state = null;
@@ -110,14 +110,14 @@ public class SpanTermQuery extends SpanQ
     } else {
       state = termContext.get(context.ord);
     }
-    
+
     if (state == null) { // term is not present in that reader
-      return TermSpans.EMPTY_TERM_SPANS;
+      return null;
     }
-    
+
     final TermsEnum termsEnum = context.reader().terms(term.field()).iterator(null);
     termsEnum.seekExact(term.bytes(), state);
-    
+
     final PostingsEnum postings = termsEnum.postings(acceptDocs, null, PostingsEnum.PAYLOADS);
     return new TermSpans(postings, term);
   }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java Wed Apr  1 01:10:11 2015
@@ -51,7 +51,7 @@ public class SpanWeight extends Weight {
     super(query);
     this.similarity = searcher.getSimilarity();
     this.query = query;
-    
+
     termContexts = new HashMap<>();
     TreeSet<Term> terms = new TreeSet<>();
     query.extractTerms(terms);
@@ -66,8 +66,8 @@ public class SpanWeight extends Weight {
     }
     final String field = query.getField();
     if (field != null) {
-      stats = similarity.computeWeight(query.getBoost(), 
-                                       searcher.collectionStatistics(query.getField()), 
+      stats = similarity.computeWeight(query.getBoost(),
+                                       searcher.collectionStatistics(query.getField()),
                                        termStats);
     }
   }
@@ -88,9 +88,9 @@ public class SpanWeight extends Weight {
   public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
     if (stats == null) {
       return null;
-    } else {
-      return new SpanScorer(query.getSpans(context, acceptDocs, termContexts), this, similarity.simScorer(stats, context));
     }
+    Spans spans = query.getSpans(context, acceptDocs, termContexts);
+    return (spans == null) ? null : new SpanScorer(spans, this, similarity.simScorer(stats, context));
   }
 
   @Override
@@ -106,11 +106,11 @@ public class SpanWeight extends Weight {
         Explanation scoreExplanation = docScorer.explain(doc, new Explanation(freq, "phraseFreq=" + freq));
         result.addDetail(scoreExplanation);
         result.setValue(scoreExplanation.getValue());
-        result.setMatch(true);          
+        result.setMatch(true);
         return result;
       }
     }
-    
+
     return new ComplexExplanation(false, 0.0f, "no matching term");
   }
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/Spans.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/Spans.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/Spans.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/Spans.java Wed Apr  1 01:10:11 2015
@@ -20,54 +20,44 @@ package org.apache.lucene.search.spans;
 import java.io.IOException;
 import java.util.Collection;
 
-/** Expert: an enumeration of span matches.  Used to implement span searching.
- * Each span represents a range of term positions within a document.  Matches
- * are enumerated in order, by increasing document number, within that by
- * increasing start position and finally by increasing end position. */
-public abstract class Spans {
-  /** Move to the next match, returning true iff any such exists. */
-  public abstract boolean next() throws IOException;
-
-  /** Skips to the first match beyond the current, whose document number is
-   * greater than or equal to <i>target</i>.
-   * <p>The behavior of this method is <b>undefined</b> when called with
-   * <code> target &le; current</code>, or after the iterator has exhausted.
-   * Both cases may result in unpredicted behavior.
-   * <p>Returns true iff there is such
-   * a match.  <p>Behaves as if written: 
-   * <pre class="prettyprint">
-   *   boolean skipTo(int target) {
-   *     do {
-   *       if (!next())
-   *         return false;
-   *     } while (target &gt; doc());
-   *     return true;
-   *   }
-   * </pre>
-   * Most implementations are considerably more efficient than that.
-   */
-  public abstract boolean skipTo(int target) throws IOException;
-
-  /** Returns the document number of the current match.  Initially invalid. */
-  public abstract int doc();
-
-  /** Returns the start position of the current match.  Initially invalid. */
-  public abstract int start();
-
-  /** Returns the end position of the current match.  Initially invalid. */
-  public abstract int end();
-  
-  /**
-   * Returns the payload data for the current span.
-   * This is invalid until {@link #next()} is called for
-   * the first time.
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.TwoPhaseIterator;
+
+/** Iterates through combinations of start/end positions per-doc.
+ *  Each start/end position represents a range of term positions within the current document.
+ *  These are enumerated in order, by increasing document number, within that by
+ *  increasing start position and finally by increasing end position.
+ */
+public abstract class Spans extends DocIdSetIterator {
+  public static final int NO_MORE_POSITIONS = Integer.MAX_VALUE;
+
+  /**
+   * Returns the next start position for the current doc.
+   * There is always at least one start/end position per doc.
+   * After the last start/end position at the current doc this returns {@link #NO_MORE_POSITIONS}.
+   */
+  public abstract int nextStartPosition() throws IOException;
+
+  /**
+   * Returns the start position in the current doc, or -1 when {@link #nextStartPosition} was not yet called on the current doc.
+   * After the last start/end position at the current doc this returns {@link #NO_MORE_POSITIONS}.
+   */
+  public abstract int startPosition();
+
+  /**
+   * Returns the end position for the current start position, or -1 when {@link #nextStartPosition} was not yet called on the current doc.
+   * After the last start/end position at the current doc this returns {@link #NO_MORE_POSITIONS}.
+   */
+  public abstract int endPosition();
+
+  /**
+   * Returns the payload data for the current start/end position.
+   * This is only valid after {@link #nextStartPosition()}
+   * returned an available start position.
    * This method must not be called more than once after each call
-   * of {@link #next()}. However, most payloads are loaded lazily,
+   * of {@link #nextStartPosition()}. However, most payloads are loaded lazily,
    * so if the payload data for the current position is not needed,
-   * this method may not be called at all for performance reasons. An ordered
-   * SpanQuery does not lazy load, so if you have payloads in your index and
-   * you do not want ordered SpanNearQuerys to collect payloads, you can
-   * disable collection with a constructor option.<br>
+   * this method may not be called at all for performance reasons.
    * <br>
    * Note that the return type is a collection, thus the ordering should not be relied upon.
    * <br>
@@ -76,25 +66,35 @@ public abstract class Spans {
    * @return a List of byte arrays containing the data of this payload, otherwise null if isPayloadAvailable is false
    * @throws IOException if there is a low-level I/O error
    */
-  // TODO: Remove warning after API has been finalized
   public abstract Collection<byte[]> getPayload() throws IOException;
 
   /**
-   * Checks if a payload can be loaded at this position.
+   * Checks if a payload can be loaded at the current start/end position.
    * <p>
    * Payloads can only be loaded once per call to
-   * {@link #next()}.
+   * {@link #nextStartPosition()}.
    *
-   * @return true if there is a payload available at this position that can be loaded
+   * @return true if there is a payload available at this start/end position
+   *              that can be loaded
    */
   public abstract boolean isPayloadAvailable() throws IOException;
-  
+
   /**
-   * Returns the estimated cost of this spans.
-   * <p>
-   * This is generally an upper bound of the number of documents this iterator
-   * might match, but may be a rough heuristic, hardcoded value, or otherwise
-   * completely inaccurate.
+   * Optional method: Return a {@link TwoPhaseIterator} view of this
+   * {@link Spans}. A return value of {@code null} indicates that
+   * two-phase iteration is not supported.
+   *
+   * Note that the returned {@link TwoPhaseIterator}'s
+   * {@link TwoPhaseIterator#approximation() approximation} must
+   * advance synchronously with this iterator: advancing the approximation must
+   * advance this iterator and vice-versa.
+   *
+   * Implementing this method is typically useful on {@link Spans}s
+   * that have a high per-document overhead in order to confirm matches.
+   *
+   * The default implementation returns {@code null}.
    */
-  public abstract long cost();
+  public TwoPhaseIterator asTwoPhaseIterator() {
+    return null;
+  }
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java Wed Apr  1 01:10:11 2015
@@ -24,10 +24,12 @@ import org.apache.lucene.util.BytesRef;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Collection;
+import java.util.Objects;
 
 /**
  * Expert:
- * Public for extension only
+ * Public for extension only.
+ * This does not work correctly for terms that indexed at position Integer.MAX_VALUE.
  */
 public class TermSpans extends Spans {
   protected final PostingsEnum postings;
@@ -39,65 +41,67 @@ public class TermSpans extends Spans {
   protected boolean readPayload;
 
   public TermSpans(PostingsEnum postings, Term term) {
-    this.postings = postings;
-    this.term = term;
-    doc = -1;
-  }
-
-  // only for EmptyTermSpans (below)
-  TermSpans() {
-    term = null;
-    postings = null;
+    this.postings = Objects.requireNonNull(postings);
+    this.term = Objects.requireNonNull(term);
+    this.doc = -1;
+    this.position = -1;
   }
 
   @Override
-  public boolean next() throws IOException {
-    if (count == freq) {
-      if (postings == null) {
-        return false;
-      }
-      doc = postings.nextDoc();
-      if (doc == DocIdSetIterator.NO_MORE_DOCS) {
-        return false;
-      }
+  public int nextDoc() throws IOException {
+    doc = postings.nextDoc();
+    if (doc != DocIdSetIterator.NO_MORE_DOCS) {
       freq = postings.freq();
+      assert freq >= 1;
       count = 0;
     }
-    position = postings.nextPosition();
-    count++;
-    readPayload = false;
-    return true;
+    position = -1;
+    return doc;
   }
 
   @Override
-  public boolean skipTo(int target) throws IOException {
+  public int advance(int target) throws IOException {
     assert target > doc;
     doc = postings.advance(target);
-    if (doc == DocIdSetIterator.NO_MORE_DOCS) {
-      return false;
+    if (doc != DocIdSetIterator.NO_MORE_DOCS) {
+      freq = postings.freq();
+      assert freq >= 1;
+      count = 0;
     }
-
-    freq = postings.freq();
-    count = 0;
-    position = postings.nextPosition();
-    count++;
-    readPayload = false;
-    return true;
+    position = -1;
+    return doc;
   }
 
   @Override
-  public int doc() {
+  public int docID() {
     return doc;
   }
 
   @Override
-  public int start() {
+  public int nextStartPosition() throws IOException {
+    if (count == freq) {
+      assert position != NO_MORE_POSITIONS;
+      return position = NO_MORE_POSITIONS;
+    }
+    int prevPosition = position;
+    position = postings.nextPosition();
+    assert position >= prevPosition : "prevPosition="+prevPosition+" > position="+position;
+    assert position != NO_MORE_POSITIONS; // int endPosition not possible
+    count++;
+    readPayload = false;
+    return position;
+  }
+
+  @Override
+  public int startPosition() {
     return position;
   }
 
   @Override
-  public int end() {
-    return position + 1;
+  public int endPosition() {
+    return (position == -1) ? -1
+          : (position != NO_MORE_POSITIONS) ? position + 1
+          : NO_MORE_POSITIONS;
   }
 
   @Override
@@ -105,7 +109,6 @@ public class TermSpans extends Spans {
     return postings.cost();
   }
 
-  // TODO: Remove warning after API has been finalized
   @Override
   public Collection<byte[]> getPayload() throws IOException {
     final BytesRef payload = postings.getPayload();
@@ -120,7 +123,6 @@ public class TermSpans extends Spans {
     return Collections.singletonList(bytes);
   }
 
-  // TODO: Remove warning after API has been finalized
   @Override
   public boolean isPayloadAvailable() throws IOException {
     return readPayload == false && postings.getPayload() != null;
@@ -129,55 +131,12 @@ public class TermSpans extends Spans {
   @Override
   public String toString() {
     return "spans(" + term.toString() + ")@" +
-            (doc == -1 ? "START" : (doc == Integer.MAX_VALUE) ? "END" : doc + "-" + position);
+            (doc == -1 ? "START" : (doc == NO_MORE_DOCS) ? "ENDDOC"
+              : doc + " - " + (position == NO_MORE_POSITIONS ? "ENDPOS" : position));
   }
 
   public PostingsEnum getPostings() {
     return postings;
   }
 
-  private static final class EmptyTermSpans extends TermSpans {
-
-    @Override
-    public boolean next() {
-      return false;
-    }
-
-    @Override
-    public boolean skipTo(int target) {
-      return false;
-    }
-
-    @Override
-    public int doc() {
-      return DocIdSetIterator.NO_MORE_DOCS;
-    }
-    
-    @Override
-    public int start() {
-      return -1;
-    }
-
-    @Override
-    public int end() {
-      return -1;
-    }
-
-    @Override
-    public Collection<byte[]> getPayload() {
-      return null;
-    }
-
-    @Override
-    public boolean isPayloadAvailable() {
-      return false;
-    }
-
-    @Override
-    public long cost() {
-      return 0;
-    }
-  }
-
-  public static final TermSpans EMPTY_TERM_SPANS = new EmptyTermSpans();
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/package-info.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/package-info.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/package-info.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/search/spans/package-info.java Wed Apr  1 01:10:11 2015
@@ -18,14 +18,18 @@
 /**
  * The calculus of spans.
  * 
- * <p>A span is a <code>&lt;doc,startPosition,endPosition&gt;</code> tuple.</p>
+ * <p>A span is a <code>&lt;doc,startPosition,endPosition&gt;</code> tuple  that is enumerated by
+ *    class {@link org.apache.lucene.search.spans.Spans Spans}.
+ *  </p>
  * 
  * <p>The following span query operators are implemented:
  * 
  * <ul>
  * 
  * <li>A {@link org.apache.lucene.search.spans.SpanTermQuery SpanTermQuery} matches all spans
- * containing a particular {@link org.apache.lucene.index.Term Term}.</li>
+ *    containing a particular {@link org.apache.lucene.index.Term Term}.
+ *    This should not be used for terms that are indexed at position Integer.MAX_VALUE.
+ * </li>
  * 
  * <li> A {@link org.apache.lucene.search.spans.SpanNearQuery SpanNearQuery} matches spans
  * which occur near one another, and can be used to implement things like

Modified: lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/util/Version.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/util/Version.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/util/Version.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/java/org/apache/lucene/util/Version.java Wed Apr  1 01:10:11 2015
@@ -46,6 +46,13 @@ public final class Version {
   @Deprecated
   public static final Version LUCENE_5_1_0 = new Version(5, 1, 0);
 
+  /**
+   * Match settings and bugs in Lucene's 5.2.0 release.
+   * @deprecated Use latest
+   */
+  @Deprecated
+  public static final Version LUCENE_5_2_0 = new Version(5, 2, 0);
+
   /** Match settings and bugs in Lucene's 6.0 release.
    *  <p>
    *  Use this to get the latest &amp; greatest settings, bug

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestConcurrentMergeScheduler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestConcurrentMergeScheduler.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestConcurrentMergeScheduler.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestConcurrentMergeScheduler.java Wed Apr  1 01:10:11 2015
@@ -217,6 +217,9 @@ public class TestConcurrentMergeSchedule
 
   public void testNoWaitClose() throws IOException {
     Directory directory = newDirectory();
+    if (directory instanceof MockDirectoryWrapper) {
+      ((MockDirectoryWrapper) directory).setPreventDoubleWrite(false);
+    }
     Document doc = new Document();
     Field idField = newStringField("id", "", Field.Store.YES);
     doc.add(idField);
@@ -248,7 +251,6 @@ public class TestConcurrentMergeSchedule
       // stress out aborting them on close:
       ((LogMergePolicy) writer.getConfig().getMergePolicy()).setMergeFactor(3);
       writer.addDocument(doc);
-      writer.commit();
 
       try {
         writer.commit();
@@ -267,7 +269,8 @@ public class TestConcurrentMergeSchedule
               setOpenMode(OpenMode.APPEND).
               setMergePolicy(newLogMergePolicy(100)).
               // Force excessive merging:
-              setMaxBufferedDocs(2)
+              setMaxBufferedDocs(2).
+              setCommitOnClose(false)
       );
     }
     writer.close();

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/index/TestFieldsReader.java Wed Apr  1 01:10:11 2015
@@ -172,7 +172,7 @@ public class TestFieldsReader extends Lu
       try {
         i.seek(getFilePointer());
       } catch (IOException e) {
-        throw new RuntimeException();
+        throw new RuntimeException(e);
       }
       return i;
     }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/TestPositionIncrement.java Wed Apr  1 01:10:11 2015
@@ -238,18 +238,20 @@ public class TestPositionIncrement exten
     if (VERBOSE) {
       System.out.println("\ngetPayloadSpans test");
     }
-    Spans pspans = MultiSpansWrapper.wrap(is.getTopReaderContext(), snq);
-    while (pspans.next()) {
-      if (VERBOSE) {
-        System.out.println("doc " + pspans.doc() + ": span " + pspans.start()
-            + " to " + pspans.end());
-      }
-      Collection<byte[]> payloads = pspans.getPayload();
-      sawZero |= pspans.start() == 0;
-      for (byte[] bytes : payloads) {
-        count++;
+    Spans pspans = MultiSpansWrapper.wrap(is.getIndexReader(), snq);
+    while (pspans.nextDoc() != Spans.NO_MORE_DOCS) {
+      while (pspans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
         if (VERBOSE) {
-          System.out.println("  payload: " + new String(bytes, StandardCharsets.UTF_8));
+          System.out.println("doc " + pspans.docID() + ": span " + pspans.startPosition()
+              + " to " + pspans.endPosition());
+        }
+        Collection<byte[]> payloads = pspans.getPayload();
+        sawZero |= pspans.startPosition() == 0;
+        for (byte[] bytes : payloads) {
+          count++;
+          if (VERBOSE) {
+            System.out.println("  payload: " + new String(bytes, StandardCharsets.UTF_8));
+          }
         }
       }
     }
@@ -257,20 +259,20 @@ public class TestPositionIncrement exten
     assertEquals(5, count);
 
     // System.out.println("\ngetSpans test");
-    Spans spans = MultiSpansWrapper.wrap(is.getTopReaderContext(), snq);
+    Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq);
     count = 0;
     sawZero = false;
-    while (spans.next()) {
-      count++;
-      sawZero |= spans.start() == 0;
-      // System.out.println(spans.doc() + " - " + spans.start() + " - " +
-      // spans.end());
+    while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
+      while (spans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
+        count++;
+        sawZero |= spans.startPosition() == 0;
+        // System.out.println(spans.doc() + " - " + spans.start() + " - " +
+        // spans.end());
+      }
     }
     assertEquals(4, count);
     assertTrue(sawZero);
 
-    // System.out.println("\nPayloadSpanUtil test");
-
     sawZero = false;
     PayloadSpanUtil psu = new PayloadSpanUtil(is.getTopReaderContext());
     Collection<byte[]> pls = psu.getPayloadsForQuery(snq);

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/payloads/TestPayloadTermQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/payloads/TestPayloadTermQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/payloads/TestPayloadTermQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/payloads/TestPayloadTermQuery.java Wed Apr  1 01:10:11 2015
@@ -160,7 +160,7 @@ public class TestPayloadTermQuery extend
       assertTrue(doc.score + " does not equal: " + 1, doc.score == 1);
     }
     CheckHits.checkExplanations(query, PayloadHelper.FIELD, searcher, true);
-    Spans spans = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), query);
+    Spans spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), query);
     assertTrue("spans is null and it shouldn't be", spans != null);
     /*float score = hits.score(0);
     for (int i =1; i < hits.length(); i++)
@@ -211,13 +211,15 @@ public class TestPayloadTermQuery extend
     }
     assertTrue(numTens + " does not equal: " + 10, numTens == 10);
     CheckHits.checkExplanations(query, "field", searcher, true);
-    Spans spans = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), query);
+    Spans spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), query);
     assertTrue("spans is null and it shouldn't be", spans != null);
     //should be two matches per document
     int count = 0;
     //100 hits times 2 matches per hit, we should have 200 in count
-    while (spans.next()) {
-      count++;
+    while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
+      while (spans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
+        count++;
+      }
     }
     assertTrue(count + " does not equal: " + 200, count == 200);
   }
@@ -253,13 +255,15 @@ public class TestPayloadTermQuery extend
     }
     assertTrue(numTens + " does not equal: " + 10, numTens == 10);
     CheckHits.checkExplanations(query, "field", searcher, true);
-    Spans spans = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), query);
+    Spans spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), query);
     assertTrue("spans is null and it shouldn't be", spans != null);
     //should be two matches per document
     int count = 0;
     //100 hits times 2 matches per hit, we should have 200 in count
-    while (spans.next()) {
-      count++;
+    while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
+      while (spans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
+        count++;
+      }
     }
     reader.close();
   }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java Wed Apr  1 01:10:11 2015
@@ -24,7 +24,6 @@ import java.util.Map;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
-import org.apache.lucene.search.Weight;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.util.Bits;
 
@@ -42,27 +41,32 @@ final class JustCompileSearchSpans {
   static final class JustCompileSpans extends Spans {
 
     @Override
-    public int doc() {
+    public int docID() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public int end() {
+    public int nextDoc() throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public boolean next() {
+    public int advance(int target) throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
-
+    
     @Override
-    public boolean skipTo(int target) {
+    public int startPosition() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public int start() {
+    public int endPosition() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+    
+    @Override
+    public int nextStartPosition() throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
@@ -104,37 +108,42 @@ final class JustCompileSearchSpans {
   static final class JustCompilePayloadSpans extends Spans {
 
     @Override
-    public Collection<byte[]> getPayload() {
+    public int docID() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public boolean isPayloadAvailable() {
+    public int nextDoc() throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public int doc() {
+    public int advance(int target) throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public int end() {
+    public int startPosition() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public boolean next() {
+    public int endPosition() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
+    
+    @Override
+    public int nextStartPosition() throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public boolean skipTo(int target) {
+    public Collection<byte[]> getPayload() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
     @Override
-    public int start() {
+    public boolean isPayloadAvailable() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
 
@@ -147,7 +156,7 @@ final class JustCompileSearchSpans {
   
   static final class JustCompileSpanScorer extends SpanScorer {
 
-    protected JustCompileSpanScorer(Spans spans, Weight weight,
+    protected JustCompileSpanScorer(Spans spans, SpanWeight weight,
         Similarity.SimScorer docScorer) throws IOException {
       super(spans, weight, docScorer);
     }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java Wed Apr  1 01:10:11 2015
@@ -18,19 +18,18 @@ package org.apache.lucene.search.spans;
  */
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Map;
-import java.util.TreeSet;
 
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.IndexReaderContext;
-import org.apache.lucene.index.ReaderUtil;
+import org.apache.lucene.index.SlowCompositeReaderWrapper;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
-import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.Bits;
 
 /**
  * 
@@ -39,141 +38,20 @@ import org.apache.lucene.search.DocIdSet
  * NOTE: This should be used for testing purposes only
  * @lucene.internal
  */
-public class MultiSpansWrapper extends Spans { // can't be package private due to payloads
+public class MultiSpansWrapper {
 
-  private SpanQuery query;
-  private List<LeafReaderContext> leaves;
-  private int leafOrd = 0;
-  private Spans current;
-  private Map<Term,TermContext> termContexts;
-  private final int numLeaves;
-
-  private MultiSpansWrapper(List<LeafReaderContext> leaves, SpanQuery query, Map<Term,TermContext> termContexts) {
-    this.query = query;
-    this.leaves = leaves;
-    this.numLeaves = leaves.size();
-    this.termContexts = termContexts;
-  }
-  
-  public static Spans wrap(IndexReaderContext topLevelReaderContext, SpanQuery query) throws IOException {
+  public static Spans wrap(IndexReader reader, SpanQuery spanQuery) throws IOException {
+    LeafReader lr = SlowCompositeReaderWrapper.wrap(reader); // slow, but ok for testing
+    LeafReaderContext lrContext = lr.getContext();
+    Query rewrittenQuery = spanQuery.rewrite(lr); // get the term contexts so getSpans can be called directly
+    HashSet<Term> termSet = new HashSet<>();
+    rewrittenQuery.extractTerms(termSet);
     Map<Term,TermContext> termContexts = new HashMap<>();
-    TreeSet<Term> terms = new TreeSet<>();
-    query.extractTerms(terms);
-    for (Term term : terms) {
-      termContexts.put(term, TermContext.build(topLevelReaderContext, term));
-    }
-    final List<LeafReaderContext> leaves = topLevelReaderContext.leaves();
-    if(leaves.size() == 1) {
-      final LeafReaderContext ctx = leaves.get(0);
-      return query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
+    for (Term term: termSet) {
+      TermContext termContext = TermContext.build(lrContext, term);
+      termContexts.put(term, termContext);
     }
-    return new MultiSpansWrapper(leaves, query, termContexts);
+    Spans actSpans = spanQuery.getSpans(lrContext, new Bits.MatchAllBits(lr.numDocs()), termContexts);
+    return actSpans;
   }
-
-  @Override
-  public boolean next() throws IOException {
-    if (leafOrd >= numLeaves) {
-      return false;
-    }
-    if (current == null) {
-      final LeafReaderContext ctx = leaves.get(leafOrd);
-      current = query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
-    }
-    while(true) {
-      if (current.next()) {
-        return true;
-      }
-      if (++leafOrd < numLeaves) {
-        final LeafReaderContext ctx = leaves.get(leafOrd);
-        current = query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
-      } else {
-        current = null;
-        break;
-      }
-    }
-    return false;
-  }
-
-  @Override
-  public boolean skipTo(int target) throws IOException {
-    if (leafOrd >= numLeaves) {
-      return false;
-    }
-
-    int subIndex = ReaderUtil.subIndex(target, leaves);
-    assert subIndex >= leafOrd;
-    if (subIndex != leafOrd) {
-      final LeafReaderContext ctx = leaves.get(subIndex);
-      current = query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
-      leafOrd = subIndex;
-    } else if (current == null) {
-      final LeafReaderContext ctx = leaves.get(leafOrd);
-      current = query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
-    }
-    while (true) {
-      if (target < leaves.get(leafOrd).docBase) {
-        // target was in the previous slice
-        if (current.next()) {
-          return true;
-        }
-      } else if (current.skipTo(target - leaves.get(leafOrd).docBase)) {
-        return true;
-      }
-      if (++leafOrd < numLeaves) {
-        final LeafReaderContext ctx = leaves.get(leafOrd);
-        current = query.getSpans(ctx, ctx.reader().getLiveDocs(), termContexts);
-      } else {
-        current = null;
-        break;
-      }
-    }
-
-    return false;
-  }
-
-  @Override
-  public int doc() {
-    if (current == null) {
-      return DocIdSetIterator.NO_MORE_DOCS;
-    }
-    return current.doc() + leaves.get(leafOrd).docBase;
-  }
-
-  @Override
-  public int start() {
-    if (current == null) {
-      return DocIdSetIterator.NO_MORE_DOCS;
-    }
-    return current.start();
-  }
-
-  @Override
-  public int end() {
-    if (current == null) {
-      return DocIdSetIterator.NO_MORE_DOCS;
-    }
-    return current.end();
-  }
-
-  @Override
-  public Collection<byte[]> getPayload() throws IOException {
-    if (current == null) {
-      return Collections.emptyList();
-    }
-    return current.getPayload();
-  }
-
-  @Override
-  public boolean isPayloadAvailable() throws IOException {
-    if (current == null) {
-      return false;
-    }
-    return current.isPayloadAvailable();
-  }
-
-  @Override
-  public long cost() {
-    return Integer.MAX_VALUE; // just for tests
-  }
-
 }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestBasics.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestBasics.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestBasics.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestBasics.java Wed Apr  1 01:10:11 2015
@@ -651,47 +651,6 @@ public class TestBasics extends LuceneTe
               1746, 1747, 1756, 1757, 1766, 1767, 1776, 1777, 1786, 1787, 1796, 1797});
   }
   
-  @Test
-  public void testSpansSkipTo() throws Exception {
-    SpanTermQuery t1 = new SpanTermQuery(new Term("field", "seventy"));
-    SpanTermQuery t2 = new SpanTermQuery(new Term("field", "seventy"));
-    Spans s1 = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), t1);
-    Spans s2 = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), t2);
-
-    assertTrue(s1.next());
-    assertTrue(s2.next());
-
-    boolean hasMore = true;
-
-    do {
-      hasMore = skipToAccordingToJavaDocs(s1, s1.doc() + 1);
-      assertEquals(hasMore, s2.skipTo(s2.doc() + 1));
-      assertEquals(s1.doc(), s2.doc());
-    } while (hasMore);
-  }
-
-  /** Skips to the first match beyond the current, whose document number is
-   * greater than or equal to <i>target</i>. <p>Returns true iff there is such
-   * a match.  <p>Behaves as if written: <pre>
-   *   boolean skipTo(int target) {
-   *     do {
-   *       if (!next())
-   *       return false;
-   *     } while (target &gt; doc());
-   *     return true;
-   *   }
-   * </pre>
-   */
-  private boolean skipToAccordingToJavaDocs(Spans s, int target)
-      throws Exception {
-    do {
-      if (!s.next())
-        return false;
-    } while (target > s.doc());
-    return true;
-
-  }
-
   private void checkHits(Query query, int[] results) throws IOException {
     CheckHits.checkHits(random(), query, "field", searcher, results);
   }

Modified: lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java?rev=1670533&r1=1670532&r2=1670533&view=diff
==============================================================================
--- lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java (original)
+++ lucene/dev/branches/lucene6271/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java Wed Apr  1 01:10:11 2015
@@ -258,37 +258,19 @@ public class TestFieldMaskingSpanQuery e
     SpanQuery q2 = new SpanTermQuery(new Term("first",  "james"));
     SpanQuery q  = new SpanOrQuery(q1, new FieldMaskingSpanQuery(q2, "gender"));
     check(q, new int[] { 0, 1, 2, 3, 4 });
-  
-    Spans span = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), q);
-    
-    assertEquals(true, span.next());
-    assertEquals(s(0,0,1), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(1,0,1), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(1,1,2), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(2,0,1), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(2,1,2), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(2,2,3), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(3,0,1), s(span));
 
-    assertEquals(true, span.next());
-    assertEquals(s(4,0,1), s(span));
+    Spans span = MultiSpansWrapper.wrap(searcher.getIndexReader(), q);
 
-    assertEquals(true, span.next());
-    assertEquals(s(4,1,2), s(span));
-
-    assertEquals(false, span.next());
+    TestSpans.tstNextSpans(span, 0,0,1);
+    TestSpans.tstNextSpans(span, 1,0,1);
+    TestSpans.tstNextSpans(span, 1,1,2);
+    TestSpans.tstNextSpans(span, 2,0,1);
+    TestSpans.tstNextSpans(span, 2,1,2);
+    TestSpans.tstNextSpans(span, 2,2,3);
+    TestSpans.tstNextSpans(span, 3,0,1);
+    TestSpans.tstNextSpans(span, 4,0,1);
+    TestSpans.tstNextSpans(span, 4,1,2);
+    TestSpans.tstEndSpans(span);
   }
   
   public void testSpans1() throws Exception {
@@ -300,19 +282,22 @@ public class TestFieldMaskingSpanQuery e
     check(qA, new int[] { 0, 1, 2, 4 });
     check(qB, new int[] { 0, 1, 2, 4 });
   
-    Spans spanA = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), qA);
-    Spans spanB = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), qB);
+    Spans spanA = MultiSpansWrapper.wrap(searcher.getIndexReader(), qA);
+    Spans spanB = MultiSpansWrapper.wrap(searcher.getIndexReader(), qB);
     
-    while (spanA.next()) {
-      assertTrue("spanB not still going", spanB.next());
-      assertEquals("spanA not equal spanB", s(spanA), s(spanB));
+    while (spanA.nextDoc() != Spans.NO_MORE_DOCS) {
+      assertNotSame("spanB not still going", Spans.NO_MORE_DOCS, spanB.nextDoc());
+      while (spanA.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
+        assertEquals("spanB start position", spanA.startPosition(), spanB.nextStartPosition());
+        assertEquals("spanB end position", spanA.endPosition(), spanB.endPosition());
+      }
+      assertEquals("spanB start position", Spans.NO_MORE_POSITIONS, spanB.nextStartPosition());
     }
-    assertTrue("spanB still going even tough spanA is done", !(spanB.next()));
-
+    assertEquals("spanB end doc", Spans.NO_MORE_DOCS, spanB.nextDoc());
   }
   
   public void testSpans2() throws Exception {
-    assumeTrue("Broken scoring: LUCENE-3723", 
+    assumeTrue("Broken scoring: LUCENE-3723",
         searcher.getSimilarity() instanceof TFIDFSimilarity);
     SpanQuery qA1 = new SpanTermQuery(new Term("gender", "female"));
     SpanQuery qA2 = new SpanTermQuery(new Term("first",  "james"));
@@ -322,30 +307,17 @@ public class TestFieldMaskingSpanQuery e
       { new FieldMaskingSpanQuery(qA, "id"),
         new FieldMaskingSpanQuery(qB, "id") }, -1, false );
     check(q, new int[] { 0, 1, 2, 3 });
-  
-    Spans span = MultiSpansWrapper.wrap(searcher.getTopReaderContext(), q);
-    
-    assertEquals(true, span.next());
-    assertEquals(s(0,0,1), s(span));
 
-    assertEquals(true, span.next());
-    assertEquals(s(1,1,2), s(span));
+    Spans span = MultiSpansWrapper.wrap(searcher.getIndexReader(), q);
 
-    assertEquals(true, span.next());
-    assertEquals(s(2,0,1), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(2,2,3), s(span));
-
-    assertEquals(true, span.next());
-    assertEquals(s(3,0,1), s(span));
-
-    assertEquals(false, span.next());
+    TestSpans.tstNextSpans(span, 0,0,1);
+    TestSpans.tstNextSpans(span, 1,1,2);
+    TestSpans.tstNextSpans(span, 2,0,1);
+    TestSpans.tstNextSpans(span, 2,2,3);
+    TestSpans.tstNextSpans(span, 3,0,1);
+    TestSpans.tstEndSpans(span);
   }
   
-  public String s(Spans span) {
-    return s(span.doc(), span.start(), span.end());
-  }
   public String s(int doc, int start, int end) {
     return "s(" + doc + "," + start + "," + end +")";
   }