add LimitIterator class (#62)

* bump up version 1.8.4

Change-Id: I5d150e0c30f83200880cf8dd95e6e4b3678137b0
diff --git a/pom.xml b/pom.xml
index 91fb035..de8e289 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>com.baidu.hugegraph</groupId>
     <artifactId>hugegraph-common</artifactId>
-    <version>1.8.3</version>
+    <version>1.8.4</version>
 
     <name>hugegraph-common</name>
     <url>https://github.com/hugegraph/hugegraph-common</url>
@@ -266,7 +266,7 @@
                         <manifestEntries>
                             <!-- Must be on one line, otherwise the automatic
                                  upgrade script cannot replace the version number -->
-                            <Implementation-Version>1.8.3.0</Implementation-Version>
+                            <Implementation-Version>1.8.4.0</Implementation-Version>
                         </manifestEntries>
                     </archive>
                 </configuration>
diff --git a/src/main/java/com/baidu/hugegraph/iterator/LimitIterator.java b/src/main/java/com/baidu/hugegraph/iterator/LimitIterator.java
new file mode 100644
index 0000000..671168b
--- /dev/null
+++ b/src/main/java/com/baidu/hugegraph/iterator/LimitIterator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * 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.
+ */
+
+package com.baidu.hugegraph.iterator;
+
+import java.util.Iterator;
+import java.util.function.Function;
+
+public class LimitIterator<T> extends WrappedIterator<T> {
+
+    private final Iterator<T> originIterator;
+    private final Function<T, Boolean> filterCallback;
+
+    public LimitIterator(Iterator<T> origin, Function<T, Boolean> filter) {
+        this.originIterator = origin;
+        this.filterCallback = filter;
+    }
+
+    @Override
+    protected Iterator<T> originIterator() {
+        return this.originIterator;
+    }
+
+    @Override
+    protected final boolean fetch() {
+        while (this.originIterator.hasNext()) {
+            T next = this.originIterator.next();
+            if (next == null) {
+                continue;
+            }
+            // Do filter
+            boolean reachLimit = this.filterCallback.apply(next);
+            if (reachLimit) {
+                this.closeOriginIterator();
+                return false;
+            }
+            assert this.current == none();
+            this.current = next;
+            return true;
+        }
+        return false;
+    }
+
+    protected final void closeOriginIterator() {
+        if (this.originIterator == null) {
+            return;
+        }
+        close(this.originIterator);
+    }
+}
diff --git a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
index 4b590a1..ad6cd2c 100644
--- a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
+++ b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
@@ -27,5 +27,5 @@
 
     // The second parameter of Version.of() is for all-in-one JAR
     public static final Version VERSION = Version.of(CommonVersion.class,
-                                                     "1.8.3");
+                                                     "1.8.4");
 }
diff --git a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
index 8b84932..25eb9bf 100644
--- a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
+++ b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
@@ -39,6 +39,7 @@
 import com.baidu.hugegraph.unit.iterator.FilterIteratorTest;
 import com.baidu.hugegraph.unit.iterator.FlatMapperFilterIteratorTest;
 import com.baidu.hugegraph.unit.iterator.FlatMapperIteratorTest;
+import com.baidu.hugegraph.unit.iterator.LimitIteratorTest;
 import com.baidu.hugegraph.unit.iterator.ListIteratorTest;
 import com.baidu.hugegraph.unit.iterator.MapperIteratorTest;
 import com.baidu.hugegraph.unit.license.ExtraParamTest;
@@ -85,6 +86,7 @@
 
     ExtendableIteratorTest.class,
     FilterIteratorTest.class,
+    LimitIteratorTest.class,
     MapperIteratorTest.class,
     FlatMapperIteratorTest.class,
     FlatMapperFilterIteratorTest.class,
