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/02/17 21:21:55 UTC

svn commit: r1660489 - in /lucene/dev/branches/branch_5x/lucene/core/src: java/org/apache/lucene/index/ test/org/apache/lucene/index/

Author: rmuir
Date: Tue Feb 17 20:21:54 2015
New Revision: 1660489

URL: http://svn.apache.org/r1660489
Log:
LUCENE-6246: add backwards layer

Added:
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java   (with props)
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java   (with props)
    lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestLegacyPostings.java   (with props)
Modified:
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/LeafReader.java
    lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java

Added: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java?rev=1660489&view=auto
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java (added)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsAndPositionsEnum.java Tue Feb 17 20:21:54 2015
@@ -0,0 +1,148 @@
+package org.apache.lucene.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.util.Bits; // javadocs
+import org.apache.lucene.util.BytesRef;
+
+/** 
+ * Also iterates through positions. 
+ * @deprecated Use {@link PostingsEnum} instead.
+ */
+@Deprecated
+public abstract class DocsAndPositionsEnum extends DocsEnum {
+  
+  /** Flag to pass to {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)}
+   *  if you require offsets in the returned enum. */
+  public static final int FLAG_OFFSETS = 0x1;
+
+  /** Flag to pass to  {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)}
+   *  if you require payloads in the returned enum. */
+  public static final int FLAG_PAYLOADS = 0x2;
+
+  /** Sole constructor. (For invocation by subclass 
+   * constructors, typically implicit.) */
+  protected DocsAndPositionsEnum() {
+  }
+
+  /** Returns the next position.  You should only call this
+   *  up to {@link DocsEnum#freq()} times else
+   *  the behavior is not defined.  If positions were not
+   *  indexed this will return -1; this only happens if
+   *  offsets were indexed and you passed needsOffset=true
+   *  when pulling the enum.  */
+  public abstract int nextPosition() throws IOException;
+
+  /** Returns start offset for the current position, or -1
+   *  if offsets were not indexed. */
+  public abstract int startOffset() throws IOException;
+
+  /** Returns end offset for the current position, or -1 if
+   *  offsets were not indexed. */
+  public abstract int endOffset() throws IOException;
+
+  /** Returns the payload at this position, or null if no
+   *  payload was indexed. You should not modify anything 
+   *  (neither members of the returned BytesRef nor bytes 
+   *  in the byte[]). */
+  public abstract BytesRef getPayload() throws IOException;
+  
+  /** 
+   * Wraps a PostingsEnum with a legacy DocsAndPositionsEnum.
+   */
+  static DocsAndPositionsEnum wrap(final PostingsEnum postings) {
+    return new DocsAndPositionsEnumWrapper(postings);
+  }
+  
+  /**
+   * Unwrap a legacy DocsAndPositionsEnum and return the actual PostingsEnum.
+   * if {@code docs} is null, this returns null for convenience
+   */
+  static PostingsEnum unwrap(final DocsEnum docs) {
+    if (docs instanceof DocsAndPositionsEnumWrapper) {
+      return ((DocsAndPositionsEnumWrapper)docs).in;
+    } else if (docs == null) {
+      return null; // e.g. user is not reusing
+    } else {
+      throw new AssertionError();
+    }
+  }
+  
+  static class DocsAndPositionsEnumWrapper extends DocsAndPositionsEnum {
+    final PostingsEnum in;
+    
+    DocsAndPositionsEnumWrapper(PostingsEnum in) {
+      this.in = Objects.requireNonNull(in);
+    }
+
+    @Override
+    public int nextPosition() throws IOException {
+      return in.nextPosition();
+    }
+
+    @Override
+    public int startOffset() throws IOException {
+      return in.startOffset();
+    }
+
+    @Override
+    public int endOffset() throws IOException {
+      return in.endOffset();
+    }
+
+    @Override
+    public BytesRef getPayload() throws IOException {
+      return in.getPayload();
+    }
+
+    @Override
+    public int freq() throws IOException {
+      return in.freq();
+    }
+
+    @Override
+    public AttributeSource attributes() {
+      return in.attributes();
+    }
+
+    @Override
+    public int docID() {
+      return in.docID();
+    }
+
+    @Override
+    public int nextDoc() throws IOException {
+      return in.nextDoc();
+    }
+
+    @Override
+    public int advance(int target) throws IOException {
+      return in.advance(target);
+    }
+
+    @Override
+    public long cost() {
+      return in.cost();
+    }
+  }
+}
+

