From 86c1cfa6374547832ad54e7853839d10fabb90d4 Mon Sep 17 00:00:00 2001 From: Henri Yandell Date: Sun, 6 Sep 2009 22:56:09 +0000 Subject: [PATCH] Applying Stefan Zeller's performance improvement to StrBuilder (LANG-523) by doubling the size of the String in ensureCapacity. Tests indicate a hundredhold improvement in appending speed, which seems worth the doubling of data size. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@811944 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/commons/lang/Validate.java | 335 +++++++++++++++++- .../apache/commons/lang/text/StrBuilder.java | 2 +- 2 files changed, 333 insertions(+), 4 deletions(-) diff --git a/src/java/org/apache/commons/lang/Validate.java b/src/java/org/apache/commons/lang/Validate.java index d5d14597e..1d065c932 100644 --- a/src/java/org/apache/commons/lang/Validate.java +++ b/src/java/org/apache/commons/lang/Validate.java @@ -16,6 +16,7 @@ */ package org.apache.commons.lang; +import java.text.MessageFormat; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -31,10 +32,74 @@ import java.util.Map; * Validate.notNull( surname, "The surname must not be null"); * * + * All validate functions exist in 4 variants: + * + *

1st function with only the validation option

+ *
+ * Validate.isNotNull(surName);
+ * 
+ * + *

2nd function with an additional String message parameter. This should + * be used only if no additional parameters have to be provided. Instead of using + * String operations to create the message String, the following 3rd variant + * should be used.

+ *
+ * Validate.isNotNull(surName, "surname must be set");
+ * 
+ * + *

Since commons-lang-3.0, for each validation function a similar 3rd validation function exists + * with a list of additional message parameters as Objects in ellipsis notation. + * This is used instead of simply passing a message String due to performance reasons! + * When using a message string, all parameters would have to be string concatenated + * before the call, even if no problem arises which would cost performance.
+ * Instead of this, we will concatenate (with spaces) all given msgObjects.toString() + * only in case of a failed validation! If the first parameter of the msgObject is a + * String, it will be taken as the format string for {@code MessageFormat}.

+ * + *

Examples:

+ *

+ * Simply validating an Argument without further message: + *

+ * public void myFn(String argString, Integer argInt) {
+ *     Validate.notNull(argString);
+ *     Validate.notNull(argInt);
+ *     Validate.isTrue(argInt.intValue > 3);
+ * }
+ * 
+ *

+ * + *

+ * Validating an Argument and adding a message to the IllegalArgumentException: + *

