Repository: groovy Updated Branches: refs/heads/master a88c03474 -> 432a8e5ed
GROOVY-6950: StringGroovyMethods minor performance improvements (avoid calling toString) Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/24043a5c Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/24043a5c Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/24043a5c Branch: refs/heads/master Commit: 24043a5c56fc020d8a5e633f7bd862033cbecf75 Parents: a88c034 Author: paulk <pa...@asert.com.au> Authored: Mon May 30 17:46:28 2016 +1000 Committer: paulk <pa...@asert.com.au> Committed: Mon May 30 17:46:28 2016 +1000 ---------------------------------------------------------------------- .../groovy/runtime/StringGroovyMethods.java | 115 ++++++++----------- .../groovy/GroovyCharSequenceMethodsTest.groovy | 95 ++++++++------- 2 files changed, 95 insertions(+), 115 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/24043a5c/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 d782810..058b270 100644 --- a/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java +++ b/src/main/org/codehaus/groovy/runtime/StringGroovyMethods.java @@ -257,9 +257,8 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 2.5.0 */ public static String uncapitalize(CharSequence self) { - String s = self.toString(); - if (s == null || s.length() == 0) return s; - return Character.toLowerCase(s.charAt(0)) + s.substring(1); + if (self.length() == 0) return ""; + return "" + Character.toLowerCase(self.charAt(0)) + self.subSequence(1, self.length()); } /** @@ -279,9 +278,8 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String capitalize(CharSequence self) { - String s = self.toString(); - if (s == null || s.length() == 0) return s; - return Character.toUpperCase(s.charAt(0)) + s.substring(1); + if (self.length() == 0) return ""; + return "" + Character.toUpperCase(self.charAt(0)) + self.subSequence(1, self.length()); } /** @@ -341,20 +339,19 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String center(CharSequence self, Number numberOfChars, CharSequence padding) { - String s = self.toString(); String padding1 = padding.toString(); int numChars = numberOfChars.intValue(); - if (numChars <= s.length()) { - return s; + if (numChars <= self.length()) { + return self.toString(); } else { - int charsToAdd = numChars - s.length(); + int charsToAdd = numChars - self.length(); String semiPad = charsToAdd % 2 == 1 ? getPadding(padding1, charsToAdd / 2 + 1) : getPadding(padding1, charsToAdd / 2); if (charsToAdd % 2 == 0) - return semiPad + s + semiPad; + return semiPad + self + semiPad; else - return semiPad.substring(0, charsToAdd / 2) + s + semiPad; + return semiPad.substring(0, charsToAdd / 2) + self + semiPad; } } @@ -440,7 +437,6 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String denormalize(final CharSequence self) { - final String s = self.toString(); // Don't do this in static initializer because we may never be needed. // TODO: Put this lineSeparator property somewhere everyone can use it. if (lineSeparator == null) { @@ -459,10 +455,10 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { } } - final int len = s.length(); + final int len = self.length(); if (len < 1) { - return s; + return self.toString(); } final StringBuilder sb = new StringBuilder((110 * len) / 100); @@ -470,14 +466,14 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { int i = 0; while (i < len) { - final char ch = s.charAt(i++); + final char ch = self.charAt(i++); switch (ch) { case '\r': sb.append(lineSeparator); // Eat the following LF if any. - if ((i < len) && (s.charAt(i) == '\n')) { + if ((i < len) && (self.charAt(i) == '\n')) { ++i; } @@ -826,23 +822,22 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String expand(CharSequence self, int tabStop) { - String s = self.toString(); - if (s.length() == 0) return s; + if (self.length() == 0) return self.toString(); try { StringBuilder builder = new StringBuilder(); - for (String line : readLines(s)) { - builder.append(expandLine(line, tabStop)); + 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 (!s.endsWith("\n")) { + if (self.charAt(self.length() - 1) != '\n') { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } - return s; + return self.toString(); } /** @@ -1573,11 +1568,11 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { return counter; } - private static String getPadding(String padding, int length) { + private static String getPadding(CharSequence padding, int length) { if (padding.length() < length) { return multiply(padding, length / padding.length() + 1).substring(0, length); } else { - return padding.substring(0, length); + return "" + padding.subSequence(0, length); } } @@ -1632,9 +1627,8 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static boolean isAllWhitespace(CharSequence self) { - String s = self.toString(); - for (int i = 0; i < s.length(); i++) { - if (!Character.isWhitespace(s.charAt(i))) + for (int i = 0; i < self.length(); i++) { + if (!Character.isWhitespace(self.charAt(i))) return false; } return true; @@ -1720,11 +1714,10 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static boolean isCase(CharSequence caseValue, Object switchValue) { - String s = caseValue.toString(); if (switchValue == null) { - return s == null; + return caseValue == null; } - return s.equals(switchValue.toString()); + return caseValue.toString().equals(switchValue.toString()); } /** @@ -2096,16 +2089,15 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String multiply(CharSequence self, Number factor) { - String s = self.toString(); int size = factor.intValue(); if (size == 0) return ""; else if (size < 0) { throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size); } - StringBuilder answer = new StringBuilder(s); + StringBuilder answer = new StringBuilder(self); for (int i = 1; i < size; i++) { - answer.append(s); + answer.append(self); } return answer.toString(); } @@ -2265,12 +2257,11 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String padLeft(CharSequence self, Number numberOfChars, CharSequence padding) { - String s = self.toString(); int numChars = numberOfChars.intValue(); - if (numChars <= s.length()) { - return s; + if (numChars <= self.length()) { + return self.toString(); } else { - return getPadding(padding.toString(), numChars - s.length()) + s; + return getPadding(padding.toString(), numChars - self.length()) + self; } } @@ -2343,12 +2334,11 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String padRight(CharSequence self, Number numberOfChars, CharSequence padding) { - String s = self.toString(); int numChars = numberOfChars.intValue(); - if (numChars <= s.length()) { - return s; + if (numChars <= self.length()) { + return self.toString(); } else { - return s + getPadding(padding.toString(), numChars - s.length()); + return self + getPadding(padding.toString(), numChars - self.length()); } } @@ -3134,11 +3124,10 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String stripIndent(CharSequence self) { - String s = self.toString(); - if (s.length() == 0) return s; + if (self.length() == 0) return self.toString(); int runningCount = -1; try { - for (String line : readLines((CharSequence) s)) { + 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(); @@ -3148,7 +3137,7 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { } catch (IOException e) { /* ignore */ } - return stripIndent(s, runningCount == -1 ? 0 : runningCount); + return stripIndent(self, runningCount == -1 ? 0 : runningCount); } /** @@ -3164,11 +3153,10 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String stripIndent(CharSequence self, int numChars) { - String s = self.toString(); - if (s.length() == 0 || numChars <= 0) return s; + if (self.length() == 0 || numChars <= 0) return self.toString(); try { StringBuilder builder = new StringBuilder(); - for (String line : readLines((CharSequence) s)) { + 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)) { @@ -3177,14 +3165,14 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { builder.append("\n"); } // remove the normalized ending line ending if it was not present - if (!s.endsWith("\n")) { + if (self.charAt(self.length() - 1) != '\n') { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } - return s; + return self.toString(); } /** @@ -3245,23 +3233,22 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String stripMargin(CharSequence self, char marginChar) { - String s = self.toString(); - if (s.length() == 0) return s; + if (self.length() == 0) return self.toString(); try { StringBuilder builder = new StringBuilder(); - for (String line : readLines((CharSequence) s)) { + 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 (!s.endsWith("\n")) { + if (self.charAt(self.length() - 1) != '\n') { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } - return s; + return self.toString(); } /** @@ -3275,11 +3262,10 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String stripMargin(CharSequence self, CharSequence marginChar) { - String s = self.toString(); String mc = marginChar.toString(); - if (mc == null || mc.length() == 0) return stripMargin((CharSequence) s, '|'); + if (mc.length() == 0) return stripMargin(self, '|'); // TODO IllegalArgumentException for marginChar.length() > 1 ? Or support String as marker? - return stripMargin((CharSequence) s, mc.charAt(0)); + return stripMargin(self, mc.charAt(0)); } /** @@ -3763,23 +3749,22 @@ public class StringGroovyMethods extends DefaultGroovyMethodsSupport { * @since 1.8.2 */ public static String unexpand(CharSequence self, int tabStop) { - String s = self.toString(); - if (s.length() == 0) return s; + if (self.length() == 0) return self.toString(); try { StringBuilder builder = new StringBuilder(); - for (String line : readLines((CharSequence) s)) { - builder.append(unexpandLine(line, tabStop)); + for (String line : readLines(self)) { + builder.append(unexpandLine((CharSequence)line, tabStop)); builder.append("\n"); } // remove the normalized ending line ending if it was not present - if (!s.endsWith("\n")) { + if (self.charAt(self.length() - 1) != '\n') { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } - return s; + return self.toString(); } /** http://git-wip-us.apache.org/repos/asf/groovy/blob/24043a5c/src/test/groovy/GroovyCharSequenceMethodsTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/GroovyCharSequenceMethodsTest.groovy b/src/test/groovy/GroovyCharSequenceMethodsTest.groovy index 0e40f2c..3554e8d 100644 --- a/src/test/groovy/GroovyCharSequenceMethodsTest.groovy +++ b/src/test/groovy/GroovyCharSequenceMethodsTest.groovy @@ -20,33 +20,28 @@ package groovy /** * Tests for DGM methods on CharSequence. - * - * @author Paul King */ class GroovyCharSequenceMethodsTest extends GroovyTestCase { - def s1 = 'Today is Thu Jul 28 06:38:07 EST 2011' - def cs1 = [ - toString:{ -> s1 }, - subSequence:{ int f, int t -> s1.substring(f, t) }, - length:{ -> s1.length() }, - charAt:{ int i -> s1.chars[i] }, - ] as CharSequence - def s2 = 'Foobar' - def cs2 = [ - toString:{ -> s2 }, - subSequence:{ int f, int t -> s2.substring(f, t) }, - length:{ -> s2.length() }, - charAt:{ int i -> s2.chars[i] }, - ] as CharSequence - def cs3 = [ - toString: { -> '''\ + private static CharSequence makeCharSequence(String s) { + [ + toString : { -> s }, + subSequence: { int f, int t -> s.substring(f, t) }, + length : { -> s.length() }, + charAt : { int i -> s.chars[i] }, + ] as CharSequence + } + + def cs1 = makeCharSequence('Today is Thu Jul 28 06:38:07 EST 2011') + + def cs2 = makeCharSequence('Foobar') + + def cs3 = makeCharSequence('''\ |Foo |bar - |''' - } - ] as CharSequence - def csEmpty = [toString:{->''}, length:{->0}] as CharSequence + |''') + + def csEmpty = makeCharSequence('') void testIsCase() { // direct @@ -95,7 +90,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { void testIsAllWhitespace() { assert !cs2.isAllWhitespace() - assert ([toString:{->' \t\n\r'}] as CharSequence).isAllWhitespace() + assert makeCharSequence(' \t\n\r').isAllWhitespace() } void testReplace() { @@ -109,7 +104,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testFind() { - def csDigits = [toString:{->/\d{4}/}] as CharSequence + def csDigits = makeCharSequence(/\d{4}/) assert cs1.find(csDigits) == '2011' assert cs1.find(csDigits, {"--$it--"}) == '--2011--' assert cs1.find(~/\d\d:\d\d/) == '06:38' @@ -117,7 +112,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testFindAll() { - def csDigits = [toString:{->/\d\d/}] as CharSequence + def csDigits = makeCharSequence(/\d\d/) assert cs1.findAll(csDigits) == ['28', '06', '38', '07', '20', '11'] assert cs1.findAll(csDigits, {"<$it>"}) == ['<28>', '<06>', '<38>', '<07>', '<20>', '<11>'] assert cs1.findAll(~/\s\d\d/) == [' 28', ' 06', ' 20'] @@ -165,30 +160,30 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testCapitalize() { - def csfoo = [toString:{->'foo'}] as CharSequence + def csfoo = makeCharSequence('foo') assert csfoo.capitalize() == 'Foo' assert cs2.capitalize() == 'Foobar' } void testUncapitalize() { - def csfoo = [toString:{->'Foo'}] as CharSequence + def csfoo = makeCharSequence('Foo') assert csfoo.uncapitalize() == 'foo' assert cs2.uncapitalize() == 'foobar' } void testExpand() { - def csfoobar = [toString:{->'foo\tbar'}] as CharSequence + def csfoobar = makeCharSequence('foo\tbar') assert csfoobar.expand() == 'foo bar' assert csfoobar.expand(4) == 'foo bar' - csfoobar = [toString:{->'\tfoo\n\tbar'}] as CharSequence + csfoobar = makeCharSequence('\tfoo\n\tbar') assert csfoobar.expand(4) == ' foo\n bar' } void testUnexpand() { - def csfoobar = [toString:{->'foo bar'}] as CharSequence + def csfoobar = makeCharSequence('foo bar') assert csfoobar.unexpand() == 'foo\tbar' assert csfoobar.unexpand(4) == 'foo\t\tbar' - csfoobar = [toString:{->' foo\n bar'}] as CharSequence + csfoobar = makeCharSequence(' foo\n bar') assert csfoobar.unexpand(4) == '\t foo\n\tbar' } @@ -198,22 +193,22 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testMinus() { - def csoo = [toString:{->'oo'}] as CharSequence + def csoo = makeCharSequence('oo') assert cs2.minus(42) == 'Foobar' assert cs2.minus(csoo) == 'Fbar' assert cs2 - csoo == 'Fbar' } void testContains() { - def csoo = [toString:{->'oo'}] as CharSequence - def csbaz = [toString:{->'baz'}] as CharSequence + def csoo = makeCharSequence('oo') + def csbaz = makeCharSequence('baz') assert cs2.contains(csoo) assert !cs2.contains(csbaz) } void testCount() { - def cszero = [toString:{->'0'}] as CharSequence - def csbar = [toString:{->'|'}] as CharSequence + def cszero = makeCharSequence('0') + def csbar = makeCharSequence('|') assert cs1.count(cszero) == 3 assert cs3.count(csbar) == 3 } @@ -234,7 +229,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToInteger() { - def csFourteen = [toString:{->'014'}] as CharSequence + def csFourteen = makeCharSequence('014') assert csFourteen.isInteger() def fourteen = csFourteen.toInteger() assert fourteen instanceof Integer @@ -242,7 +237,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToLong() { - def csFourteen = [toString:{->'014'}] as CharSequence + def csFourteen = makeCharSequence('014') assert csFourteen.isLong() def fourteen = csFourteen.toLong() assert fourteen instanceof Long @@ -250,14 +245,14 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToShort() { - def csFourteen = [toString:{->'014'}] as CharSequence + def csFourteen = makeCharSequence('014') def fourteen = csFourteen.toShort() assert fourteen instanceof Short assert fourteen == 14 } void testToBigInteger() { - def csFourteen = [toString:{->'014'}] as CharSequence + def csFourteen = makeCharSequence('014') assert csFourteen.isBigInteger() def fourteen = csFourteen.toBigInteger() assert fourteen instanceof BigInteger @@ -265,7 +260,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToFloat() { - def csThreePointFive = [toString:{->'3.5'}] as CharSequence + def csThreePointFive = makeCharSequence('3.5') assert csThreePointFive.isFloat() def threePointFive = csThreePointFive.toFloat() assert threePointFive instanceof Float @@ -273,7 +268,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToDouble() { - def csThreePointFive = [toString:{->'3.5'}] as CharSequence + def csThreePointFive = makeCharSequence('3.5') assert csThreePointFive.isDouble() def threePointFive = csThreePointFive.toDouble() assert threePointFive instanceof Double @@ -281,7 +276,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testToBigDecimal() { - def csThreePointFive = [toString:{->'3.5'}] as CharSequence + def csThreePointFive = makeCharSequence('3.5') assert csThreePointFive.isBigDecimal() assert csThreePointFive.isNumber() def threePointFive = csThreePointFive.toBigDecimal() @@ -300,8 +295,8 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { void testSplitEachLine() { def regexOp = /\s*\*\s*/ - def csOp = [toString:{->regexOp}] as CharSequence - def csTwoLines = [toString:{->'10*15\n11 * 9'}] as CharSequence + def csOp = makeCharSequence(regexOp) + def csTwoLines = makeCharSequence('10*15\n11 * 9') def result = [] csTwoLines.splitEachLine(csOp){ left, right -> result << left.toInteger() * right.toInteger() } assert result == [150, 99] @@ -342,7 +337,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { private enum Coin { penny, nickel, dime, quarter } void testAsType() { - def csDime = [toString:{->'dime'}] as CharSequence + def csDime = makeCharSequence('dime') def dime = csDime as Coin assert dime instanceof Coin assert dime == Coin.dime @@ -351,7 +346,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { void testEachMatch() { def result = [] def regexDigits = /(\d)(.)(\d)/ - def csDigits = [toString:{->regexDigits}] as CharSequence + def csDigits = makeCharSequence(regexDigits) assert cs1.eachMatch(csDigits) { all, first, delim, second -> result << "$first $delim $second" } assert result == ['8 0', '6 : 3', '8 : 0', '2 0 1'] result = [] @@ -364,8 +359,8 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { } void testReplaceAllFirst() { - def csDigit = [toString:{->/\d/}] as CharSequence - def csUnder = [toString:{->/_/}] as CharSequence + def csDigit = makeCharSequence(/\d/) + def csUnder = makeCharSequence(/_/) assert cs1.replaceAll(~/\d/, csUnder) == 'Today is Thu Jul __ __:__:__ EST ____' assert cs1.replaceAll(csDigit, csUnder) == 'Today is Thu Jul __ __:__:__ EST ____' @@ -377,7 +372,7 @@ class GroovyCharSequenceMethodsTest extends GroovyTestCase { void testNormalizeDenormalize() { def text = 'the quick brown\nfox jumped\r\nover the lazy dog' - def csText = [toString : { -> text }] as CharSequence + def csText = makeCharSequence(text) assert csText.normalize() == text.normalize() assert csText.normalize().denormalize() == text.normalize().denormalize() }