Added: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java?rev=1660489&view=auto
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java (added)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/DocsEnum.java Tue Feb 17 20:21:54 2015
@@ -0,0 +1,70 @@
+package org.apache.lucene.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+
+/** 
+ * Iterates through the documents and term freqs.
+ * NOTE: you must first call {@link #nextDoc} before using
+ * any of the per-doc methods. 
+ * @deprecated Use {@link PostingsEnum} instead.
+ */
+@Deprecated
+public abstract class DocsEnum extends PostingsEnum {
+  
+  /**
+   * Flag to pass to {@link TermsEnum#docs(Bits,DocsEnum,int)} if you don't
+   * require term frequencies in the returned enum. When passed to
+   * {@link TermsEnum#docsAndPositions(Bits,DocsAndPositionsEnum,int)} means
+   * that no offsets and payloads will be returned.
+   */
+  public static final int FLAG_NONE = 0x0;
+
+  /** Flag to pass to {@link TermsEnum#docs(Bits,DocsEnum,int)}
+   *  if you require term frequencies in the returned enum. */
+  public static final int FLAG_FREQS = 0x1;
+
+  /** Sole constructor. (For invocation by subclass 
+   *  constructors, typically implicit.) */
+  protected DocsEnum() {
+  }
+
+  @Override
+  public int nextPosition() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public int startOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public int endOffset() throws IOException {
+    return -1;
+  }
+
+  @Override
+  public BytesRef getPayload() throws IOException {
+    return null;
+  }
+}

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/LeafReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/LeafReader.java?rev=1660489&r1=1660488&r2=1660489&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/LeafReader.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/LeafReader.java Tue Feb 17 20:21:54 2015
@@ -297,4 +297,40 @@ public abstract class LeafReader extends
    * @lucene.internal
    */
   public abstract void checkIntegrity() throws IOException;
+  
+  /** Returns {@link DocsEnum} for the specified term.
+   *  This will return null if either the field or
+   *  term does not exist.
+   *  @deprecated use {@link #postings(Term)} instead */
+  @Deprecated
+  public final DocsEnum termDocsEnum(Term term) throws IOException {
+    assert term.field() != null;
+    assert term.bytes() != null;
+    final Terms terms = terms(term.field());
+    if (terms != null) {
+      final TermsEnum termsEnum = terms.iterator(null);
+      if (termsEnum.seekExact(term.bytes())) {
+        return termsEnum.docs(getLiveDocs(), null);
+      }
+    }
+    return null;
+  }
+
+  /** Returns {@link DocsAndPositionsEnum} for the specified
+   *  term.  This will return null if the
+   *  field or term does not exist or positions weren't indexed.
+   *  @deprecated use {@link #postings(Term, int)} instead */
+  @Deprecated
+  public final DocsAndPositionsEnum termPositionsEnum(Term term) throws IOException {
+    assert term.field() != null;
+    assert term.bytes() != null;
+    final Terms terms = terms(term.field());
+    if (terms != null) {
+      final TermsEnum termsEnum = terms.iterator(null);
+      if (termsEnum.seekExact(term.bytes())) {
+        return termsEnum.docsAndPositions(getLiveDocs(), null);
+      }
+    }
+    return null;
+  }
 }

Modified: lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java?rev=1660489&r1=1660488&r2=1660489&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java (original)
+++ lucene/dev/branches/branch_5x/lucene/core/src/java/org/apache/lucene/index/TermsEnum.java Tue Feb 17 20:21:54 2015
@@ -243,4 +243,104 @@ public abstract class TermsEnum implemen
     }
 
   };
