GROOVY-6950: StringGroovyMethods minor performance improvements (make use of 
line based iterator)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/fc872b60
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/fc872b60
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/fc872b60

Branch: refs/heads/GROOVY_2_4_X
Commit: fc872b60d6faa854000d8ba5150119c9cd359d27
Parents: 16ce780
Author: paulk <pa...@asert.com.au>
Authored: Mon May 30 21:11:46 2016 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Tue May 31 12:06:15 2016 +1000

----------------------------------------------------------------------
 .../groovy/runtime/StringGroovyMethods.java     | 118 +++++++------
 .../groovy/util/CharSequenceReader.java         | 170 +++++++++++++++++++
 2 files changed, 228 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/fc872b60/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java 
b/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java
index 8f6ae2b..be27c70 100644
--- a/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java
+++ b/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java
@@ -29,6 +29,7 @@ import groovy.transform.stc.FromString;
 import groovy.transform.stc.SimpleType;
 import org.codehaus.groovy.runtime.callsite.BooleanClosureWrapper;
 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.util.CharSequenceReader;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -40,12 +41,14 @@ import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.TreeSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -66,6 +69,7 @@ import static 
org.codehaus.groovy.runtime.DefaultGroovyMethods.each;
  * at the Java method call level. I.e. future versions of Groovy may
  * remove or move a method call in this file but would normally
  * aim to keep the method available from within Groovy.
+<<<<<<< HEAD
  *
  * @author <a href="mailto:ja...@coredevelopers.net";>James Strachan</a>
  * @author Jeremy Rayner
@@ -95,6 +99,8 @@ import static 
org.codehaus.groovy.runtime.DefaultGroovyMethods.each;
  * @author Dinko Srkoc
  * @author Pascal Lombard
  * @author Christophe Charles
+=======
+>>>>>>> 35b5f34... GROOVY-6950: StringGroovyMethods minor performance 
improvements (make use of line based iterator)
  */
 public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
 
