Index: contrib/analyzers/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java
===================================================================
--- contrib/analyzers/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java	(revision 688745)
+++ contrib/analyzers/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java	(working copy)
@@ -66,11 +66,19 @@
    */
   private boolean outputUnigrams = true;
 
+   /**
+    * Sometimes you may want to make sure to output a single unigram in case
+    * there are no shingles. */
+  private boolean outputUnigramIfNoNgrams = false;
+
   /**
    * maximum shingle size (number of tokens)
    */
   private int maxShingleSize;
 
+  private Token mostRecentInputToken = null;
+  private boolean returnedAnyTokensYet = false;
+
   /**
    * Constructs a ShingleFilter with the specified single size from the
    * TokenStream <code>input</code>
@@ -125,6 +133,18 @@
   }
 
   /**
+   * If the input stream is only one token, should we output a unigram,
+   * regardless of the setting of outputUnigrams? (default: false.)
+   *
+   * @param outputUnigramIfNoNgrams Whether or not the output stream shall contain
+   * a unigram if the input stream contains only one input token
+   */
+  public void setOutputUnigramIfNoNgrams(boolean outputUnigramIfNoNgrams) {
+    this.outputUnigramIfNoNgrams = outputUnigramIfNoNgrams;
+  }
+
+
+  /**
    * Set the max shingle size (default: 2)
    *
    * @param maxShingleSize max size of output shingles
@@ -157,12 +177,20 @@
     if (outputBuf.isEmpty()) {
       fillOutputBuf(reusableToken);
     }
-    Token nextToken = null;
     if ( ! outputBuf.isEmpty())
     {
-      nextToken = (Token)outputBuf.remove(0);
+      returnedAnyTokensYet = true;
+      return (Token)outputBuf.remove(0);
     }
-    return nextToken;
+    else if (outputUnigramIfNoNgrams && !returnedAnyTokensYet && mostRecentInputToken != null)
+    {
+      returnedAnyTokensYet = true;
+      return mostRecentInputToken;
+    }
+    else
+    {
+      return null;
+    }
   }
 
   /**
@@ -186,6 +214,7 @@
           tokenBuf.add(fillerToken);
         }
         tokenBuf.add(nextToken.clone());
+        mostRecentInputToken = (Token)nextToken.clone(); // We probably don't really need to clone twice...
         return getNextToken(nextToken);
       } else {
         return null;
Index: contrib/analyzers/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java
===================================================================
--- contrib/analyzers/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java	(revision 688745)
+++ contrib/analyzers/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java	(working copy)
@@ -101,6 +101,22 @@
     1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
   };
 
+  public static final Token[] BI_GRAM_TOKENS_WITHOUT_UNIGRAMS = new Token[] {
+    createToken("please divide", 0, 13),
+    createToken("divide this", 7, 18),
+    createToken("this sentence", 14, 27),
+    createToken("sentence into", 19, 32),
+    createToken("into shingles", 28, 39),
+  };
+
+  public static final int[] BI_GRAM_POSITION_INCREMENTS_WITHOUT_UNIGRAMS = new int[] {
+    1, 1, 1, 1, 1, 1
+  };
+
+  public static final String[] BI_GRAM_TYPES_WITHOUT_UNIGRAMS = new String[] {
+    "shingle", "shingle", "shingle", "shingle", "shingle"
+  };
+
   public static final Token[] TRI_GRAM_TOKENS = new Token[] {
     createToken("please", 0, 6),
     createToken("please divide", 0, 13),
@@ -132,7 +148,22 @@
     "word"
   };
 
+  public static final Token[] TEST_SINGLE_TOKEN = new Token[] {
+    createToken("please", 0, 6)
+  };
 
+  public static final Token[] SINGLE_TOKEN = new Token[] {
+    createToken("please", 0, 6)
+  };
+
+  public static final int[] SINGLE_TOKEN_INCREMENTS = new int[] {
+    1
+  };
+
+  public static final String[] SINGLE_TOKEN_TYPES = new String[] {
+    "word"
+  };
+
   protected void setUp() throws Exception {
     super.setUp();
     testTokenWithHoles = new Token[] {
@@ -151,24 +182,44 @@
    */
   public void testBiGramFilter() throws IOException {
     this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS,
-                           BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES);
+                           BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES, false);
   }
 
   public void testBiGramFilterWithHoles() throws IOException {
     this.shingleFilterTest(2, testTokenWithHoles, BI_GRAM_TOKENS_WITH_HOLES,
-                           BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES);
+                           BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES, false);
   }
 
+  public void testBiGramFilterWithJustOneToken() throws IOException {
+    this.shingleFilterTest(2, TEST_SINGLE_TOKEN, SINGLE_TOKEN,
+                           SINGLE_TOKEN_INCREMENTS, SINGLE_TOKEN_TYPES, false);
+  }
+
+  public void testBiGramFilterWithOutputUnigramIfNoNgramsOption() throws IOException {
+    this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS_WITHOUT_UNIGRAMS,
+                           BI_GRAM_POSITION_INCREMENTS_WITHOUT_UNIGRAMS,
+                           BI_GRAM_TYPES_WITHOUT_UNIGRAMS,
+                           true);
+  }
+
+  public void testBiGramFilterWithOutputUnigramIfNoNgramsOptionAndJustOneToken() throws IOException {
+    this.shingleFilterTest(2, TEST_SINGLE_TOKEN, SINGLE_TOKEN,
+                           SINGLE_TOKEN_INCREMENTS, SINGLE_TOKEN_TYPES, true);
+  }
+
   public void testTriGramFilter() throws IOException {
     this.shingleFilterTest(3, TEST_TOKEN, TRI_GRAM_TOKENS,
-                           TRI_GRAM_POSITION_INCREMENTS, TRI_GRAM_TYPES);
+                           TRI_GRAM_POSITION_INCREMENTS, TRI_GRAM_TYPES, false);
   }
 
   protected void shingleFilterTest(int maxSize, Token[] tokensToShingle, Token[] tokensToCompare,
-                                   int[] positionIncrements, String[] types)
+                                   int[] positionIncrements, String[] types, boolean outputUnigramIfNoNgramsOption)
     throws IOException {
 
-    TokenStream filter = new ShingleFilter(new TestTokenStream(tokensToShingle), maxSize);
+    ShingleFilter filter = new ShingleFilter(new TestTokenStream(tokensToShingle), maxSize);
+    filter.setOutputUnigramIfNoNgrams(outputUnigramIfNoNgramsOption);
+    filter.setOutputUnigrams(!outputUnigramIfNoNgramsOption);
+
     int i = 0;
     final Token reusableToken = new Token();
     for (Token nextToken = filter.next(reusableToken); nextToken != null; nextToken = filter.next(reusableToken)) {
@@ -184,6 +235,7 @@
       assertEquals("Wrong type for token \"" + termText + "\"", types[i], nextToken.type());
       i++;
     }
+    assertEquals("Wrong number of tokens/shingles", tokensToCompare.length, i);
   }
 
   private static Token createToken(String term, int start, int offset)
