Merge pull request #380 from rfoltyns/feature/filtering_parser_match_count

#208 FilteringParserDelegate match count support
diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
index c355446..4459e7f 100644
--- a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
+++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
@@ -414,7 +414,7 @@
                 }
                 _itemFilter = f;
                 if (f == TokenFilter.INCLUDE_ALL) {
-                    if (_includePath) {
+                    if (_verifyAllowedMatches() && _includePath) {
                         return (_currToken = t);
                     }
                 }
@@ -437,7 +437,9 @@
                 f = _headContext.checkValue(f);
                 if ((f == TokenFilter.INCLUDE_ALL)
                         || ((f != null) && f.includeValue(delegate))) {
-                    return (_currToken = t);
+                    if (_verifyAllowedMatches()) {
+                        return (_currToken = t);
+                    }
                 }
             }
             // Otherwise not included (leaves must be explicitly included)
@@ -572,7 +574,7 @@
                     }
                     _itemFilter = f;
                     if (f == TokenFilter.INCLUDE_ALL) {
-                        if (_includePath) {
+                        if (_verifyAllowedMatches() && _includePath) {
                             return (_currToken = t);
                         }
 //                        if (_includeImmediateParent) { ...
@@ -597,7 +599,9 @@
                     f = _headContext.checkValue(f);
                     if ((f == TokenFilter.INCLUDE_ALL)
                             || ((f != null) && f.includeValue(delegate))) {
-                        return (_currToken = t);
+                        if (_verifyAllowedMatches()) {
+                            return (_currToken = t);
+                        }
                     }
                 }
                 // Otherwise not included (leaves must be explicitly included)
@@ -714,7 +718,7 @@
                         continue main_loop;
                     }
                     _itemFilter = f;
-                    if (f == TokenFilter.INCLUDE_ALL) {
+                    if (f == TokenFilter.INCLUDE_ALL && _verifyAllowedMatches()) {
                         return _nextBuffered(buffRoot);
                     }
                 }
@@ -729,7 +733,9 @@
                     f = _headContext.checkValue(f);
                     if ((f == TokenFilter.INCLUDE_ALL)
                             || ((f != null) && f.includeValue(delegate))) {
-                        return _nextBuffered(buffRoot);
+                        if (_verifyAllowedMatches()) {
+                            return _nextBuffered(buffRoot);
+                        }
                     }
                 }
                 // Otherwise not included (leaves must be explicitly included)
@@ -767,7 +773,15 @@
             }
         }
     }
-    
+
+    private final boolean _verifyAllowedMatches() throws IOException {
+        if (_matchCount == 0 || _allowMultipleMatches) {
+            ++_matchCount;
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public JsonToken nextValue() throws IOException {
         // Re-implemented same as ParserMinimalBase:
diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java
index 759167e..a99e814 100644
--- a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java
@@ -190,6 +190,7 @@
         gen.close();
 
         assertEquals(aposToQuotes("{'ob':{'value':['x']}}"), w.toString());
+        assertEquals(1, gen.getMatchCount());
     }
 
     public void testSingleMatchFilteringWithPathRawBinary() throws Exception
@@ -240,47 +241,51 @@
         gen.close();
 
         assertEquals(aposToQuotes("{'array':['AQ==',1,2,3,4 ,5.0 /*x*/,6.25,7.5]}"), w.toString());
+        assertEquals(1, gen.getMatchCount());
     }
     
     public void testMultipleMatchFilteringWithPath1() throws Exception
     {
         StringWriter w = new StringWriter();
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 new NameMatchFilter("value0", "value2"),
                 true, /* includePath */ true /* multipleMatches */ );
         final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}";
         writeJsonDoc(JSON_F, JSON, gen);
         assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':4}}"), w.toString());
+        assertEquals(2, gen.getMatchCount());
     }
 
     public void testMultipleMatchFilteringWithPath2() throws Exception
     {
         StringWriter w = new StringWriter();
-        
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 new NameMatchFilter("array", "b", "value"),
                 true, true);
         final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}";
         writeJsonDoc(JSON_F, JSON, gen);
         assertEquals(aposToQuotes("{'array':[1,2],'ob':{'value':3},'b':true}"), w.toString());
