diff --git a/src/main/org/apache/tools/ant/taskdefs/SQLExec.java b/src/main/org/apache/tools/ant/taskdefs/SQLExec.java index 30bd9f520..aaf18a3b6 100644 --- a/src/main/org/apache/tools/ant/taskdefs/SQLExec.java +++ b/src/main/org/apache/tools/ant/taskdefs/SQLExec.java @@ -20,6 +20,7 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; +import org.apache.tools.ant.util.StringUtils; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.FileSet; @@ -441,7 +442,7 @@ public class SQLExec extends JDBCTask { protected void runStatements(Reader reader, PrintStream out) throws SQLException, IOException { StringBuffer sql = new StringBuffer(); - String line = ""; + String line; BufferedReader in = new BufferedReader(reader); @@ -481,7 +482,7 @@ public class SQLExec extends JDBCTask { } } if ((delimiterType.equals(DelimiterType.NORMAL) - && sql.toString().endsWith(delimiter)) + && StringUtils.endsWith(sql, delimiter)) || (delimiterType.equals(DelimiterType.ROW) && line.equals(delimiter))) { @@ -574,8 +575,7 @@ public class SQLExec extends JDBCTask { * @throws SQLException on SQL problems. */ protected void printResults(PrintStream out) throws SQLException { - ResultSet rs = null; - rs = statement.getResultSet(); + ResultSet rs = statement.getResultSet(); try { printResults(rs, out); } finally { diff --git a/src/main/org/apache/tools/ant/util/StringUtils.java b/src/main/org/apache/tools/ant/util/StringUtils.java index 40acacb24..5141c64b2 100644 --- a/src/main/org/apache/tools/ant/util/StringUtils.java +++ b/src/main/org/apache/tools/ant/util/StringUtils.java @@ -98,4 +98,35 @@ public final class StringUtils { return sw.toString(); } + /** + * Checks that a string buffer ends up with a given string. It may sound trivial with the existing + * JDK API but the various implementation among JDKs can make those methods extremely resource intensive + * and perform poorly due to massive memory allocation and copying. See + * @param buffer the buffer to perform the check on + * @param suffix the suffix + * @return true if the character sequence represented by the + * argument is a suffix of the character sequence represented by + * the StringBuffer object; false otherwise. Note that the + * result will be true if the argument is the + * empty string. + */ + public static boolean endsWith(StringBuffer buffer, String suffix) { + if (suffix.length() > buffer.length()) { + return false; + } + // this loop is done on purpose to avoid memory allocation performance problems on various JDKs + // StringBuffer.lastIndexOf() was introduced in jdk 1.4 and implementation is ok though does allocation/copying + // StringBuffer.toString().endsWith() does massive memory allocation/copying on JDK 1.5 + // See http://issues.apache.org/bugzilla/show_bug.cgi?id=37169 + int endIndex = suffix.length() - 1; + int bufferIndex = buffer.length() - 1; + while ( endIndex >= 0 ) { + if ( buffer.charAt(bufferIndex) != suffix.charAt(endIndex) ) { + return false; + } + bufferIndex--; + endIndex--; + } + return true; + } } diff --git a/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java b/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java index 16c3c0e8c..47ea2a86c 100644 --- a/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java +++ b/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java @@ -55,4 +55,51 @@ public class StringUtilsTest extends TestCase { assertEquals("bcbcbc", res); } + public void testEndsWithBothEmpty() { + assertTrue( StringUtils.endsWith( new StringBuffer(), "") ); + } + + public void testEndsWithEmptyString() { + assertTrue( StringUtils.endsWith( new StringBuffer("12234545"), "") ); + } + + public void testEndsWithShorterString() { + assertTrue( StringUtils.endsWith( new StringBuffer("12345678"), "78")); + } + + public void testEndsWithSameString() { + assertTrue( StringUtils.endsWith( new StringBuffer("123"), "123")); + } + + public void testEndsWithLongerString() { + assertFalse( StringUtils.endsWith( new StringBuffer("12"), "1245")); + } + + public void testEndsWithNoMatch() { + assertFalse( StringUtils.endsWith( new StringBuffer("12345678"), "789")); + } + + public void testEndsWithEmptyBuffer() { + assertFalse( StringUtils.endsWith( new StringBuffer(""), "12345667") ); + } + + public void testEndsWithJDKPerf() { + StringBuffer buf = getFilledBuffer(1024*300, 'a'); + for (int i = 0; i < 1000; i++) { + assertTrue(buf.toString().endsWith("aa")); + } + } + + public void testEndsWithPerf() { + StringBuffer buf = getFilledBuffer(1024*300, 'a'); + for (int i = 0; i < 1000; i++) { + assertTrue(StringUtils.endsWith(buf, "aa")); + } + } + + private StringBuffer getFilledBuffer(int size, char ch) { + StringBuffer buf = new StringBuffer(size); + for (int i = 0; i < size; i++) { buf.append(ch); }; + return buf; + } }