LANG-805 RandomStringUtils.random(count, 0, 0, false, false, universe, random) always throws java.lang.ArrayIndexOutOfBoundsException
Merge fixes from LANG3 code
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/branches/LANG_2_X@1348424 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/commons/lang/RandomStringUtils.java b/src/main/java/org/apache/commons/lang/RandomStringUtils.java
index facbd1e..f7e2a4e 100644
--- a/src/main/java/org/apache/commons/lang/RandomStringUtils.java
+++ b/src/main/java/org/apache/commons/lang/RandomStringUtils.java
@@ -17,10 +17,11 @@
package org.apache.commons.lang;
import java.util.Random;
+
/**
- * <p>Operations for random <code>String</code>s.</p>
+ * <p>Operations for random {@code String}s.</p>
* <p>Currently <em>private high surrogate</em> characters are ignored.
- * These are unicode characters that fall between the values 56192 (db80)
+ * These are Unicode characters that fall between the values 56192 (db80)
* and 56319 (dbff) as we don't know how to handle them.
* High and low surrogates are correctly dealt with - that is if a
* high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f)
@@ -29,10 +30,6 @@
* chosen high surrogate. </p>
*
* <p>#ThreadSafe#</p>
- * @author Apache Software Foundation
- * @author <a href="mailto:steven@caswell.name">Steven Caswell</a>
- * @author Gary Gregory
- * @author Phil Steitz
* @since 1.0
* @version $Id$
*/
@@ -46,9 +43,9 @@
private static final Random RANDOM = new Random();
/**
- * <p><code>RandomStringUtils</code> instances should NOT be constructed in
+ * <p>{@code RandomStringUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used as
- * <code>RandomStringUtils.random(5);</code>.</p>
+ * {@code RandomStringUtils.random(5);}.</p>
*
* <p>This constructor is public to permit tools that require a JavaBean instance
* to operate.</p>
@@ -77,7 +74,7 @@
* specified.</p>
*
* <p>Characters will be chosen from the set of characters whose
- * ASCII value is between <code>32</code> and <code>126</code> (inclusive).</p>
+ * ASCII value is between {@code 32} and {@code 126} (inclusive).</p>
*
* @param count the length of random string to create
* @return the random string
@@ -136,9 +133,9 @@
* characters as indicated by the arguments.</p>
*
* @param count the length of random string to create
- * @param letters if <code>true</code>, generated string will include
+ * @param letters if {@code true}, generated string will include
* alphabetic characters
- * @param numbers if <code>true</code>, generated string will include
+ * @param numbers if {@code true}, generated string will include
* numeric characters
* @return the random string
*/
@@ -156,9 +153,9 @@
* @param count the length of random string to create
* @param start the position in set of chars to start at
* @param end the position in set of chars to end before
- * @param letters if <code>true</code>, generated string will include
+ * @param letters if {@code true}, generated string will include
* alphabetic characters
- * @param numbers if <code>true</code>, generated string will include
+ * @param numbers if {@code true}, generated string will include
* numeric characters
* @return the random string
*/
@@ -181,10 +178,10 @@
* @param letters only allow letters?
* @param numbers only allow numbers?
* @param chars the set of chars to choose randoms from.
- * If <code>null</code>, then it will use the set of all chars.
+ * If {@code null}, then it will use the set of all chars.
* @return the random string
* @throws ArrayIndexOutOfBoundsException if there are not
- * <code>(end - start) + 1</code> characters in the set array.
+ * {@code (end - start) + 1} characters in the set array.
*/
public static String random(int count, int start, int end, boolean letters, boolean numbers, char[] chars) {
return random(count, start, end, letters, numbers, chars, RANDOM);
@@ -194,13 +191,13 @@
* <p>Creates a random string based on a variety of options, using
* supplied source of randomness.</p>
*
- * <p>If start and end are both <code>0</code>, start and end are set
- * to <code>' '</code> and <code>'z'</code>, the ASCII printable
+ * <p>If start and end are both {@code 0}, start and end are set
+ * to {@code ' '} and {@code 'z'}, the ASCII printable
* characters, will be used, unless letters and numbers are both
- * <code>false</code>, in which case, start and end are set to
- * <code>0</code> and <code>Integer.MAX_VALUE</code>.
+ * {@code false}, in which case, start and end are set to
+ * {@code 0} and {@code Integer.MAX_VALUE}.
*
- * <p>If set is not <code>null</code>, characters between start and
+ * <p>If set is not {@code null}, characters between start and
* end are chosen.</p>
*
* <p>This method accepts a user-supplied {@link Random}
@@ -214,13 +211,13 @@
* @param end the position in set of chars to end before
* @param letters only allow letters?
* @param numbers only allow numbers?
- * @param chars the set of chars to choose randoms from.
- * If <code>null</code>, then it will use the set of all chars.
+ * @param chars the set of chars to choose randoms from, must not be empty.
+ * If {@code null}, then it will use the set of all chars.
* @param random a source of randomness.
* @return the random string
* @throws ArrayIndexOutOfBoundsException if there are not
- * <code>(end - start) + 1</code> characters in the set array.
- * @throws IllegalArgumentException if <code>count</code> < 0.
+ * {@code (end - start) + 1} characters in the set array.
+ * @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty.
* @since 2.0
*/
public static String random(int count, int start, int end, boolean letters, boolean numbers,
@@ -230,12 +227,20 @@
} else if (count < 0) {
throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
}
- if ((start == 0) && (end == 0)) {
- end = 'z' + 1;
- start = ' ';
- if (!letters && !numbers) {
- start = 0;
- end = Integer.MAX_VALUE;
+ if (chars != null && chars.length == 0) {
+ throw new IllegalArgumentException("The chars array must not be empty");
+ }
+
+ if (start == 0 && end == 0) {
+ if (chars != null) {
+ end = chars.length;
+ } else {
+ if (!letters && !numbers) {
+ end = Integer.MAX_VALUE;
+ } else {
+ end = 'z' + 1;
+ start = ' ';
+ }
}
}
@@ -249,10 +254,9 @@
} else {
ch = chars[random.nextInt(gap) + start];
}
- if ((letters && Character.isLetter(ch))
- || (numbers && Character.isDigit(ch))
- || (!letters && !numbers))
- {
+ if (letters && Character.isLetter(ch)
+ || numbers && Character.isDigit(ch)
+ || !letters && !numbers) {
if(ch >= 56320 && ch <= 57343) {
if(count == 0) {
count++;
@@ -289,13 +293,14 @@
* specified.</p>
*
* <p>Characters will be chosen from the set of characters
- * specified.</p>
+ * specified by the string, must not be empty.
+ * If null, the set of all characters is used.</p>
*
* @param count the length of random string to create
* @param chars the String containing the set of characters to use,
- * may be null
+ * may be null, but must not be empty
* @return the random string
- * @throws IllegalArgumentException if <code>count</code> < 0.
+ * @throws IllegalArgumentException if {@code count} < 0 or the string is empty.
*/
public static String random(int count, String chars) {
if (chars == null) {
@@ -314,7 +319,7 @@
* @param chars the character array containing the set of characters to use,
* may be null
* @return the random string
- * @throws IllegalArgumentException if <code>count</code> < 0.
+ * @throws IllegalArgumentException if {@code count} < 0.
*/
public static String random(int count, char[] chars) {
if (chars == null) {
diff --git a/src/site/changes/changes.xml b/src/site/changes/changes.xml
index 7f5c3f4..be5bf49 100644
--- a/src/site/changes/changes.xml
+++ b/src/site/changes/changes.xml
@@ -21,6 +21,7 @@
</properties>
<body>
<release version="2.7" date="TBA" description="TBA (requires minimum of Java 1.3)">
+ <action issue="LANG-805" type="fix">RandomStringUtils.random(count, 0, 0, false, false, universe, random) always throws java.lang.ArrayIndexOutOfBoundsException</action>
<action issue="LANG-803" type="fix">LocaleUtils - DCL idiom is not thread-safe.</action>
<action issue="LANG-677" type="fix">DateUtils isSameLocalTime() compares the hour using 12hour Calendar.HOUR instead of 24hour Calendar.HOUR_OF_DAY</action>
</release>
diff --git a/src/test/java/org/apache/commons/lang/RandomStringUtilsTest.java b/src/test/java/org/apache/commons/lang/RandomStringUtilsTest.java
index 966b973..1172b1a 100644
--- a/src/test/java/org/apache/commons/lang/RandomStringUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang/RandomStringUtilsTest.java
@@ -20,13 +20,9 @@
import java.lang.reflect.Modifier;
import java.util.Random;
-
/**
* Unit tests {@link org.apache.commons.lang.RandomStringUtils}.
*
- * @author <a href="mailto:steven@caswell.name">Steven Caswell</a>
- * @author <a href="mailto:ridesmet@users.sourceforge.net">Ringo De Smet</a>
- * @author Phil Steitz
* @version $Id$
*/
public class RandomStringUtilsTest extends junit.framework.TestCase {
@@ -127,9 +123,15 @@
r1 = RandomStringUtils.random(0);
assertEquals("random(0).equals(\"\")", "", r1);
-
}
+
+ public void testLANG805() {
+ long seed = System.currentTimeMillis();
+ assertEquals("aaa", RandomStringUtils.random(3,0,0,false,false,new char[]{'a'},new Random(seed)));
+ }
+
public void testExceptions() {
+ final char[] DUMMY = new char[]{'a'}; // valid char array
try {
RandomStringUtils.random(-1);
fail();
@@ -139,7 +141,11 @@
fail();
} catch (IllegalArgumentException ex) {}
try {
- RandomStringUtils.random(-1, new char[0]);
+ RandomStringUtils.random(-1, DUMMY);
+ fail();
+ } catch (IllegalArgumentException ex) {}
+ try {
+ RandomStringUtils.random(1, new char[0]); // must not provide empty array => IAE
fail();
} catch (IllegalArgumentException ex) {}
try {
@@ -147,15 +153,19 @@
fail();
} catch (IllegalArgumentException ex) {}
try {
+ RandomStringUtils.random(-1, (String)null);
+ fail();
+ } catch (IllegalArgumentException ex) {}
+ try {
RandomStringUtils.random(-1, 'a', 'z', false, false);
fail();
} catch (IllegalArgumentException ex) {}
try {
- RandomStringUtils.random(-1, 'a', 'z', false, false, new char[0]);
+ RandomStringUtils.random(-1, 'a', 'z', false, false, DUMMY);
fail();
} catch (IllegalArgumentException ex) {}
try {
- RandomStringUtils.random(-1, 'a', 'z', false, false, new char[0], new Random());
+ RandomStringUtils.random(-1, 'a', 'z', false, false, DUMMY, new Random());
fail();
} catch (IllegalArgumentException ex) {}
}
@@ -290,8 +300,8 @@
double sumSq = 0.0d;
double dev = 0.0d;
for (int i = 0; i < observed.length; i++) {
- dev = (double) (observed[i] - expected[i]);
- sumSq += dev * dev / (double) expected[i];
+ dev = observed[i] - expected[i];
+ sumSq += dev * dev / expected[i];
}
return sumSq;
}
@@ -315,8 +325,8 @@
for (int i=0; i < orig.length() && i < copy.length(); i++) {
char o = orig.charAt(i);
char c = copy.charAt(i);
- assertEquals("differs at " + i + "(" + Integer.toHexString((new Character(o)).hashCode()) + "," +
- Integer.toHexString((new Character(c)).hashCode()) + ")", o, c);
+ assertEquals("differs at " + i + "(" + Integer.toHexString(new Character(o).hashCode()) + "," +
+ Integer.toHexString(new Character(c).hashCode()) + ")", o, c);
}
// compare length also
assertEquals(orig.length(), copy.length());