+ * public void myFn(String argString, Integer argInt) {
+ *     Validate.notNull(argInt, "Integer parameter must be set);
+ *     Validate.isTrue(argInt.intValue > 3, "Integer parameter must be <=3!");
+ * }
+ * 
+ *

+ * + *

+ * If the first parameter of the msgObject is a String {@code MessageFormat} will be used: + *

+ *     Validate.isTrue(argInt1.intValue > argInt2.intValue, "param2 actually is {1} but must larger than param1 {0} !", argInt1, argInt2);
+ * 
+ *

+ * + *

The same function sometimes exists multiple times in a 4th form with a single message String parameter + * and an additional value parameter. This is essentially the same like the 3rd form, but with fixed + * object values to preserve backward compatibility with Validate 2.0!

+ *

If the message String contains a "{0}", it will be passed to + * {@code MessageFormat} with the value parameter as single Object parameter. If not, the value parameter + * will simply get concatenated to the message String separated with a space. + *

+ + * @see MessageFormat + * * @author Ola Berg * @author Stephen Colebourne * @author Gary Gregory * @author Norm Deane + * @author Mark Struberg * @since 2.0 * @version $Id$ */ @@ -74,7 +139,7 @@ public class Validate { */ public static void isTrue(boolean expression, String message, Object value) { if (expression == false) { - throw new IllegalArgumentException(message + value); + throw new IllegalArgumentException(getMessage(message, value)); } } @@ -100,7 +165,8 @@ public class Validate { */ public static void isTrue(boolean expression, String message, long value) { if (expression == false) { - throw new IllegalArgumentException(message + value); + + throw new IllegalArgumentException(getMessage(message, value)); } } @@ -127,7 +193,7 @@ public class Validate { */ public static void isTrue(boolean expression, String message, double value) { if (expression == false) { - throw new IllegalArgumentException(message + value); + throw new IllegalArgumentException(getMessage(message, value)); } } @@ -182,6 +248,32 @@ public class Validate { } } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the test result is false.

+ * + *

This is used when validating according to an arbitrary boolean expression, + * such as validating a primitive number or using your own custom validation + * expression.

+ * + *
+     * Validate.isTrue(argInt1.intValue > argInt2.intValue, 
+     *                 "param2 actually is {1} but must larger than param1 {0} !", argInt1, argInt2);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param expression a boolean expression + * @throws IllegalArgumentException if expression is false + */ + public static void isTrue(boolean expression, Object... msgObjects) { + if (expression == false) { + throw new IllegalArgumentException(getMessage(msgObjects)); + } + } + // notNull //--------------------------------------------------------------------------------- @@ -221,6 +313,28 @@ public class Validate { notNull(object, "The validated object is null"); } + + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument is null.

+ * + *
+     * Validate.notNull(myObject, "This happens while processing user {0}, currentUser);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param object Object to validate + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + */ + public static void notNull(Object object, Object... msgObjects) { + if (object == null) { + throw new IllegalArgumentException(getMessage(msgObjects)); + } + } + // notEmpty array //--------------------------------------------------------------------------------- @@ -259,6 +373,26 @@ public class Validate { notEmpty(array, "The validated array is empty"); } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument array is empty (null or no elements).

+ * + *
+     * Validate.notEmpty(myArray, "This happens while processing user {0}, currentUser);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param array the array to check is not empty + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the array is empty + */ + public static void notEmpty(Object[] array, Object... msgObjects) { + notEmpty(array, getMessage(msgObjects)); + } + // notEmpty collection //--------------------------------------------------------------------------------- @@ -297,6 +431,26 @@ public class Validate { notEmpty(collection, "The validated collection is empty"); } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument Collection is empty (null or no elements).

+ * + *
+     * Validate.notEmpty(myCollection, "This happens while processing user {0}, currentUser);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param collection the collection to check is not empty + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the collection is empty + */ + public static void notEmpty(Collection collection, Object... msgObjects) { + notEmpty(collection, getMessage(msgObjects)); + } + // notEmpty map //--------------------------------------------------------------------------------- @@ -335,6 +489,26 @@ public class Validate { notEmpty(map, "The validated map is empty"); } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument Map is empty (null or no elements).

+ * + *
+     * Validate.notEmpty(myMap, "This happens while processing user {0}, currentUser);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param map the map to check is not empty + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the map is empty + */ + public static void notEmpty(Map map, Object... msgObjects) { + notEmpty(map, getMessage(msgObjects)); + } + // notEmpty string //--------------------------------------------------------------------------------- @@ -373,6 +547,26 @@ public class Validate { notEmpty(string, "The validated string is empty"); } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument String is empty (null or zero length).

+ * + *
+     * Validate.notEmpty(myString);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + * @param string the string to check is not empty + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the string is empty + */ + public static void notEmpty(String string, Object... msgObjects) { + notEmpty(string, getMessage(msgObjects)); + } + // notNullElements array //--------------------------------------------------------------------------------- @@ -429,6 +623,36 @@ public class Validate { } } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument array has null elements or is + * null.

+ * + *
+     * Validate.noNullElements(myArray);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + *

If the array is null then the message in the exception is 'The validated object is null'.

+ * + * @param array the array to check + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the array has null + * elements or is null + */ + public static void noNullElements(Object[] array, Object... msgObjects) { + Validate.notNull(array); + for (int i = 0; i < array.length; i++) { + if (array[i] == null) { + //X TODO maybe we can add 'i' as 0-th element? + throw new IllegalArgumentException(getMessage(msgObjects)); + } + } + } + // notNullElements collection //--------------------------------------------------------------------------------- @@ -485,6 +709,41 @@ public class Validate { } } + /** + *

Validate an argument, throwing IllegalArgumentException + * if the argument Collection has null elements or is + * null.

+ * + *
+     * Validate.noNullElements(myCollection);
+     * 
+ * + *

If the first msgObject is a String, the {@code MessageFormat} will be used to construct the message

+ *

Otherwise the message in the exception is 'Validation failed: ' followed by all given + * parameters delimited with spaces.

+ * + *

If the collection is null then the message in the exception is 'The validated object is null'.

+ * + * @param collection the collection to check + * @param msgObjects additional Objects added as text message to the InvalidArgumentException + * @throws IllegalArgumentException if the collection has + * null elements or is null + */ + public static void noNullElements(Collection collection, Object... msgObjects) { + Validate.notNull(collection); + int i = 0; + for (Iterator it = collection.iterator(); it.hasNext(); i++) { + if (it.next() == null) { + //X TODO how about adding 'i' as 0-th element? + throw new IllegalArgumentException(getMessage(msgObjects)); + } + } + } + + + // allElementsOfType collection + //--------------------------------------------------------------------------------- + /** *

Validate an argument, throwing IllegalArgumentException * if the argument collection is null or has elements that @@ -541,4 +800,74 @@ public class Validate { } } + /** + *

+ * Validate an argument, throwing IllegalArgumentException if the argument collection is + * null or has elements that are not of type clazz or a subclass. + *

+ * + *
+     * Validate.allElementsOfType(collection, String.class);
+     * 
+ * + *

+ * The message in the exception is 'The validated collection contains an element not of type clazz at index: '. + *

+ * + * @param collection + * the collection to check, not null + * @param clazz + * the Class which the collection's elements are expected to be, not null + * @since 2.1 + */ + public static void allElementsOfType(Collection collection, Class clazz, Object... msgObjects) { + Validate.notNull(collection); + Validate.notNull(clazz); + int i = 0; + for (Iterator it = collection.iterator(); it.hasNext(); i++) { + if (clazz.isInstance(it.next()) == false) { + //X TODO how to add clazz.getName() and i? + throw new IllegalArgumentException(getMessage(msgObjects)); + } + } + } + + // private helper functions + //--------------------------------------------------------------------------------- + + + /** + * private helper function to create an error message from the given Objects + * If the first object in msgObjects is of type {@code String} then + * {@code MessageFormat} will be used to format the output message. + * + * @param msgObjects + * @return concatenated String representation of all the objects + */ + private static String getMessage(Object... msgObjects) { + if (msgObjects.length > 0 && msgObjects[0] instanceof String) { + String message = (String) msgObjects[0]; + if (msgObjects.length == 2 && !message.matches("[^\\{]*\\{\\d*\\}.*")) { + // if it doesn't contain {0}, {1} etc we simply use string concatenation + return message + msgObjects[1]; // no space between to act like original function! + } + + MessageFormat form = new MessageFormat((String) msgObjects[0]); + Object[] params = new Object[msgObjects.length - 1]; + System.arraycopy(msgObjects, 1, params, 0, msgObjects.length - 1); + return form.format(params); + } + else { + StringBuffer sb = new StringBuffer("Validation failed: ["); + for(int i = 0; i < msgObjects.length; i++) { + if (i > 0) { + sb.append(' '); + } + sb.append(msgObjects[i]); + } + sb.append(']'); + return sb.toString(); + } + } + } diff --git a/src/java/org/apache/commons/lang/text/StrBuilder.java b/src/java/org/apache/commons/lang/text/StrBuilder.java index b8894d495..c823fecc3 100644 --- a/src/java/org/apache/commons/lang/text/StrBuilder.java +++ b/src/java/org/apache/commons/lang/text/StrBuilder.java @@ -231,7 +231,7 @@ public class StrBuilder implements CharSequence, Appendable { public StrBuilder ensureCapacity(int capacity) { if (capacity > buffer.length) { char[] old = buffer; - buffer = new char[capacity]; + buffer = new char[capacity * 2]; System.arraycopy(old, 0, buffer, 0, size); } return this;