+        assertEquals(3, gen.getMatchCount());
     }
 
     public void testMultipleMatchFilteringWithPath3() throws Exception
     {
         StringWriter w = new StringWriter();
-        
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 new NameMatchFilter("value"),
                 true, true);
         final String JSON = "{'root':{'a0':true,'a':{'value':3},'b':{'value':4}},'b0':false}";
         writeJsonDoc(JSON_F, JSON, gen);
         assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':4}}}"), w.toString());
+        assertEquals(2, gen.getMatchCount());
     }
 
     public void testIndexMatchWithPath1() throws Exception
     {
         StringWriter w = new StringWriter();
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 new IndexMatchFilter(1),
                 true, true);
         final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}";
@@ -293,24 +298,26 @@
                 true, true);
         writeJsonDoc(JSON_F, JSON, gen);
         assertEquals(aposToQuotes("{'array':[1]}"), w.toString());
+        assertEquals(1, gen.getMatchCount());
     }
 
     public void testIndexMatchWithPath2() throws Exception
     {
         StringWriter w = new StringWriter();
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 new IndexMatchFilter(0,1),
                 true, true);
         final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}";
         writeJsonDoc(JSON_F, JSON, gen);
         assertEquals(aposToQuotes("{'array':[1,2]}"), w.toString());
+        assertEquals(2, gen.getMatchCount());
     }
 
     public void testWriteStartObjectWithObject() throws Exception
     {
         StringWriter w = new StringWriter();
 
-        JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
+        FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w),
                 TokenFilter.INCLUDE_ALL,
                 true, true);
 
diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
index 987d89c..290fb04 100644
--- a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
@@ -81,52 +81,118 @@
     public void testSingleMatchFilteringWithoutPath() throws Exception
     {
         JsonParser p0 = JSON_F.createParser(SIMPLE);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                new NameMatchFilter("value"),
                    false, // includePath
                    false // multipleMatches
                 );
         String result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("3"), result);
+        assertEquals(1, p.getMatchCount());
+    }
+
+    public void testSingleMatchFilteringWithPath() throws Exception
+    {
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}");
+        JsonParser p0 = JSON_F.createParser(jsonString);
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
+                new NameMatchFilter("a"),
+                true, // includePath
+                false // multipleMatches
+        );
+        String result = readAndWrite(JSON_F, p);
+        assertEquals(aposToQuotes("{'a':123}"), result);
+        assertEquals(1, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")
-    public void testNotAllowMultipleMatches() throws Exception
+    public void testNotAllowMultipleMatchesWithoutPath1() throws Exception
     {
-    	String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'value':4,'b':true}");
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'b':true}");
         JsonParser p0 = JSON_F.createParser(jsonString);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                new NameMatchFilter("value"),
                    false, // includePath
                    false // multipleMatches -false
                 );
         String result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("3"), result);
+        assertEquals(1, p.getMatchCount());
     }
-    
+
     @SuppressWarnings("resource")
-    public void testAllowMultipleMatches() throws Exception
+    public void testNotAllowMultipleMatchesWithoutPath2() throws Exception
     {
-    	String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'value':4,'b':true}");
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'array':[3,4],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}");
         JsonParser p0 = JSON_F.createParser(jsonString);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
+                new IndexMatchFilter(1),
+                false, // includePath
+                false // multipleMatches -false
+        );
+        String result = readAndWrite(JSON_F, p);
+        assertEquals(aposToQuotes("2"), result);
+        assertEquals(1, p.getMatchCount());
+    }
+
+    @SuppressWarnings("resource")
+    public void testAllowMultipleMatchesWithoutPath() throws Exception
+    {
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}");
+        JsonParser p0 = JSON_F.createParser(jsonString);
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                new NameMatchFilter("value"),
                    false, // includePath
                    true // multipleMatches - true
                 );
         String result = readAndWrite(JSON_F, p);
