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;
+ }
}