+  
+  /** Get {@link DocsEnum} for the current term.  Do not
+   *  call this when the enum is unpositioned.  This method
+   *  will not return null.
+   *  
+   * @param liveDocs unset bits are documents that should not
+   * be returned
+   * @param reuse pass a prior DocsEnum for possible reuse 
+   * @deprecated Use {@link #postings(Bits, PostingsEnum)} instead */
+  @Deprecated
+  public final DocsEnum docs(Bits liveDocs, DocsEnum reuse) throws IOException {
+    return docs(liveDocs, reuse, DocsEnum.FLAG_FREQS);
+  }
+
+  /** Get {@link DocsEnum} for the current term, with
+   *  control over whether freqs are required.  Do not
+   *  call this when the enum is unpositioned.  This method
+   *  will not return null.
+   *  
+   * @param liveDocs unset bits are documents that should not
+   * be returned
+   * @param reuse pass a prior DocsEnum for possible reuse
+   * @param flags specifies which optional per-document values
+   *        you require; see {@link DocsEnum#FLAG_FREQS} 
+   * @see #docs(Bits, DocsEnum, int) 
+   * @deprecated Use {@link #postings(Bits, PostingsEnum, int)} instead */
+  @Deprecated
+  public final DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+    final int newFlags;
+    if (flags == DocsEnum.FLAG_FREQS) {
+      newFlags = PostingsEnum.FREQS;
+    } else if (flags == DocsEnum.FLAG_NONE) {
+      newFlags = PostingsEnum.NONE;
+    } else {
+      throw new IllegalArgumentException("Invalid legacy docs flags: " + flags);
+    }
+    PostingsEnum actualReuse = DocsAndPositionsEnum.unwrap(reuse);
+    PostingsEnum postings = postings(liveDocs, actualReuse, newFlags);
+    if (postings == null) {
+      throw new AssertionError();
+    } else if (postings == actualReuse) {
+      return reuse;
+    } else {
+      return DocsAndPositionsEnum.wrap(postings);
+    }
+  };
+
+  /** Get {@link DocsAndPositionsEnum} for the current term.
+   *  Do not call this when the enum is unpositioned.  This
+   *  method will return null if positions were not
+   *  indexed.
+   *  
+   *  @param liveDocs unset bits are documents that should not
+   *  be returned
+   *  @param reuse pass a prior DocsAndPositionsEnum for possible reuse
+   *  @see #docsAndPositions(Bits, DocsAndPositionsEnum, int)
+   *  @deprecated Use {@link #postings(Bits, PostingsEnum, int)} instead */
+  @Deprecated
+  public final DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse) throws IOException {
+    return docsAndPositions(liveDocs, reuse, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+  }
+
+  /** Get {@link DocsAndPositionsEnum} for the current term,
+   *  with control over whether offsets and payloads are
+   *  required.  Some codecs may be able to optimize their
+   *  implementation when offsets and/or payloads are not required.
+   *  Do not call this when the enum is unpositioned.  This
+   *  will return null if positions were not indexed.
+
+   *  @param liveDocs unset bits are documents that should not
+   *  be returned
+   *  @param reuse pass a prior DocsAndPositionsEnum for possible reuse
+   *  @param flags specifies which optional per-position values you
+   *         require; see {@link DocsAndPositionsEnum#FLAG_OFFSETS} and 
+   *         {@link DocsAndPositionsEnum#FLAG_PAYLOADS}. 
+   *  @deprecated Use {@link #postings(Bits, PostingsEnum, int)} instead */
+  @Deprecated
+  public final DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException {
+    final int newFlags;
+    if (flags == (DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS)) {
+      newFlags = PostingsEnum.OFFSETS | PostingsEnum.PAYLOADS; 
+    } else if (flags == DocsAndPositionsEnum.FLAG_OFFSETS) {
+      newFlags = PostingsEnum.OFFSETS;
+    } else if (flags == DocsAndPositionsEnum.FLAG_PAYLOADS) {
+      newFlags = PostingsEnum.PAYLOADS;
+    } else if (flags == DocsAndPositionsEnum.FLAG_NONE) {
+      newFlags = PostingsEnum.POSITIONS;
+    } else {
+      throw new IllegalArgumentException("Invalid legacy docsAndPositions flags: " + flags);
+    }
+    PostingsEnum actualReuse = DocsAndPositionsEnum.unwrap(reuse);
+    PostingsEnum postings = postings(liveDocs, actualReuse, newFlags);
+    if (postings == null) {
+      return null; // if no positions were indexed
+    } else if (postings == actualReuse) {
+      return reuse;
+    } else {
+      return DocsAndPositionsEnum.wrap(postings);
+    }
+  }
 }

