You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2016/05/31 02:01:02 UTC

[2/3] groovy git commit: GROOVY-6950: StringGroovyMethods minor performance improvements (make use of line based iterator)

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/35b5f345
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/35b5f345
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/35b5f345

Branch: refs/heads/master
Commit: 35b5f3450788601e5bad8a18b5a4758663fedebb
Parents: 24043a5
Author: paulk <pa...@asert.com.au>
Authored: Mon May 30 21:11:46 2016 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Mon May 30 21:11:46 2016 +1000

----------------------------------------------------------------------
 .../groovy/runtime/StringGroovyMethods.java     | 146 ++++++----------
 .../groovy/util/CharSequenceReader.java         | 170 +++++++++++++++++++
 2 files changed, 226 insertions(+), 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/35b5f345/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 058b270..665f606 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.PickFirstResolver;
 import groovy.transform.stc.SimpleType;
 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.util.CharSequenceReader;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -40,6 +41,7 @@ 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;
@@ -47,6 +49,7 @@ import java.util.Map;
 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;
 
@@ -69,36 +72,6 @@ import static org.codehaus.groovy.runtime.DefaultGroovyMethods.join;
  * 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.
- *
- * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
- * @author Jeremy Rayner
- * @author Sam Pullara
- * @author Rod Cope
- * @author Guillaume Laforge
- * @author John Wilson
- * @author Hein Meling
- * @author Dierk Koenig
- * @author Pilho Kim
- * @author Marc Guillemot
- * @author Russel Winder
- * @author bing ran
- * @author Jochen Theodorou
- * @author Paul King
- * @author Michael Baehr
- * @author Joachim Baumann
- * @author Alex Tkachman
- * @author Ted Naleid
- * @author Brad Long
- * @author Jim Jagielski
- * @author Rodolfo Velasco
- * @author jeremi Joslin
- * @author Hamlet D'Arcy
- * @author Cedric Champeau
- * @author Tim Yates
- * @author Dinko Srkoc
- * @author Pascal Lombard
- * @author Christophe Charles
- * @author Andres Almiray
  */
 public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
 
@@ -339,15 +312,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
@@ -628,6 +600,19 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
         }
     }
 
+    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
@@ -660,7 +645,7 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
     public static <T> T eachLine(CharSequence self, int firstLine, @ClosureParams(value=FromString.class, options={"String","String,Integer"}) Closure<T> closure) throws IOException {
         int count = firstLine;
         T result = null;
-        for (String line : readLines((CharSequence)self.toString())) {
+        for (String line : new LineIterable(self)) {
             result = callClosureForLine(closure, line, count);
             count++;
         }
@@ -822,22 +807,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();
     }
 
     /**
@@ -3126,16 +3106,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);
     }
@@ -3154,25 +3130,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");
+        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));
             }
-            // 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 */
+            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();
     }
 
     /**
@@ -3234,21 +3205,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/35b5f345/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