Repository: yegor256/cactoos (java)

Issue:
missing analog to get substring from the end of the line
Function in commons-lang3 for StringUtils.substring can be called with negative start value to get tail from the string 
 
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html#substring-java.lang.String-int-int-

Pull request description:
Feat: add support for negative value to get tail from the string
Closes #1717

@yegor256 please review and consider merging it, thanks!

Patch:
diff --git a/src/main/java/org/cactoos/text/Sub.java b/src/main/java/org/cactoos/text/Sub.java
index 6378501c..c0743e02 100644
--- a/src/main/java/org/cactoos/text/Sub.java
+++ b/src/main/java/org/cactoos/text/Sub.java
@@ -148,13 +148,22 @@ public final class Sub extends TextEnvelope {
             new Mapped(
                 origin -> {
                     int begin = start.apply(origin);
+                    if (begin < 0) {
+                        begin = origin.length() + begin;
+                    }
                     if (begin < 0) {
                         begin = 0;
                     }
                     int finish = end.apply(origin);
-                    if (origin.length() < finish) {
+                    if (finish < 0) {
+                        finish = origin.length() + finish;
+                    }
+                    if (finish > origin.length()) {
                         finish = origin.length();
                     }
+                    if (finish < 0) {
+                        finish = 0;
+                    }
                     return origin.substring(begin, finish);
                 },
                 text


Test patch:
diff --git a/src/test/java/org/cactoos/text/SubTest.java b/src/test/java/org/cactoos/text/SubTest.java
index a6a139d5..07c6d751 100644
--- a/src/test/java/org/cactoos/text/SubTest.java
+++ b/src/test/java/org/cactoos/text/SubTest.java
@@ -67,4 +67,31 @@ final class SubTest {
         ).affirm();
     }
 
+    @Test
+    void cutTextWithNegativeStart() {
+        new Assertion<>(
+            "Can't cut text with negative start",
+            new Sub("hello world", -5),
+            new HasString("world")
+        ).affirm();
+    }
+
+    @Test
+    void cutTextWithNegativeStartAndEnd() {
+        new Assertion<>(
+            "Can't cut text with negative start and positive end",
+            new Sub("hello world", -5, 8),
+            new HasString("wo")
+        ).affirm();
+    }
+
+    @Test
+    void cutTextWithNegativeStartAndNegativeEnd() {
+        new Assertion<>(
+            "Can't cut text with negative start and negative end",
+            new Sub("hello world", -5, -2),
+            new HasString("wor")
+        ).affirm();
+    }
+
 }