Added: lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestLegacyPostings.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestLegacyPostings.java?rev=1660489&view=auto
==============================================================================
--- lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestLegacyPostings.java (added)
+++ lucene/dev/branches/branch_5x/lucene/core/src/test/org/apache/lucene/index/TestLegacyPostings.java Tue Feb 17 20:21:54 2015
@@ -0,0 +1,933 @@
+package org.apache.lucene.index;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.CannedTokenStream;
+import org.apache.lucene.analysis.MockTokenizer;
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
+
+/** 
+ * Test old postings api (DocsEnum, DocsAndPositionsEnum, etc)
+ * @deprecated only for testing backwards compat cruft
+ */
+@SuppressCodecs("Direct") // Direct does not support reuse, but we test that it works...
+@Deprecated
+public class TestLegacyPostings extends LuceneTestCase {
+  
+  public void testDocsOnly() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(null);
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    doc.add(new StringField("foo", "bar", Field.Store.NO));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(1, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(1, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    assertEquals(1, docsOnly.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    assertEquals(1, docsOnly2.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // sugar method: we did not index positions
+    DocsAndPositionsEnum docsAndPositionsEnum = getOnlySegmentReader(reader).termPositionsEnum(new Term("foo", "bar"));
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNull(docsAndPositionsEnum);
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+  
+  public void testFreqs() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(new Analyzer() {
+      @Override
+      protected TokenStreamComponents createComponents(String fieldName) {
+        return new TokenStreamComponents(new MockTokenizer());
+      }
+    });
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    FieldType ft = new FieldType(TextField.TYPE_NOT_STORED);
+    ft.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
+    doc.add(new Field("foo", "bar bar", ft));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // sugar method: we did not index positions
+    DocsAndPositionsEnum docsAndPositionsEnum = getOnlySegmentReader(reader).termPositionsEnum(new Term("foo", "bar"));
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNull(docsAndPositionsEnum);
+    
+    // we did not index positions
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNull(docsAndPositionsEnum);
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+  
+  public void testPositions() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(new Analyzer() {
+      @Override
+      protected TokenStreamComponents createComponents(String fieldName) {
+        return new TokenStreamComponents(new MockTokenizer());
+      }
+    });
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    doc.add(new TextField("foo", "bar bar", Field.Store.NO));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // asking for positions, ok
+    DocsAndPositionsEnum docsAndPositionsEnum = getOnlySegmentReader(reader).termPositionsEnum(new Term("foo", "bar"));
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // now reuse the positions
+    DocsAndPositionsEnum docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // payloads, offsets, etc don't cause an error if they aren't there
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    // but make sure they work
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_PAYLOADS | DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+  
+  public void testOffsets() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(new Analyzer() {
+      @Override
+      protected TokenStreamComponents createComponents(String fieldName) {
+        return new TokenStreamComponents(new MockTokenizer());
+      }
+    });
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    FieldType ft = new FieldType(TextField.TYPE_NOT_STORED);
+    ft.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
+    doc.add(new Field("foo", "bar bar", ft));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // asking for positions, ok
+    DocsAndPositionsEnum docsAndPositionsEnum = getOnlySegmentReader(reader).termPositionsEnum(new Term("foo", "bar"));
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // now reuse the positions
+    DocsAndPositionsEnum docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // payloads don't cause an error if they aren't there
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    // but make sure they work
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertNull(docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+  
+  public void testPayloads() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(null);
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    Token token1 = new Token("bar", 0, 3);
+    token1.setPayload(new BytesRef("pay1"));
+    Token token2 = new Token("bar", 4, 7);
+    token2.setPayload(new BytesRef("pay2"));
+    doc.add(new TextField("foo", new CannedTokenStream(token1, token2)));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // asking for positions, ok
+    DocsAndPositionsEnum docsAndPositionsEnum = termsEnum.docsAndPositions(null, null);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // now reuse the positions
+    DocsAndPositionsEnum docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // payloads
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(-1, docsAndPositionsEnum.startOffset());
+    assertEquals(-1, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+  
+  public void testAll() throws Exception {
+    Directory dir = newDirectory();
+    IndexWriterConfig iwc = new IndexWriterConfig(null);
+    IndexWriter iw = new IndexWriter(dir, iwc);
+    Document doc = new Document();
+    Token token1 = new Token("bar", 0, 3);
+    token1.setPayload(new BytesRef("pay1"));
+    Token token2 = new Token("bar", 4, 7);
+    token2.setPayload(new BytesRef("pay2"));
+    FieldType ft = new FieldType(TextField.TYPE_NOT_STORED);
+    ft.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
+    doc.add(new Field("foo", new CannedTokenStream(token1, token2), ft));
+    iw.addDocument(doc);
+    DirectoryReader reader = DirectoryReader.open(iw, false);
+    
+    // sugar method (FREQS)
+    DocsEnum postings = getOnlySegmentReader(reader).termDocsEnum(new Term("foo", "bar"));
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // termsenum reuse (FREQS)
+    TermsEnum termsEnum = getOnlySegmentReader(reader).terms("foo").iterator(null);
+    termsEnum.seekExact(new BytesRef("bar"));
+    DocsEnum postings2 = termsEnum.docs(null, postings);
+    assertNotNull(postings2);
+    assertSame(postings, postings2);
+    // and it had better work
+    assertEquals(-1, postings.docID());
+    assertEquals(0, postings.nextDoc());
+    assertEquals(2, postings.freq());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, postings.nextDoc());
+    
+    // asking for docs only: ok
+    DocsEnum docsOnly = termsEnum.docs(null, null, DocsEnum.FLAG_NONE);
+    assertEquals(-1, docsOnly.docID());
+    assertEquals(0, docsOnly.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly.nextDoc());
+    // reuse that too
+    DocsEnum docsOnly2 = termsEnum.docs(null, docsOnly, DocsEnum.FLAG_NONE);
+    assertNotNull(docsOnly2);
+    assertSame(docsOnly, docsOnly2);
+    // and it had better work
+    assertEquals(-1, docsOnly2.docID());
+    assertEquals(0, docsOnly2.nextDoc());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsOnly.freq() == 1 || docsOnly.freq() == 2);
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsOnly2.nextDoc());
+    
+    // asking for positions, ok
+    DocsAndPositionsEnum docsAndPositionsEnum = getOnlySegmentReader(reader).termPositionsEnum(new Term("foo", "bar"));
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // now reuse the positions
+    DocsAndPositionsEnum docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    // payloads
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 0);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 3);
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.startOffset() == -1 || docsAndPositionsEnum.startOffset() == 4);
+    assertTrue(docsAndPositionsEnum.endOffset() == -1 || docsAndPositionsEnum.endOffset() == 7);
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    // reuse
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay1").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    // we don't define what it is, but if its something else, we should look into it?
+    assertTrue(docsAndPositionsEnum.getPayload() == null || new BytesRef("pay2").equals(docsAndPositionsEnum.getPayload()));
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    docsAndPositionsEnum = termsEnum.docsAndPositions(null, null, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertNotNull(docsAndPositionsEnum);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    docsAndPositionsEnum2 = termsEnum.docsAndPositions(null, docsAndPositionsEnum, DocsAndPositionsEnum.FLAG_OFFSETS | DocsAndPositionsEnum.FLAG_PAYLOADS);
+    assertSame(docsAndPositionsEnum, docsAndPositionsEnum2);
+    assertEquals(-1, docsAndPositionsEnum.docID());
+    assertEquals(0, docsAndPositionsEnum.nextDoc());
+    assertEquals(2, docsAndPositionsEnum.freq());
+    assertEquals(0, docsAndPositionsEnum.nextPosition());
+    assertEquals(0, docsAndPositionsEnum.startOffset());
+    assertEquals(3, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay1"), docsAndPositionsEnum.getPayload());
+    assertEquals(1, docsAndPositionsEnum.nextPosition());
+    assertEquals(4, docsAndPositionsEnum.startOffset());
+    assertEquals(7, docsAndPositionsEnum.endOffset());
+    assertEquals(new BytesRef("pay2"), docsAndPositionsEnum.getPayload());
+    assertEquals(DocIdSetIterator.NO_MORE_DOCS, docsAndPositionsEnum.nextDoc());
+    
+    iw.close();
+    reader.close();
+    dir.close();
+  }
+}