Index: src/test/org/apache/lucene/index/TestIndexWriter.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexWriter.java (revision 641983) +++ src/test/org/apache/lucene/index/TestIndexWriter.java (working copy) @@ -30,6 +30,7 @@ import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.analysis.WhitespaceTokenizer; import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.SinkTokenizer; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.standard.StandardAnalyzer; @@ -40,6 +41,9 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Hits; import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.RAMDirectory; @@ -3549,4 +3553,46 @@ assertEquals(expected[i], utf16a.result[i]); } } + + // LUCENE-1255 + public void testNegativePositions() throws Throwable { + SinkTokenizer tokens = new SinkTokenizer(); + Token t = new Token(); + t.setTermText("a"); + t.setPositionIncrement(0); + tokens.add(t); + t.setTermText("b"); + t.setPositionIncrement(1); + tokens.add(t); + t.setTermText("c"); + tokens.add(t); + + MockRAMDirectory dir = new MockRAMDirectory(); + IndexWriter w = new IndexWriter(dir, false, new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.UNLIMITED); + Document doc = new Document(); + doc.add(new Field("field", tokens)); + w.addDocument(doc); + w.commit(); + + IndexSearcher s = new IndexSearcher(dir); + PhraseQuery pq = new PhraseQuery(); + pq.add(new Term("field", "a")); + pq.add(new Term("field", "b")); + pq.add(new Term("field", "c")); + Hits hits = s.search(pq); + assertEquals(1, hits.length()); + + Query q = new SpanTermQuery(new Term("field", "a")); + hits = s.search(q); + assertEquals(1, hits.length()); + TermPositions tps = s.getIndexReader().termPositions(new Term("field", "a")); + assertTrue(tps.next()); + assertEquals(1, tps.freq()); + assertEquals(0, tps.nextPosition()); + w.close(); + + assertTrue(_TestUtil.checkIndex(dir)); + s.close(); + dir.close(); + } } Index: src/java/org/apache/lucene/index/DocumentsWriterFieldData.java =================================================================== --- src/java/org/apache/lucene/index/DocumentsWriterFieldData.java (revision 642934) +++ src/java/org/apache/lucene/index/DocumentsWriterFieldData.java (working copy) @@ -239,11 +239,13 @@ try { offsetEnd = offset-1; - Token token; for(;;) { - token = stream.next(localToken); + Token token = stream.next(localToken); if (token == null) break; position += (token.getPositionIncrement() - 1); + // LUCENE-1255: don't allow negative positon + if (position < 0) + position = 0; addPosition(token); if (++length >= maxFieldLength) { if (threadState.docWriter.infoStream != null) Index: src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- src/java/org/apache/lucene/index/IndexWriter.java (revision 641983) +++ src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -1692,8 +1692,11 @@ throw oom; } finally { synchronized(this) { - if (!closed) + if (!closed) { closing = false; + if (infoStream != null) + message("hit exception while closing"); + } notifyAll(); } } Index: src/java/org/apache/lucene/index/CheckIndex.java =================================================================== --- src/java/org/apache/lucene/index/CheckIndex.java (revision 641983) +++ src/java/org/apache/lucene/index/CheckIndex.java (working copy) @@ -223,7 +223,7 @@ final int pos = termPositions.nextPosition(); if (pos < 0) throw new RuntimeException("term " + term + ": doc " + doc + ": pos " + pos + " is out of bounds"); - if (pos <= lastPos) + if (pos < lastPos) throw new RuntimeException("term " + term + ": doc " + doc + ": pos " + pos + " < lastPos " + lastPos); } }