diff --git a/src/test/java/com/baidu/hugegraph/unit/date/SafeDateFormatTest.java b/src/test/java/com/baidu/hugegraph/unit/date/SafeDateFormatTest.java
index 418424d..cf0036b 100644
--- a/src/test/java/com/baidu/hugegraph/unit/date/SafeDateFormatTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/date/SafeDateFormatTest.java
@@ -37,6 +37,7 @@
 public class SafeDateFormatTest {
 
     @Test
+    @SuppressWarnings("deprecation")
     public void testSafeDateFormatInConcurrency() throws Exception {
         SafeDateFormat format = new SafeDateFormat("yyyy-MM-dd");
         List<String> sources = ImmutableList.of(
diff --git a/src/test/java/com/baidu/hugegraph/unit/iterator/FilterIteratorTest.java b/src/test/java/com/baidu/hugegraph/unit/iterator/FilterIteratorTest.java
index 4f3ec6c..9024673 100644
--- a/src/test/java/com/baidu/hugegraph/unit/iterator/FilterIteratorTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/iterator/FilterIteratorTest.java
@@ -41,13 +41,12 @@
 
     @Test
     public void testFilter() {
-
-        AtomicInteger valuesCount = new AtomicInteger(0);
+        AtomicInteger callbackCount = new AtomicInteger(0);
 
         Iterator<Integer> values = DATA.iterator();
 
         Function<Integer, Boolean> filter = value -> {
-            valuesCount.incrementAndGet();
+            callbackCount.incrementAndGet();
             return (value % 2 == 0);
         };
 
@@ -58,7 +57,7 @@
             actual.add(results.next());
         }
 
-        Assert.assertEquals(4, valuesCount.get());
+        Assert.assertEquals(4, callbackCount.get());
         Assert.assertEquals(ImmutableList.of(2, 4), actual);
     }
 
@@ -140,6 +139,29 @@
     }
 
     @Test
+    public void testNextWithOriginIteratorReturnNullElem() {
+        List<Integer> list = new ArrayList<>();
+        list.add(1);
+        list.add(null);
+        list.add(3);
+        Iterator<Integer> vals = list.iterator();
+
+        AtomicInteger callbackCount = new AtomicInteger(0);
+
+        Iterator<Integer> results = new FilterIterator<>(vals, val -> {
+            callbackCount.incrementAndGet();
+            return true;
+        });
+
+        Assert.assertTrue(results.hasNext());
+        for (int i = 0; i < 2; i++) {
+            results.next();
+        }
+        Assert.assertFalse(results.hasNext());
+        Assert.assertEquals(2, callbackCount.get());
+    }
+
+    @Test
     public void testRemove() {
         List<Integer> list = new ArrayList<>(DATA);
 
diff --git a/src/test/java/com/baidu/hugegraph/unit/iterator/LimitIteratorTest.java b/src/test/java/com/baidu/hugegraph/unit/iterator/LimitIteratorTest.java
new file mode 100644
index 0000000..acd4fc1
--- /dev/null
+++ b/src/test/java/com/baidu/hugegraph/unit/iterator/LimitIteratorTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * 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.
+ */
+
+package com.baidu.hugegraph.unit.iterator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.iterator.LimitIterator;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.unit.BaseUnitTest;
+import com.baidu.hugegraph.unit.iterator.ExtendableIteratorTest.CloseableItor;
+import com.google.common.collect.ImmutableList;
+
+@SuppressWarnings("resource")
+public class LimitIteratorTest extends BaseUnitTest {
+
+    private static final List<Integer> DATA = ImmutableList.of(1, 2, 3, 4);
+
+    @Test
+    public void testLimit() {
+        AtomicInteger callbackCount = new AtomicInteger(0);
+
+        Iterator<Integer> values = DATA.iterator();
+
+        int limit = 2;
+        Function<Integer, Boolean> filter = value -> {
+            return callbackCount.incrementAndGet() > limit;
+        };
+
+        Iterator<Integer> results = new LimitIterator<>(values, filter);
+
+        List<Integer> actual = new ArrayList<>();
+        while (results.hasNext()) {
+            actual.add(results.next());
+        }
+
+        Assert.assertEquals(3, callbackCount.get());
+        Assert.assertEquals(ImmutableList.of(1, 2), actual);
+    }
+
+    @Test
+    public void testHasNext() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> false);
+        Assert.assertTrue(results.hasNext());
+    }
+
+    @Test
+    public void testHasNextWithMultiTimesWithoutAnyResult() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> true);
+        Assert.assertFalse(results.hasNext());
+        Assert.assertFalse(results.hasNext());
+    }
+
+    @Test
+    public void testHasNextAndNextWithMultiTimes() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> false);
+
+        for (int i = 0; i < 5; i++) {
+            Assert.assertTrue(results.hasNext());
+        }
+
+        for (int i = 0; i < 4; i++) {
+            results.next();
+        }
+
+        Assert.assertFalse(results.hasNext());
+        Assert.assertFalse(results.hasNext());
+
+        Assert.assertThrows(NoSuchElementException.class, () -> {
+            results.next();
+        });
+        Assert.assertThrows(NoSuchElementException.class, () -> {
+            results.next();
+        });
+
+        Iterator<Integer> results2 = new LimitIterator<>(vals, val -> false);
+        Assert.assertFalse(results2.hasNext());
+    }
+
+    @Test
+    public void testNext() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> false);
+        // Call next() without testNext()
+        results.next();
+    }
+
+    @Test
+    public void testNextWithMultiTimes() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> false);
+        for (int i = 0; i < 4; i++) {
+            results.next();
+        }
+        Assert.assertThrows(NoSuchElementException.class, () -> {
+            results.next();
+        });
+    }
+
+    @Test
+    public void testNextWithMultiTimesWithoutAnyResult() {
+        Iterator<Integer> vals = DATA.iterator();
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> true);
+        Assert.assertThrows(NoSuchElementException.class, () -> {
+            results.next();
+        });
+        Assert.assertThrows(NoSuchElementException.class, () -> {
+            results.next();
+        });
+    }
+
+    @Test
+    public void testNextWithOriginIteratorReturnNullElem() {
+        List<Integer> list = new ArrayList<>();
+        list.add(1);
+        list.add(null);
+        list.add(3);
+        Iterator<Integer> vals = list.iterator();
+
+        AtomicInteger callbackCount = new AtomicInteger(0);
+
+        Iterator<Integer> results = new LimitIterator<>(vals, val -> {
+            callbackCount.incrementAndGet();
+            return false;
+        });
+        Assert.assertTrue(results.hasNext());
+        for (int i = 0; i < 2; i++) {
+            results.next();
+        }
+        Assert.assertFalse(results.hasNext());
+        Assert.assertEquals(2, callbackCount.get());
+    }
+
+    @Test
+    public void testRemove() {
+        List<Integer> list = new ArrayList<>(DATA);
+
+        Iterator<Integer> results = new LimitIterator<>(list.iterator(),
+                                                        val -> false);
+
+        Assert.assertEquals(ImmutableList.of(1, 2, 3, 4), list);
+
+        results.next();
+        results.next();
+        results.remove();
+
+        Assert.assertEquals(ImmutableList.of(1, 3, 4), list);
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        CloseableItor<Integer> vals = new CloseableItor<>(DATA.iterator());
+
+        LimitIterator<Integer> results = new LimitIterator<>(vals,
+                                                             val -> true);
+
+        Assert.assertFalse(vals.closed());
+        results.close();
+        Assert.assertTrue(vals.closed());
+    }
+}
diff --git a/src/test/java/com/baidu/hugegraph/unit/license/LicenseCreateParamTest.java b/src/test/java/com/baidu/hugegraph/unit/license/LicenseCreateParamTest.java
index 6a24259..76f6847 100644
--- a/src/test/java/com/baidu/hugegraph/unit/license/LicenseCreateParamTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/license/LicenseCreateParamTest.java
@@ -20,11 +20,7 @@
 package com.baidu.hugegraph.unit.license;
 
 import java.io.IOException;
-import java.text.DateFormat;
 import java.text.ParseException;
-import java.time.ZoneId;
-import java.util.Arrays;
-import java.util.TimeZone;
 
 import org.junit.Test;