-        assertEquals(aposToQuotes("3 4"), result);
+        assertEquals(aposToQuotes("3 {\"value0\":2} \"val\""), result);
+        assertEquals(3, p.getMatchCount());
+    }
+
+
+    @SuppressWarnings("resource")
+    public void testAllowMultipleMatchesWithPath1() throws Exception
+    {
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}");
+        JsonParser p0 = JSON_F.createParser(jsonString);
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
+                new NameMatchFilter("value"),
+                true, // includePath
+                true // multipleMatches - true
+        );
+        String result = readAndWrite(JSON_F, p);
+        assertEquals(aposToQuotes("{\"ob\":{\"value\":3,\"value\":{\"value0\":2}},\"value\":\"val\"}"), result);
+        assertEquals(3, p.getMatchCount());
+    }
+
+
+    @SuppressWarnings("resource")
+    public void testAllowMultipleMatchesWithPath2() throws Exception
+    {
+        String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'array':[3,4],'value':{'value0':2}},'value':\"val\",'b':true}");
+        JsonParser p0 = JSON_F.createParser(jsonString);
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
+                new IndexMatchFilter(1),
+                true, // includePath
+                true // multipleMatches - true
+        );
+        String result = readAndWrite(JSON_F, p);
+        assertEquals(aposToQuotes("{\"array\":[2],\"ob\":{\"array\":[4]}}"), result);
+        assertEquals(2, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")
     public void testMultipleMatchFilteringWithPath1() throws Exception
     {
         JsonParser p0 = JSON_F.createParser(SIMPLE);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                 new NameMatchFilter("value0", "value2"),
                 true, /* includePath */ true /* multipleMatches */ );
         String result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':4}}"), result);
+        assertEquals(2, p.getMatchCount());
+
     }
 
     @SuppressWarnings("resource")
@@ -134,51 +200,57 @@
     {
         String INPUT = aposToQuotes("{'a':123,'ob':{'value0':2,'value':3,'value2':4},'b':true}");
         JsonParser p0 = JSON_F.createParser(INPUT);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                 new NameMatchFilter("b", "value"),
                 true, true);
 
         String result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("{'ob':{'value':3},'b':true}"), result);
+        assertEquals(2, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")
     public void testMultipleMatchFilteringWithPath3() throws Exception
     {
-        final String JSON = aposToQuotes("{'root':{'a0':true,'a':{'value':3},'b':{'value':4}},'b0':false}");
+        final String JSON = aposToQuotes("{'root':{'a0':true,'a':{'value':3},'b':{'value':\"foo\"}},'b0':false}");
         JsonParser p0 = JSON_F.createParser(JSON);
-        JsonParser p = new FilteringParserDelegate(p0,
+        FilteringParserDelegate p = new FilteringParserDelegate(p0,
                 new NameMatchFilter("value"),
                 true, true);
         String result = readAndWrite(JSON_F, p);
-        assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':4}}}"), result);
+        assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':\"foo\"}}}"), result);
+        assertEquals(2, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")
     public void testIndexMatchWithPath1() throws Exception
     {
-        JsonParser p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE),
+        FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE),
                 new IndexMatchFilter(1), true, true);
         String result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("{'array':[2]}"), result);
+        assertEquals(1, p.getMatchCount());
 
         p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE),
                 new IndexMatchFilter(0), true, true);
         result = readAndWrite(JSON_F, p);
         assertEquals(aposToQuotes("{'array':[1]}"), result);
+        assertEquals(1, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")
     public void testIndexMatchWithPath2() throws Exception
     {
-        JsonParser p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE),
+        FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE),
                 new IndexMatchFilter(0, 1), true, true);
         assertEquals(aposToQuotes("{'array':[1,2]}"), readAndWrite(JSON_F, p));
+        assertEquals(2, p.getMatchCount());
     
         String JSON = aposToQuotes("{'a':123,'array':[1,2,3,4,5],'b':[1,2,3]}");
         p = new FilteringParserDelegate(JSON_F.createParser(JSON),
                 new IndexMatchFilter(1, 3), true, true);
         assertEquals(aposToQuotes("{'array':[2,4],'b':[2]}"), readAndWrite(JSON_F, p));
+        assertEquals(3, p.getMatchCount());
     }
 
     @SuppressWarnings("resource")