Index: contrib/analyzers/common/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java
===================================================================
--- contrib/analyzers/common/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java	(revision 813015)
+++ contrib/analyzers/common/src/java/org/apache/lucene/analysis/shingle/ShingleFilter.java	(working copy)
@@ -70,6 +70,11 @@
   private boolean outputUnigrams = true;
 
   /**
+   * By default, we don't override behavior of outputUnigrams.
+   */
+  private boolean outputUnigramIfNoNgrams = false;
+
+  /**
    * maximum shingle size (number of tokens)
    */
   private int maxShingleSize;
@@ -132,6 +137,17 @@
   }
 
   /**
+   * Shall we override the behavior of outputUnigrams==false for those
+   * times when no ngrams are available? (default: true.)
+   *
+   * @param outputUnigramIfNoNgrams Whether or not to output a single
+   * unigram when no ngrams are available.
+   */
+  public void setOutputUnigramIfNoNgrams(boolean outputUnigramIfNoNgrams) {
+    this.outputUnigramIfNoNgrams = outputUnigramIfNoNgrams;
+  }
+
+  /**
    * Set the max shingle size (default: 2)
    *
    * @param maxShingleSize max size of output shingles
@@ -160,6 +176,11 @@
   private int shingleBufferPosition;
   private int[] endOffsets;
 
+
+  // for outputUnigramIfNoNgrams:
+  private AttributeSource.State firstToken;
+  boolean returnedAnyTokensYet = false;
+
   /* (non-Javadoc)
    * @see org.apache.lucene.analysis.TokenStream#next()
    */
@@ -167,7 +188,14 @@
     while (true) {
       if (nextToken == null) {
         if (!fillShingleBuffer()) {
-          return false;
+          if (outputUnigramIfNoNgrams && (!returnedAnyTokensYet) && firstToken != null) {
+            restoreState(firstToken);
+            returnedAnyTokensYet = true;
+            return true;
+          }
+          else {
+            return false;
+          }
         }
       }
       
@@ -178,6 +206,7 @@
           restoreState(nextToken);
           posIncrAtt.setPositionIncrement(1);
           shingleBufferPosition++;
+          returnedAnyTokensYet = true;
           return true;
         }
       } else {
@@ -205,6 +234,7 @@
           nextToken = null;
           shingleBufferPosition = 0;
         }
+        returnedAnyTokensYet = true;
         return true;
       } else {
         nextToken = null;
@@ -277,6 +307,9 @@
      */
     do {
       if (getNextToken()) {
+        if (outputUnigramIfNoNgrams && firstToken == null) {
+          firstToken = captureState();
+        }
         shingleBuf.add(captureState());
         if (shingleBuf.size() > maxShingleSize)
         {
@@ -343,10 +376,12 @@
   public void reset() throws IOException {
     super.reset();
     nextToken = null;
+    firstToken = null;
     shingleBufferPosition = 0;
     shingleBuf.clear();
     numFillerTokensToInsert = 0;
     currentToken = null;
     hasCurrentToken = false;
+    returnedAnyTokensYet = false;
   }
 }
Index: contrib/analyzers/common/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java
===================================================================
--- contrib/analyzers/common/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java	(revision 813015)
+++ contrib/analyzers/common/src/test/org/apache/lucene/analysis/shingle/ShingleFilterTest.java	(working copy)
@@ -220,58 +220,83 @@
   public void testBiGramFilter() throws IOException {
     this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS,
                            BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES,
-                           true);
+                           true, false);
   }
 
   public void testBiGramFilterWithHoles() throws IOException {
     this.shingleFilterTest(2, testTokenWithHoles, BI_GRAM_TOKENS_WITH_HOLES,
                            BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES,
-                           true);
+                           true, false);
   }
 
   public void testBiGramFilterWithoutUnigrams() throws IOException {
     this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS_WITHOUT_UNIGRAMS,
                            BI_GRAM_POSITION_INCREMENTS_WITHOUT_UNIGRAMS, BI_GRAM_TYPES_WITHOUT_UNIGRAMS,
-                           false);
+                           false, false);
   }
 
   public void testBiGramFilterWithHolesWithoutUnigrams() throws IOException {
     this.shingleFilterTest(2, testTokenWithHoles, BI_GRAM_TOKENS_WITH_HOLES_WITHOUT_UNIGRAMS,
                            BI_GRAM_POSITION_INCREMENTS_WITH_HOLES_WITHOUT_UNIGRAMS, BI_GRAM_TYPES_WITHOUT_UNIGRAMS,
-                           false);
+                           false, false);
   }
 
   public void testBiGramFilterWithSingleToken() throws IOException {
     this.shingleFilterTest(2, TEST_SINGLE_TOKEN, SINGLE_TOKEN,
                            SINGLE_TOKEN_INCREMENTS, SINGLE_TOKEN_TYPES,
-                           true);
+                           true, false);
   }
 
   public void testBiGramFilterWithSingleTokenWithoutUnigrams() throws IOException {
     this.shingleFilterTest(2, TEST_SINGLE_TOKEN, EMPTY_TOKEN_ARRAY,
                            EMPTY_TOKEN_INCREMENTS_ARRAY, EMPTY_TOKEN_TYPES_ARRAY,
-                           false);
+                           false, false);
   }
 
   public void testBiGramFilterWithEmptyTokenStream() throws IOException {
     this.shingleFilterTest(2, EMPTY_TOKEN_ARRAY, EMPTY_TOKEN_ARRAY,
                            EMPTY_TOKEN_INCREMENTS_ARRAY, EMPTY_TOKEN_TYPES_ARRAY,
-                           true);
+                           true, false);
   }
 
   public void testBiGramFilterWithEmptyTokenStreamWithoutUnigrams() throws IOException {
     this.shingleFilterTest(2, EMPTY_TOKEN_ARRAY, EMPTY_TOKEN_ARRAY,
                            EMPTY_TOKEN_INCREMENTS_ARRAY, EMPTY_TOKEN_TYPES_ARRAY,
-                           false);
+                           false, false);
   }
 
   public void testTriGramFilter() throws IOException {
     this.shingleFilterTest(3, TEST_TOKEN, TRI_GRAM_TOKENS,
                            TRI_GRAM_POSITION_INCREMENTS, TRI_GRAM_TYPES,
-                           true);
+                           true, false);
   }
 