@@ -314,15 +320,14 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * @since 1.8.2
      */
     public static String center(CharSequence self, Number numberOfChars, 
CharSequence padding) {
-        String padding1 = padding.toString();
         int numChars = numberOfChars.intValue();
         if (numChars <= self.length()) {
             return self.toString();
         } else {
             int charsToAdd = numChars - self.length();
             String semiPad = charsToAdd % 2 == 1 ?
-                    getPadding(padding1, charsToAdd / 2 + 1) :
-                    getPadding(padding1, charsToAdd / 2);
+                    getPadding(padding, charsToAdd / 2 + 1) :
+                    getPadding(padding, charsToAdd / 2);
             if (charsToAdd % 2 == 0)
                 return semiPad + self + semiPad;
             else
@@ -575,6 +580,19 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
         return dropWhile(self.toString(), condition).toString();
     }
 
+    private static final class LineIterable implements Iterable<String> {
+        private final CharSequence delegate;
+
+        public LineIterable(CharSequence cs) {
+            this.delegate = cs;
+        }
+
+        @Override
+        public Iterator<String> iterator() {
+            return IOGroovyMethods.iterator(new CharSequenceReader(delegate));
+        }
+    }
+
     /**
      * Iterates through this CharSequence line by line.  Each line is passed
      * to the given 1 or 2 arg closure. If a 2 arg closure is found
@@ -601,7 +619,6 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * @param closure a closure (arg 1 is line, optional arg 2 is line number)
      * @return the last value returned by the closure
      * @throws java.io.IOException if an error occurs
-     * @see #eachLine(String, int, groovy.lang.Closure)
      * @since 1.8.2
      */
     public static <T> T eachLine(CharSequence self, int firstLine, 
@ClosureParams(value=FromString.class, options={"String","String,Integer"}) 
Closure<T> closure) throws IOException {
@@ -766,22 +783,17 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
      * @since 1.8.2
      */
     public static String expand(CharSequence self, int tabStop) {
-        if (self.length() == 0) return self.toString();
-        try {
-            StringBuilder builder = new StringBuilder();
-            for (String line : readLines(self)) {
-                builder.append(expandLine((CharSequence)line, tabStop));
-                builder.append("\n");
-            }
-            // remove the normalized ending line ending if it was not present
-            if (self.charAt(self.length() - 1) != '\n') {
-                builder.deleteCharAt(builder.length() - 1);
-            }
-            return builder.toString();
-        } catch (IOException e) {
-            /* ignore */
+        if (self.length() == 0) return "";
+        StringBuilder builder = new StringBuilder();
+        for (String line : new LineIterable(self)) {
+            builder.append(expandLine((CharSequence)line, tabStop));
+            builder.append("\n");
         }
-        return self.toString();
+        // remove the normalized ending line ending if it was not present
+        if (self.charAt(self.length() - 1) != '\n') {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        return builder.toString();
     }
 
     /**
@@ -2933,16 +2945,12 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
     public static String stripIndent(CharSequence self) {
         if (self.length() == 0) return self.toString();
         int runningCount = -1;
-        try {
-            for (String line : readLines(self)) {
-                // don't take blank lines into account for calculating the 
indent
-                if (isAllWhitespace((CharSequence) line)) continue;
-                if (runningCount == -1) runningCount = line.length();
-                runningCount = findMinimumLeadingSpaces(line, runningCount);
-                if (runningCount == 0) break;
-            }
-        } catch (IOException e) {
-            /* ignore */
+        for (String line : new LineIterable(self)) {
+            // don't take blank lines into account for calculating the indent
+            if (isAllWhitespace((CharSequence) line)) continue;
+            if (runningCount == -1) runningCount = line.length();
+            runningCount = findMinimumLeadingSpaces(line, runningCount);
+            if (runningCount == 0) break;
         }
         return stripIndent(self, runningCount == -1 ? 0 : runningCount);
     }
@@ -2961,25 +2969,20 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
      */
     public static String stripIndent(CharSequence self, int numChars) {
         if (self.length() == 0 || numChars <= 0) return self.toString();
-        try {
-            StringBuilder builder = new StringBuilder();
-            for (String line : readLines(self)) {
-                // normalize an empty or whitespace line to \n
-                // or strip the indent for lines containing non-space 
characters
-                if (!isAllWhitespace((CharSequence) line)) {
-                    builder.append(stripIndentFromLine(line, numChars));
-                }
-                builder.append("\n");
-            }
-            // remove the normalized ending line ending if it was not present
-            if (self.charAt(self.length() - 1) != '\n') {
-                builder.deleteCharAt(builder.length() - 1);
+        StringBuilder builder = new StringBuilder();
+        for (String line : new LineIterable(self)) {
+            // normalize an empty or whitespace line to \n
+            // or strip the indent for lines containing non-space characters
+            if (!isAllWhitespace((CharSequence) line)) {
+                builder.append(stripIndentFromLine(line, numChars));
             }
-            return builder.toString();
-        } catch (IOException e) {
-            /* ignore */
+            builder.append("\n");
         }
-        return self.toString();
+        // remove the normalized ending line ending if it was not present
+        if (self.charAt(self.length() - 1) != '\n') {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        return builder.toString();
     }
 
     /**
@@ -3041,21 +3044,16 @@ public class StringGroovyMethods extends 
DefaultGroovyMethodsSupport {
      */
     public static String stripMargin(CharSequence self, char marginChar) {
         if (self.length() == 0) return self.toString();
-        try {
-            StringBuilder builder = new StringBuilder();
-            for (String line : readLines(self)) {
-                builder.append(stripMarginFromLine(line, marginChar));
-                builder.append("\n");
-            }
-            // remove the normalized ending line ending if it was not present
-            if (self.charAt(self.length() - 1) != '\n') {
-                builder.deleteCharAt(builder.length() - 1);
-            }
-            return builder.toString();
-        } catch (IOException e) {
-            /* ignore */
+        StringBuilder builder = new StringBuilder();
+        for (String line : new LineIterable(self)) {
+            builder.append(stripMarginFromLine(line, marginChar));
+            builder.append("\n");
         }
-        return self.toString();
+        // remove the normalized ending line ending if it was not present
+        if (self.charAt(self.length() - 1) != '\n') {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        return builder.toString();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/groovy/blob/fc872b60/src/main/org/codehaus/groovy/util/CharSequenceReader.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/util/CharSequenceReader.java 
b/src/main/org/codehaus/groovy/util/CharSequenceReader.java
new file mode 100644
index 0000000..2154002
--- /dev/null
+++ b/src/main/org/codehaus/groovy/util/CharSequenceReader.java
@@ -0,0 +1,170 @@
+/*
+ * 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 org.codehaus.groovy.util;
+
+import java.io.Reader;
+import java.io.Serializable;
+
+/**
+ * {@link Reader} implementation that can read from String, StringBuffer,
+ * StringBuilder, CharBuffer or GString.
+ * <p>
+ * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.
+ */
+public class CharSequenceReader extends Reader implements Serializable {
+    /*
+      NOTE: nearly 100% borrowed from Commons-IO but we don't want to bring
+      in that whole package just yet. We need to consider reworking all of our
+      IO in light of Java 8 streams and decide whether it makes sense to bring
+      in an external package.
+     */
+    private static final long serialVersionUID = -6661279371843310693L;
+    private final CharSequence charSequence;
+    private int idx;
+    private int mark;
+    private static final int EOF = -1;
+
+    /**
+     * Construct a new instance with the specified character sequence.
+     *
+     * @param charSequence The character sequence, may be {@code null}
+     */
+    public CharSequenceReader(final CharSequence charSequence) {
+        this.charSequence = charSequence != null ? charSequence : "";
+    }
+
+    /**
+     * Close resets the reader back to the start and removes any marked 
position.
+     */
+    @Override
+    public void close() {
+        idx = 0;
+        mark = 0;
+    }
+
+    /**
+     * Mark the current position.
+     *
+     * @param readAheadLimit ignored
+     */
+    @Override
+    public void mark(final int readAheadLimit) {
+        mark = idx;
+    }
+
+    /**
+     * Mark is supported (returns true).
+     *
+     * @return {@code true}
+     */
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
+    /**
+     * Read a single character.
+     *
+     * @return the next character from the character sequence
+     * or -1 if the end has been reached.
+     */
+    @Override
+    public int read() {
+        if (idx >= charSequence.length()) {
+            return EOF;
+        } else {
+            return charSequence.charAt(idx++);
+        }
+    }
+
+    /**
+     * Read the sepcified number of characters into the array.
+     *
+     * @param array The array to store the characters in
+     * @param offset The starting position in the array to store
+     * @param length The maximum number of characters to read
+     * @return The number of characters read or -1 if there are
+     * no more
+     */
+    @Override
+    public int read(final char[] array, final int offset, final int length) {
+        if (idx >= charSequence.length()) {
+            return EOF;
+        }
+        if (array == null) {
+            throw new NullPointerException("Character array is missing");
+        }
+        if (length < 0 || offset < 0 || offset + length > array.length) {
+            throw new IndexOutOfBoundsException("Array Size=" + array.length +
+                    ", offset=" + offset + ", length=" + length);
+        }
+        int count = 0;
+        for (int i = 0; i < length; i++) {
+            final int c = read();
+            if (c == EOF) {
+                return count;
+            }
+            array[offset + i] = (char)c;
+            count++;
+        }
+        return count;
+    }
+
+    /**
+     * Reset the reader to the last marked position (or the beginning if
+     * mark has not been called).
+     */
+    @Override
+    public void reset() {
+        idx = mark;
+    }
+
+    /**
+     * Skip the specified number of characters.
+     *
+     * @param n The number of characters to skip
+     * @return The actual number of characters skipped
+     */
+    @Override
+    public long skip(final long n) {
+        if (n < 0) {
+            throw new IllegalArgumentException(
+                    "Number of characters to skip is less than zero: " + n);
+        }
+        if (idx >= charSequence.length()) {
+            return EOF;
+        }
+        final int dest = (int)Math.min(charSequence.length(), idx + n);
+        final int count = dest - idx;
+        idx = dest;
+        return count;
+    }
+
+    /**
+     * Return a String representation of the underlying
+     * character sequence.
+     *
+     * @return The contents of the character sequence
+     */
+    @Override
+    public String toString() {
+        return charSequence.toString();
+    }
+}
\ No newline at end of file

Reply via email to