+  // testing outputUnigramIfNoNgram...
+ 
+  public void testOutputNgramIfNoUnigramsSingleTokenCase() throws IOException {
+    // Single token input with outputUnigrams==false is the primary case where
+    // enabling this option should alter program behavior.
+    this.shingleFilterTest(2, TEST_SINGLE_TOKEN, SINGLE_TOKEN,
+                           SINGLE_TOKEN_INCREMENTS, SINGLE_TOKEN_TYPES,
+                           false, true);
+  }
 
+  public void testOutputNgramIfNoNgramsWithSimpleBigram() throws IOException {
+    // Here we expect the same result as with testBiGramFilter().
+    this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS,
+                           BI_GRAM_POSITION_INCREMENTS, BI_GRAM_TYPES,
+                           true, true);
+  }
+
+  public void testOutputNgramIfNoNgramsWithSimpleUnigramlessBigram() throws IOException {
+    // Here we expect the same result as with testBiGramFilterWithoutUnigrams().
+    this.shingleFilterTest(2, TEST_TOKEN, BI_GRAM_TOKENS_WITHOUT_UNIGRAMS,
+                           BI_GRAM_POSITION_INCREMENTS_WITHOUT_UNIGRAMS, BI_GRAM_TYPES_WITHOUT_UNIGRAMS,
+                           false, true);
+  }
+
+
+
   
   public void testReset() throws Exception {
     Tokenizer wsTokenizer = new WhitespaceTokenizer(new StringReader("please divide this sentence"));
@@ -293,11 +318,12 @@
   
   protected void shingleFilterTest(int maxSize, Token[] tokensToShingle, Token[] tokensToCompare,
                                    int[] positionIncrements, String[] types,
-                                   boolean outputUnigrams)
+                                   boolean outputUnigrams, boolean outputUnigramIfNoNgrams)
     throws IOException {
 
     ShingleFilter filter = new ShingleFilter(new TestTokenStream(tokensToShingle), maxSize);
     filter.setOutputUnigrams(outputUnigrams);
+    filter.setOutputUnigramIfNoNgrams(outputUnigramIfNoNgrams);
 
     TermAttribute termAtt = (TermAttribute) filter.addAttribute(TermAttribute.class);
     OffsetAttribute offsetAtt = (OffsetAttribute) filter.addAttribute(OffsetAttribute.class);
