Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/ArrayUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/ArrayUtils.java (.../ArrayUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/ArrayUtils.java (.../ArrayUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,58 +1,22 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; @@ -62,22 +26,25 @@ import org.apache.commons.lang.builder.ToStringStyle; /** - *

Operations on arrays, primitive arrays (like int[]) and primitive wrapper arrays - * (like Integer[]).

- * + *

Operations on arrays, primitive arrays (like int[]) and + * primitive wrapper arrays (like Integer[]).

+ * *

This class tries to handle null input gracefully. * An exception will not be thrown for a null * array input. However, an Object array that contains a null * element may throw an exception. Each method documents its behaviour.

* - * @author Stephen Colebourne + *

#ThreadSafe#

+ * @author Apache Software Foundation * @author Moritz Petersen * @author Fredrik Westermarck * @author Nikolay Metchev * @author Matthew Hawthorne * @author Tim O'Brien * @author Pete Gieser * @author Gary Gregory + * @author Ashwin S + * @author Maarten Coene * @since 2.0 * @version $Id$ */ @@ -161,15 +128,23 @@ public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; /** + * The index value when an element is not found in a list or array: -1. + * This value is returned by methods in this class and can also be used in comparisons with values returned by + * various method from {@link java.util.List}. + */ + public static final int INDEX_NOT_FOUND = -1; + + /** *

ArrayUtils instances should NOT be constructed in standard programming. * Instead, the class should be used as ArrayUtils.clone(new int[] {2}).

* *

This constructor is public to permit tools that require a JavaBean instance * to operate.

*/ public ArrayUtils() { + super(); } - + // Basic methods handling multi-dimensional arrays //----------------------------------------------------------------------- /** @@ -179,11 +154,11 @@ * multi-dimensional primitive arrays.

* *

The format is that of Java source code, for example {a,b}.

- * + * * @param array the array to get a toString for, may be null * @return a String representation of the array, '{}' if null array input */ - public static String toString(final Object array) { + public static String toString(Object array) { return toString(array, "{}"); } @@ -194,12 +169,12 @@ * multi-dimensional primitive arrays.

* *

The format is that of Java source code, for example {a,b}.

- * + * * @param array the array to get a toString for, may be null * @param stringIfNull the String to return if the array is null * @return a String representation of the array - */ - public static String toString(final Object array, final String stringIfNull) { + */ + public static String toString(Object array, String stringIfNull) { if (array == null) { return stringIfNull; } @@ -208,27 +183,27 @@ /** *

Get a hashCode for an array handling multi-dimensional arrays correctly.

- * + * *

Multi-dimensional primitive arrays are also handled correctly by this method.

- * + * * @param array the array to get a hashCode for, may be null * @return a hashCode for the array, zero if null array input */ - public static int hashCode(final Object array) { + public static int hashCode(Object array) { return new HashCodeBuilder().append(array).toHashCode(); } /** *

Compares two arrays, using equals(), handling multi-dimensional arrays * correctly.

- * + * *

Multi-dimensional primitive arrays are also handled correctly by this method.

- * - * @param array1 the array to get a hashCode for, may be null - * @param array2 the array to get a hashCode for, may be null + * + * @param array1 the left hand array to compare, may be null + * @param array2 the right hand array to compare, may be null * @return true if the arrays are equal */ - public static boolean isEquals(final Object array1, final Object array2) { + public static boolean isEquals(Object array1, Object array2) { return new EqualsBuilder().append(array1, array2).isEquals(); } @@ -248,18 +223,18 @@ * {"GREEN", "#00FF00"}, * {"BLUE", "#0000FF"}}); * - * - *

This method returns null if null array input.

* - * @param array an array whose elements are either a {@link java.util.Map.Entry} or + *

This method returns null for a null input array.

+ * + * @param array an array whose elements are either a {@link java.util.Map.Entry} or * an Array containing at least two elements, may be null * @return a Map that was created from the array * @throws IllegalArgumentException if one element of this Array is * itself an Array containing less then two elements * @throws IllegalArgumentException if the array contains elements other * than {@link java.util.Map.Entry} and an Array */ - public static Map toMap(final Object[] array) { + public static Map toMap(Object[] array) { if (array == null) { return null; } @@ -272,13 +247,13 @@ } else if (object instanceof Object[]) { Object[] entry = (Object[]) object; if (entry.length < 2) { - throw new IllegalArgumentException("Array element " + i + ", '" + throw new IllegalArgumentException("Array element " + i + ", '" + object + "', has a length less than 2"); } map.put(entry[0], entry[1]); } else { - throw new IllegalArgumentException("Array element " + i + ", '" + throw new IllegalArgumentException("Array element " + i + ", '" + object + "', is neither of type Map.Entry nor an Array"); } @@ -292,15 +267,15 @@ *

Shallow clones an array returning a typecast result and handling * null.

* - *

The objecs in the array are not cloned, thus there is no special + *

The objects in the array are not cloned, thus there is no special * handling for multi-dimensional arrays.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array the array to shallow clone, may be null * @return the cloned array, null if null input */ - public static Object[] clone(final Object[] array) { + public static Object[] clone(Object[] array) { if (array == null) { return null; } @@ -311,12 +286,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static long[] clone(final long[] array) { + public static long[] clone(long[] array) { if (array == null) { return null; } @@ -327,8 +302,8 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ @@ -343,12 +318,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static short[] clone(final short[] array) { + public static short[] clone(short[] array) { if (array == null) { return null; } @@ -359,12 +334,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static char[] clone(final char[] array) { + public static char[] clone(char[] array) { if (array == null) { return null; } @@ -375,12 +350,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static byte[] clone(final byte[] array) { + public static byte[] clone(byte[] array) { if (array == null) { return null; } @@ -391,12 +366,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static double[] clone(final double[] array) { + public static double[] clone(double[] array) { if (array == null) { return null; } @@ -407,12 +382,12 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static float[] clone(final float[] array) { + public static float[] clone(float[] array) { if (array == null) { return null; } @@ -423,32 +398,755 @@ *

Clones an array returning a typecast result and handling * null.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array the array to clone, may be null * @return the cloned array, null if null input */ - public static boolean[] clone(final boolean[] array) { + public static boolean[] clone(boolean[] array) { if (array == null) { return null; } return (boolean[]) array.clone(); } + // nullToEmpty + //----------------------------------------------------------------------- + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Object[] nullToEmpty(Object[] array) { + if (array == null || array.length == 0) { + return EMPTY_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static String[] nullToEmpty(String[] array) { + if (array == null || array.length == 0) { + return EMPTY_STRING_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static long[] nullToEmpty(long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static int[] nullToEmpty(int[] array) { + if (array == null || array.length == 0) { + return EMPTY_INT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static short[] nullToEmpty(short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static char[] nullToEmpty(char[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static byte[] nullToEmpty(byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static double[] nullToEmpty(double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static float[] nullToEmpty(float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static boolean[] nullToEmpty(boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Long[] nullToEmpty(Long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Integer[] nullToEmpty(Integer[] array) { + if (array == null || array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Short[] nullToEmpty(Short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Character[] nullToEmpty(Character[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Byte[] nullToEmpty(Byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Double[] nullToEmpty(Double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Float[] nullToEmpty(Float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a null + * reference to an empty one.

+ * + *

This method returns an empty array for a null input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty public static references in this class.

+ * + * @param array the array to check for null or empty + * @return the same array, public static empty array if null or empty input + * @since 2.5 + */ + public static Boolean[] nullToEmpty(Boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; + } + return array; + } + + // Subarrays + //----------------------------------------------------------------------- + /** + *

Produces a new array containing the elements between + * the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + *

The component type of the subarray is always the same as + * that of the input array. Thus, if the input is an array of type + * Date, the following usage is envisaged:

+ * + *
+     * Date[] someDates = (Date[])ArrayUtils.subarray(allDates, 2, 5);
+     * 
+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static Object[] subarray(Object[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + Class type = array.getClass().getComponentType(); + if (newSize <= 0) { + return (Object[]) Array.newInstance(type, 0); + } + Object[] subarray = (Object[]) Array.newInstance(type, newSize); + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new long array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static long[] subarray(long[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_LONG_ARRAY; + } + + long[] subarray = new long[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new int array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static int[] subarray(int[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_INT_ARRAY; + } + + int[] subarray = new int[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new short array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static short[] subarray(short[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_SHORT_ARRAY; + } + + short[] subarray = new short[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new char array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static char[] subarray(char[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_CHAR_ARRAY; + } + + char[] subarray = new char[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new byte array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BYTE_ARRAY; + } + + byte[] subarray = new byte[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new double array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static double[] subarray(double[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_DOUBLE_ARRAY; + } + + double[] subarray = new double[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new float array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static float[] subarray(float[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_FLOAT_ARRAY; + } + + float[] subarray = new float[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new boolean array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static boolean[] subarray(boolean[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BOOLEAN_ARRAY; + } + + boolean[] subarray = new boolean[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + // Is same length //----------------------------------------------------------------------- /** *

Checks whether two arrays are the same length, treating * null arrays as length 0. * *

Any multi-dimensional aspects of the arrays are ignored.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array - */ - public static boolean isSameLength(final Object[] array1, final Object[] array2) { + */ + public static boolean isSameLength(Object[] array1, Object[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -460,13 +1158,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final long[] array1, final long[] array2) { + public static boolean isSameLength(long[] array1, long[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -478,13 +1176,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final int[] array1, final int[] array2) { + public static boolean isSameLength(int[] array1, int[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -496,13 +1194,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final short[] array1, final short[] array2) { + public static boolean isSameLength(short[] array1, short[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -514,13 +1212,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final char[] array1, final char[] array2) { + public static boolean isSameLength(char[] array1, char[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -532,13 +1230,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final byte[] array1, final byte[] array2) { + public static boolean isSameLength(byte[] array1, byte[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -550,13 +1248,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final double[] array1, final double[] array2) { + public static boolean isSameLength(double[] array1, double[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -568,13 +1266,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final float[] array1, final float[] array2) { + public static boolean isSameLength(float[] array1, float[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -586,13 +1284,13 @@ /** *

Checks whether two arrays are the same length, treating * null arrays as length 0.

- * + * * @param array1 the first array, may be null * @param array2 the second array, may be null * @return true if length of arrays matches, treating * null as an empty array */ - public static boolean isSameLength(final boolean[] array1, final boolean[] array2) { + public static boolean isSameLength(boolean[] array1, boolean[] array2) { if ((array1 == null && array2 != null && array2.length > 0) || (array2 == null && array1 != null && array1.length > 0) || (array1 != null && array2 != null && array1.length != array2.length)) { @@ -601,16 +1299,44 @@ return true; } + //----------------------------------------------------------------------- /** + *

Returns the length of the specified array. + * This method can deal with Object arrays and with primitive arrays.

+ * + *

If the input array is null, 0 is returned.

+ * + *
+     * ArrayUtils.getLength(null)            = 0
+     * ArrayUtils.getLength([])              = 0
+     * ArrayUtils.getLength([null])          = 1
+     * ArrayUtils.getLength([true, false])   = 2
+     * ArrayUtils.getLength([1, 2, 3])       = 3
+     * ArrayUtils.getLength(["a", "b", "c"]) = 3
+     * 
+ * + * @param array the array to retrieve the length from, may be null + * @return The length of the array, or 0 if the array is null + * @throws IllegalArgumentException if the object arguement is not an array. + * @since 2.1 + */ + public static int getLength(Object array) { + if (array == null) { + return 0; + } + return Array.getLength(array); + } + + /** *

Checks whether two arrays are the same type taking into account * multi-dimensional arrays.

- * + * * @param array1 the first array, must not be null * @param array2 the second array, must not be null * @return true if type of arrays matches * @throws IllegalArgumentException if either array is null - */ - public static boolean isSameType(final Object array1, final Object array2) { + */ + public static boolean isSameType(Object array1, Object array2) { if (array1 == null || array2 == null) { throw new IllegalArgumentException("The Array must not be null"); } @@ -619,16 +1345,16 @@ // Reverse //----------------------------------------------------------------------- - /** + /** *

Reverses the order of the given array.

* *

There is no special handling for multi-dimensional arrays.

* - *

This method does nothing if null array input.

- * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final Object[] array) { + public static void reverse(Object[] array) { if (array == null) { return; } @@ -646,12 +1372,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final long[] array) { + public static void reverse(long[] array) { if (array == null) { return; } @@ -669,12 +1395,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final int[] array) { + public static void reverse(int[] array) { if (array == null) { return; } @@ -692,12 +1418,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final short[] array) { + public static void reverse(short[] array) { if (array == null) { return; } @@ -715,12 +1441,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final char[] array) { + public static void reverse(char[] array) { if (array == null) { return; } @@ -738,12 +1464,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final byte[] array) { + public static void reverse(byte[] array) { if (array == null) { return; } @@ -761,12 +1487,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final double[] array) { + public static void reverse(double[] array) { if (array == null) { return; } @@ -784,12 +1510,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final float[] array) { + public static void reverse(float[] array) { if (array == null) { return; } @@ -807,12 +1533,12 @@ /** *

Reverses the order of the given array.

- * - *

This method does nothing if null array input.

- * + * + *

This method does nothing for a null input array.

+ * * @param array the array to reverse, may be null */ - public static void reverse(final boolean[] array) { + public static void reverse(boolean[] array) { if (array == null) { return; } @@ -830,40 +1556,40 @@ // IndexOf search // ---------------------------------------------------------------------- - + // Object IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given object in the array.

+ *

Finds the index of the given object in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param objectToFind the object to find, may be null - * @return the index of the object within the array, - * -1 if not found or null array input + * @return the index of the object within the array, + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final Object[] array, final Object objectToFind) { + public static int indexOf(Object[] array, Object objectToFind) { return indexOf(array, objectToFind, 0); } /** - *

Find the index of the given object in the array starting at the given index.

+ *

Finds the index of the given object in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param objectToFind the object to find, may be null * @param startIndex the index to start searching at * @return the index of the object within the array starting at the index, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final Object[] array, final Object objectToFind, int startIndex) { + public static int indexOf(Object[] array, Object objectToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -874,50 +1600,50 @@ return i; } } - } else { + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { for (int i = startIndex; i < array.length; i++) { if (objectToFind.equals(array[i])) { return i; } } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given object within the array.

+ *

Finds the last index of the given object within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param objectToFind the object to find, may be null * @return the last index of the object within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final Object[] array, final Object objectToFind) { + public static int lastIndexOf(Object[] array, Object objectToFind) { return lastIndexOf(array, objectToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given object in the array starting at the given index.

+ *

Finds the last index of the given object in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than * the array length will search from the end of the array.

- * + * * @param array the array to traverse for looking for the object, may be null * @param objectToFind the object to find, may be null * @param startIndex the start index to travers backwards from * @return the last index of the object within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final Object[] array, final Object objectToFind, int startIndex) { + public static int lastIndexOf(Object[] array, Object objectToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -927,62 +1653,62 @@ return i; } } - } else { + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { for (int i = startIndex; i >= 0; i--) { if (objectToFind.equals(array[i])) { return i; } } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the object is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param objectToFind the object to find * @return true if the array contains the object */ - public static boolean contains(final Object[] array, final Object objectToFind) { - return (indexOf(array, objectToFind) != -1); + public static boolean contains(Object[] array, Object objectToFind) { + return indexOf(array, objectToFind) != INDEX_NOT_FOUND; } // long IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final long[] array, final long valueToFind) { + public static int indexOf(long[] array, long valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final long[] array, final long valueToFind, int startIndex) { + public static int indexOf(long[] array, long valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -992,43 +1718,43 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final long[] array, final long valueToFind) { + public static int lastIndexOf(long[] array, long valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final long[] array, final long valueToFind, int startIndex) { + public static int lastIndexOf(long[] array, long valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1037,55 +1763,55 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final long[] array, final long valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(long[] array, long valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } // int IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final int[] array, final int valueToFind) { + public static int indexOf(int[] array, int valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final int[] array, final int valueToFind, int startIndex) { + public static int indexOf(int[] array, int valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1095,43 +1821,43 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final int[] array, final int valueToFind) { + public static int lastIndexOf(int[] array, int valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final int[] array, final int valueToFind, int startIndex) { + public static int lastIndexOf(int[] array, int valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1140,55 +1866,55 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final int[] array, final int valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(int[] array, int valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } // short IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final short[] array, final short valueToFind) { + public static int indexOf(short[] array, short valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final short[] array, final short valueToFind, int startIndex) { + public static int indexOf(short[] array, short valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1198,43 +1924,43 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final short[] array, final short valueToFind) { + public static int lastIndexOf(short[] array, short valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final short[] array, final short valueToFind, int startIndex) { + public static int lastIndexOf(short[] array, short valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1243,55 +1969,163 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final short[] array, final short valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(short[] array, short valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } + // char IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * + * @param array the array to search through for the object, may be null + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * + * @param array the array to search through for the object, may be null + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * + * @param array the array to travers backwords looking for the object, may be null + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be null + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns false if a null array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return true if the array contains the object + * @since 2.1 + */ + public static boolean contains(char[] array, char valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + // byte IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final byte[] array, final byte valueToFind) { + public static int indexOf(byte[] array, byte valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final byte[] array, final byte valueToFind, int startIndex) { + public static int indexOf(byte[] array, byte valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1301,43 +2135,43 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final byte[] array, final byte valueToFind) { + public static int lastIndexOf(byte[] array, byte valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final byte[] array, final byte valueToFind, int startIndex) { + public static int lastIndexOf(byte[] array, byte valueToFind, int startIndex) { if (array == null) { - return -1; + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1346,72 +2180,72 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final byte[] array, final byte valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(byte[] array, byte valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } // double IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final double[] array, final double valueToFind) { + public static int indexOf(double[] array, double valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value within a given tolerance in the array. + *

Finds the index of the given value within a given tolerance in the array. * This method will return the index of the first value which falls between the region * defined by valueToFind - tolerance and valueToFind + tolerance.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param tolerance tolerance of the search * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final double[] array, final double valueToFind, final double tolerance) { + public static int indexOf(double[] array, double valueToFind, double tolerance) { return indexOf(array, valueToFind, 0, tolerance); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final double[] array, final double valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int indexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1421,29 +2255,29 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the index of the given value in the array starting at the given index. + *

Finds the index of the given value in the array starting at the given index. * This method will return the index of the first value which falls between the region * defined by valueToFind - tolerance and valueToFind + tolerance.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @param tolerance tolerance of the search * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final double[] array, final double valueToFind, int startIndex, double tolerance) { - if (array == null || array.length == 0) { - return -1; + public static int indexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1455,60 +2289,60 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final double[] array, final double valueToFind) { + public static int lastIndexOf(double[] array, double valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value within a given tolerance in the array. + *

Finds the last index of the given value within a given tolerance in the array. * This method will return the index of the last value which falls between the region * defined by valueToFind - tolerance and valueToFind + tolerance.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param tolerance tolerance of the search * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final double[] array, final double valueToFind, final double tolerance) { + public static int lastIndexOf(double[] array, double valueToFind, double tolerance) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final double[] array, final double valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int lastIndexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1517,32 +2351,32 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value in the array starting at the given index. + *

Finds the last index of the given value in the array starting at the given index. * This method will return the index of the last value which falls between the region * defined by valueToFind - tolerance and valueToFind + tolerance.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @param tolerance search for value within plus/minus this amount * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final double[] array, final double valueToFind, int startIndex, double tolerance) { - if (array == null || array.length == 0) { - return -1; + public static int lastIndexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1553,25 +2387,25 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final double[] array, final double valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(double[] array, double valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } /** *

Checks if a value falling within the given tolerance is in the - * given array. If the array contains a value within the inclusive range + * given array. If the array contains a value within the inclusive range * defined by (value - tolerance) to (value + tolerance).

* *

The method returns false if a null array @@ -1582,43 +2416,43 @@ * @param tolerance the array contains the tolerance of the search * @return true if value falling within tolerance is in array */ - public static boolean contains(final double[] array, final double valueToFind, final double tolerance) { - return (indexOf(array, valueToFind, 0, tolerance) != -1); + public static boolean contains(double[] array, double valueToFind, double tolerance) { + return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND; } // float IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final float[] array, final float valueToFind) { + public static int indexOf(float[] array, float valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final float[] array, final float valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int indexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1628,43 +2462,43 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final float[] array, final float valueToFind) { + public static int lastIndexOf(float[] array, float valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than the + * array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final float[] array, final float valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int lastIndexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1673,55 +2507,56 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final float[] array, final float valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(float[] array, float valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } // boolean IndexOf //----------------------------------------------------------------------- /** - *

Find the index of the given value in the array.

+ *

Finds the index of the given value in the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int indexOf(final boolean[] array, final boolean valueToFind) { + public static int indexOf(boolean[] array, boolean valueToFind) { return indexOf(array, valueToFind, 0); } /** - *

Find the index of the given value in the array starting at the given index.

+ *

Finds the index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* *

A negative startIndex is treated as zero. A startIndex larger than the array - * length will return -1.

- * + * length will return {@link #INDEX_NOT_FOUND} (-1).

+ * * @param array the array to search through for the object, may be null * @param valueToFind the value to find * @param startIndex the index to start searching at * @return the index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null + * array input */ - public static int indexOf(final boolean[] array, final boolean valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int indexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { startIndex = 0; @@ -1731,43 +2566,44 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** - *

Find the last index of the given value within the array.

+ *

Finds the last index of the given value within the array.

* - *

This method returns -1 if null array input.

- * + *

This method returns {@link #INDEX_NOT_FOUND} (-1) if + * null array input.

+ * * @param array the array to travers backwords looking for the object, may be null * @param valueToFind the object to find * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final boolean[] array, final boolean valueToFind) { + public static int lastIndexOf(boolean[] array, boolean valueToFind) { return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); } /** - *

Find the last index of the given value in the array starting at the given index.

+ *

Finds the last index of the given value in the array starting at the given index.

* - *

This method returns -1 if null array input.

+ *

This method returns {@link #INDEX_NOT_FOUND} (-1) for a null input array.

* - *

A negative startIndex will return -1. A startIndex larger than the array - * length will search from the end of the array.

- * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} (-1). A startIndex larger than + * the array length will search from the end of the array.

+ * * @param array the array to traverse for looking for the object, may be null * @param valueToFind the value to find * @param startIndex the start index to travers backwards from * @return the last index of the value within the array, - * -1 if not found or null array input + * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input */ - public static int lastIndexOf(final boolean[] array, final boolean valueToFind, int startIndex) { - if (array == null || array.length == 0) { - return -1; + public static int lastIndexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; } if (startIndex < 0) { - return -1; + return INDEX_NOT_FOUND; } else if (startIndex >= array.length) { startIndex = array.length - 1; } @@ -1776,37 +2612,105 @@ return i; } } - return -1; + return INDEX_NOT_FOUND; } /** *

Checks if the value is in the given array.

* *

The method returns false if a null array is passed in.

- * + * * @param array the array to search through * @param valueToFind the value to find * @return true if the array contains the object */ - public static boolean contains(final boolean[] array, final boolean valueToFind) { - return (indexOf(array, valueToFind) != -1); + public static boolean contains(boolean[] array, boolean valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; } // Primitive/Object array converters // ---------------------------------------------------------------------- - + + // Character array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Characters to primitives.

+ * + *

This method returns null for a null input array.

+ * + * @param array a Character array, may be null + * @return a char array, null if null array input + * @throws NullPointerException if array content is null + */ + public static char[] toPrimitive(Character[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].charValue(); + } + return result; + } + + /** + *

Converts an array of object Character to primitives handling null.

+ * + *

This method returns null for a null input array.

+ * + * @param array a Character array, may be null + * @param valueForNull the value to insert if null found + * @return a char array, null if null array input + */ + public static char[] toPrimitive(Character[] array, char valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + Character b = array[i]; + result[i] = (b == null ? valueForNull : b.charValue()); + } + return result; + } + + /** + *

Converts an array of primitive chars to objects.

+ * + *

This method returns null for a null input array.

+ * + * @param array a char array + * @return a Character array, null if null array input + */ + public static Character[] toObject(char[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + final Character[] result = new Character[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = new Character(array[i]); + } + return result; + } + // Long array converters // ---------------------------------------------------------------------- /** *

Converts an array of object Longs to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Long array, may be null * @return a long array, null if null array input * @throws NullPointerException if array content is null */ - public static long[] toPrimitive(final Long[] array) { + public static long[] toPrimitive(Long[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1818,17 +2722,17 @@ } return result; } - + /** *

Converts an array of object Long to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Long array, may be null * @param valueForNull the value to insert if null found * @return a long array, null if null array input */ - public static long[] toPrimitive(final Long[] array, final long valueForNull) { + public static long[] toPrimitive(Long[] array, long valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -1841,16 +2745,16 @@ } return result; } - + /** *

Converts an array of primitive longs to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a long array * @return a Long array, null if null array input */ - public static Long[] toObject(final long[] array) { + public static Long[] toObject(long[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1868,13 +2772,13 @@ /** *

Converts an array of object Integers to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Integer array, may be null * @return an int array, null if null array input * @throws NullPointerException if array content is null */ - public static int[] toPrimitive(final Integer[] array) { + public static int[] toPrimitive(Integer[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1889,14 +2793,14 @@ /** *

Converts an array of object Integer to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Integer array, may be null * @param valueForNull the value to insert if null found * @return an int array, null if null array input */ - public static int[] toPrimitive(final Integer[] array, final int valueForNull) { + public static int[] toPrimitive(Integer[] array, int valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -1913,12 +2817,12 @@ /** *

Converts an array of primitive ints to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array an int array * @return an Integer array, null if null array input */ - public static Integer[] toObject(final int[] array) { + public static Integer[] toObject(int[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1930,19 +2834,19 @@ } return result; } - + // Short array converters // ---------------------------------------------------------------------- /** *

Converts an array of object Shorts to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Short array, may be null * @return a byte array, null if null array input * @throws NullPointerException if array content is null */ - public static short[] toPrimitive(final Short[] array) { + public static short[] toPrimitive(Short[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1957,14 +2861,14 @@ /** *

Converts an array of object Short to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Short array, may be null * @param valueForNull the value to insert if null found * @return a byte array, null if null array input */ - public static short[] toPrimitive(final Short[] array, final short valueForNull) { + public static short[] toPrimitive(Short[] array, short valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -1981,12 +2885,12 @@ /** *

Converts an array of primitive shorts to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a short array * @return a Short array, null if null array input */ - public static Short[] toObject(final short[] array) { + public static Short[] toObject(short[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -1997,20 +2901,20 @@ result[i] = new Short(array[i]); } return result; - } + } // Byte array converters // ---------------------------------------------------------------------- /** *

Converts an array of object Bytes to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Byte array, may be null * @return a byte array, null if null array input * @throws NullPointerException if array content is null */ - public static byte[] toPrimitive(final Byte[] array) { + public static byte[] toPrimitive(Byte[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2025,14 +2929,14 @@ /** *

Converts an array of object Bytes to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Byte array, may be null * @param valueForNull the value to insert if null found * @return a byte array, null if null array input */ - public static byte[] toPrimitive(final Byte[] array, final byte valueForNull) { + public static byte[] toPrimitive(Byte[] array, byte valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -2049,12 +2953,12 @@ /** *

Converts an array of primitive bytes to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a byte array * @return a Byte array, null if null array input */ - public static Byte[] toObject(final byte[] array) { + public static Byte[] toObject(byte[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2065,20 +2969,20 @@ result[i] = new Byte(array[i]); } return result; - } - + } + // Double array converters // ---------------------------------------------------------------------- /** *

Converts an array of object Doubles to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Double array, may be null * @return a double array, null if null array input * @throws NullPointerException if array content is null */ - public static double[] toPrimitive(final Double[] array) { + public static double[] toPrimitive(Double[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2093,14 +2997,14 @@ /** *

Converts an array of object Doubles to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Double array, may be null * @param valueForNull the value to insert if null found * @return a double array, null if null array input */ - public static double[] toPrimitive(final Double[] array, final double valueForNull) { + public static double[] toPrimitive(Double[] array, double valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -2117,12 +3021,12 @@ /** *

Converts an array of primitive doubles to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a double array * @return a Double array, null if null array input */ - public static Double[] toObject(final double[] array) { + public static Double[] toObject(double[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2140,13 +3044,13 @@ /** *

Converts an array of object Floats to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Float array, may be null * @return a float array, null if null array input * @throws NullPointerException if array content is null */ - public static float[] toPrimitive(final Float[] array) { + public static float[] toPrimitive(Float[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2161,14 +3065,14 @@ /** *

Converts an array of object Floats to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Float array, may be null * @param valueForNull the value to insert if null found * @return a float array, null if null array input */ - public static float[] toPrimitive(final Float[] array, final float valueForNull) { + public static float[] toPrimitive(Float[] array, float valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -2185,12 +3089,12 @@ /** *

Converts an array of primitive floats to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a float array * @return a Float array, null if null array input */ - public static Float[] toObject(final float[] array) { + public static Float[] toObject(float[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2208,13 +3112,13 @@ /** *

Converts an array of object Booleans to primitives.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a Boolean array, may be null * @return a boolean array, null if null array input * @throws NullPointerException if array content is null */ - public static boolean[] toPrimitive(final Boolean[] array) { + public static boolean[] toPrimitive(Boolean[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2229,14 +3133,14 @@ /** *

Converts an array of object Booleans to primitives handling null.

- * - *

This method returns null if null array input.

- * + * + *

This method returns null for a null input array.

+ * * @param array a Boolean array, may be null * @param valueForNull the value to insert if null found * @return a boolean array, null if null array input */ - public static boolean[] toPrimitive(final Boolean[] array, final boolean valueForNull) { + public static boolean[] toPrimitive(Boolean[] array, boolean valueForNull) { if (array == null) { return null; } else if (array.length == 0) { @@ -2253,12 +3157,12 @@ /** *

Converts an array of primitive booleans to objects.

* - *

This method returns null if null array input.

- * + *

This method returns null for a null input array.

+ * * @param array a boolean array * @return a Boolean array, null if null array input */ - public static Boolean[] toObject(final boolean[] array) { + public static Boolean[] toObject(boolean[] array) { if (array == null) { return null; } else if (array.length == 0) { @@ -2271,4 +3175,1705 @@ return result; } + // ---------------------------------------------------------------------- + /** + *

Checks if an array of Objects is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(Object[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive longs is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(long[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive ints is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(int[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive shorts is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(short[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive chars is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(char[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive bytes is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(byte[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive doubles is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(double[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive floats is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(float[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive booleans is empty or null.

+ * + * @param array the array to test + * @return true if the array is empty or null + * @since 2.1 + */ + public static boolean isEmpty(boolean[] array) { + return array == null || array.length == 0; + } + + // ---------------------------------------------------------------------- + /** + *

Checks if an array of Objects is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(Object[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive longs is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(long[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive ints is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(int[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive shorts is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(short[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive chars is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(char[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive bytes is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(byte[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive doubles is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(double[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive floats is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(float[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive booleans is not empty or not null.

+ * + * @param array the array to test + * @return true if the array is not empty or not null + * @since 2.5 + */ + public static boolean isNotEmpty(boolean[] array) { + return (array != null && array.length != 0); + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(null, null)     = null
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll([null], [null]) = [null, null]
+     * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
+     * 
+ * + * @param array1 the first array whose elements are added to the new array, may be null + * @param array2 the second array whose elements are added to the new array, may be null + * @return The new array, null if both arrays are null. + * The type of the new array is the type of the first array, + * unless the first array is null, in which case the type is the same as the second array. + * @since 2.1 + * @throws IllegalArgumentException if the array types are incompatible + */ + public static Object[] addAll(Object[] array1, Object[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + Object[] joinedArray = (Object[]) Array.newInstance(array1.getClass().getComponentType(), + array1.length + array2.length); + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + try { + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + } catch (ArrayStoreException ase) { + // Check if problem was due to incompatible types + /* + * We do this here, rather than before the copy because: + * - it would be a wasted check most of the time + * - safer, in case check turns out to be too strict + */ + final Class type1 = array1.getClass().getComponentType(); + final Class type2 = array2.getClass().getComponentType(); + if (!type1.isAssignableFrom(type2)){ + throw new IllegalArgumentException("Cannot store "+type2.getName()+" in an array of "+type1.getName()); + } + throw ase; // No, so rethrow original + } + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new boolean[] array. + * @since 2.1 + */ + public static boolean[] addAll(boolean[] array1, boolean[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + boolean[] joinedArray = new boolean[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new char[] array. + * @since 2.1 + */ + public static char[] addAll(char[] array1, char[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + char[] joinedArray = new char[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new byte[] array. + * @since 2.1 + */ + public static byte[] addAll(byte[] array1, byte[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + byte[] joinedArray = new byte[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new short[] array. + * @since 2.1 + */ + public static short[] addAll(short[] array1, short[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + short[] joinedArray = new short[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new int[] array. + * @since 2.1 + */ + public static int[] addAll(int[] array1, int[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + int[] joinedArray = new int[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new long[] array. + * @since 2.1 + */ + public static long[] addAll(long[] array1, long[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + long[] joinedArray = new long[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new float[] array. + * @since 2.1 + */ + public static float[] addAll(float[] array1, float[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + float[] joinedArray = new float[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of array1 followed + * by all of the elements array2. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new double[] array. + * @since 2.1 + */ + public static double[] addAll(double[] array1, double[] array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + double[] joinedArray = new double[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element, unless the element itself is null, + * in which case the return type is Object[]

+ * + *
+     * ArrayUtils.add(null, null)      = [null]
+     * ArrayUtils.add(null, "a")       = ["a"]
+     * ArrayUtils.add(["a"], null)     = ["a", null]
+     * ArrayUtils.add(["a"], "b")      = ["a", "b"]
+     * ArrayUtils.add(["a", "b"], "c") = ["a", "b", "c"]
+     * 
+ * + * @param array the array to "add" the element to, may be null + * @param element the object to add, may be null + * @return A new array containing the existing elements plus the new element + * The returned array type will be that of the input array (unless null), + * in which case it will have the same type as the element. + * @since 2.1 + */ + public static Object[] add(Object[] array, Object element) { + Class type; + if (array != null){ + type = array.getClass(); + } else if (element != null) { + type = element.getClass(); + } else { + type = Object.class; + } + Object[] newArray = (Object[]) copyArrayGrow1(array, type); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, true)          = [true]
+     * ArrayUtils.add([true], false)       = [true, false]
+     * ArrayUtils.add([true, false], true) = [true, false, true]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static boolean[] add(boolean[] array, boolean element) { + boolean[] newArray = (boolean[])copyArrayGrow1(array, Boolean.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static byte[] add(byte[] array, byte element) { + byte[] newArray = (byte[])copyArrayGrow1(array, Byte.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, '0')       = ['0']
+     * ArrayUtils.add(['1'], '0')      = ['1', '0']
+     * ArrayUtils.add(['1', '0'], '1') = ['1', '0', '1']
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static char[] add(char[] array, char element) { + char[] newArray = (char[])copyArrayGrow1(array, Character.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static double[] add(double[] array, double element) { + double[] newArray = (double[])copyArrayGrow1(array, Double.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static float[] add(float[] array, float element) { + float[] newArray = (float[])copyArrayGrow1(array, Float.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static int[] add(int[] array, int element) { + int[] newArray = (int[])copyArrayGrow1(array, Integer.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static long[] add(long[] array, long element) { + long[] newArray = (long[])copyArrayGrow1(array, Long.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be null + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static short[] add(short[] array, short element) { + short[] newArray = (short[])copyArrayGrow1(array, Short.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * Returns a copy of the given array of size 1 greater than the argument. + * The last value of the array is left to the default value. + * + * @param array The array to copy, must not be null. + * @param newArrayComponentType If array is null, create a + * size 1 array of this type. + * @return A new copy of the array of size 1 greater than the input. + */ + private static Object copyArrayGrow1(Object array, Class newArrayComponentType) { + if (array != null) { + int arrayLength = Array.getLength(array); + Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1); + System.arraycopy(array, 0, newArray, 0, arrayLength); + return newArray; + } + return Array.newInstance(newArrayComponentType, 1); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, null)      = [null]
+     * ArrayUtils.add(null, 0, "a")       = ["a"]
+     * ArrayUtils.add(["a"], 1, null)     = ["a", null]
+     * ArrayUtils.add(["a"], 1, "b")      = ["a", "b"]
+     * ArrayUtils.add(["a", "b"], 3, "c") = ["a", "b", "c"]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static Object[] add(Object[] array, int index, Object element) { + Class clss = null; + if (array != null) { + clss = array.getClass().getComponentType(); + } else if (element != null) { + clss = element.getClass(); + } else { + return new Object[]{null}; + } + return (Object[]) add(array, index, element, clss); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, true)          = [true]
+     * ArrayUtils.add([true], 0, false)       = [false, true]
+     * ArrayUtils.add([false], 1, true)       = [false, true]
+     * ArrayUtils.add([true, false], 1, true) = [true, true, false]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static boolean[] add(boolean[] array, int index, boolean element) { + return (boolean[]) add(array, index, BooleanUtils.toBooleanObject(element), Boolean.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, 'a')            = ['a']
+     * ArrayUtils.add(['a'], 0, 'b')           = ['b', 'a']
+     * ArrayUtils.add(['a', 'b'], 0, 'c')      = ['c', 'a', 'b']
+     * ArrayUtils.add(['a', 'b'], 1, 'k')      = ['a', 'k', 'b']
+     * ArrayUtils.add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c']
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static char[] add(char[] array, int index, char element) { + return (char[]) add(array, index, new Character(element), Character.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 3)      = [2, 6, 3]
+     * ArrayUtils.add([2, 6], 0, 1)      = [1, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static byte[] add(byte[] array, int index, byte element) { + return (byte[]) add(array, index, new Byte(element), Byte.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
+     * ArrayUtils.add([2, 6], 0, -4)     = [-4, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static short[] add(short[] array, int index, short element) { + return (short[]) add(array, index, new Short(element), Short.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
+     * ArrayUtils.add([2, 6], 0, -4)     = [-4, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static int[] add(int[] array, int index, int element) { + return (int[]) add(array, index, new Integer(element), Integer.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1L], 0, 2L)           = [2L, 1L]
+     * ArrayUtils.add([2L, 6L], 2, 10L)      = [2L, 6L, 10L]
+     * ArrayUtils.add([2L, 6L], 0, -4L)      = [-4L, 2L, 6L]
+     * ArrayUtils.add([2L, 6L, 3L], 2, 1L)   = [2L, 6L, 1L, 3L]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static long[] add(long[] array, int index, long element) { + return (long[]) add(array, index, new Long(element), Long.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1.1f], 0, 2.2f)               = [2.2f, 1.1f]
+     * ArrayUtils.add([2.3f, 6.4f], 2, 10.5f)        = [2.3f, 6.4f, 10.5f]
+     * ArrayUtils.add([2.6f, 6.7f], 0, -4.8f)        = [-4.8f, 2.6f, 6.7f]
+     * ArrayUtils.add([2.9f, 6.0f, 0.3f], 2, 1.0f)   = [2.9f, 6.0f, 1.0f, 0.3f]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static float[] add(float[] array, int index, float element) { + return (float[]) add(array, index, new Float(element), Float.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1.1], 0, 2.2)              = [2.2, 1.1]
+     * ArrayUtils.add([2.3, 6.4], 2, 10.5)        = [2.3, 6.4, 10.5]
+     * ArrayUtils.add([2.6, 6.7], 0, -4.8)        = [-4.8, 2.6, 6.7]
+     * ArrayUtils.add([2.9, 6.0, 0.3], 2, 1.0)    = [2.9, 6.0, 1.0, 0.3]
+     * 
+ * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static double[] add(double[] array, int index, double element) { + return (double[]) add(array, index, new Double(element), Double.TYPE); + } + + /** + * Underlying implementation of add(array, index, element) methods. + * The last parameter is the class, which may not equal element.getClass + * for primitives. + * + * @param array the array to add the element to, may be null + * @param index the position of the new object + * @param element the object to add + * @param clss the type of the element being added + * @return A new array containing the existing elements and the new element + */ + private static Object add(Object array, int index, Object element, Class clss) { + if (array == null) { + if (index != 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: 0"); + } + Object joinedArray = Array.newInstance(clss, 1); + Array.set(joinedArray, 0, element); + return joinedArray; + } + int length = Array.getLength(array); + if (index > length || index < 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + Object result = Array.newInstance(clss, length + 1); + System.arraycopy(array, 0, result, 0, index); + Array.set(result, index, element); + if (index < length) { + System.arraycopy(array, index, result, index + 1, length - index); + } + return result; + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove(["a"], 0)           = []
+     * ArrayUtils.remove(["a", "b"], 0)      = ["b"]
+     * ArrayUtils.remove(["a", "b"], 1)      = ["a"]
+     * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static Object[] remove(Object[] array, int index) { + return (Object[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, "a")            = null
+     * ArrayUtils.removeElement([], "a")              = []
+     * ArrayUtils.removeElement(["a"], "b")           = ["a"]
+     * ArrayUtils.removeElement(["a", "b"], "a")      = ["b"]
+     * ArrayUtils.removeElement(["a", "b", "a"], "a") = ["b", "a"]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static Object[] removeElement(Object[] array, Object element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([true], 0)              = []
+     * ArrayUtils.remove([true, false], 0)       = [false]
+     * ArrayUtils.remove([true, false], 1)       = [true]
+     * ArrayUtils.remove([true, true, false], 1) = [true, false]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static boolean[] remove(boolean[] array, int index) { + return (boolean[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, true)                = null
+     * ArrayUtils.removeElement([], true)                  = []
+     * ArrayUtils.removeElement([true], false)             = [true]
+     * ArrayUtils.removeElement([true, false], false)      = [true]
+     * ArrayUtils.removeElement([true, false, true], true) = [false, true]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static boolean[] removeElement(boolean[] array, boolean element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)          = []
+     * ArrayUtils.remove([1, 0], 0)       = [0]
+     * ArrayUtils.remove([1, 0], 1)       = [1]
+     * ArrayUtils.remove([1, 0, 1], 1)    = [1, 1]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static byte[] remove(byte[] array, int index) { + return (byte[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)        = null
+     * ArrayUtils.removeElement([], 1)          = []
+     * ArrayUtils.removeElement([1], 0)         = [1]
+     * ArrayUtils.removeElement([1, 0], 0)      = [1]
+     * ArrayUtils.removeElement([1, 0, 1], 1)   = [0, 1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static byte[] removeElement(byte[] array, byte element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove(['a'], 0)           = []
+     * ArrayUtils.remove(['a', 'b'], 0)      = ['b']
+     * ArrayUtils.remove(['a', 'b'], 1)      = ['a']
+     * ArrayUtils.remove(['a', 'b', 'c'], 1) = ['a', 'c']
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static char[] remove(char[] array, int index) { + return (char[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 'a')            = null
+     * ArrayUtils.removeElement([], 'a')              = []
+     * ArrayUtils.removeElement(['a'], 'b')           = ['a']
+     * ArrayUtils.removeElement(['a', 'b'], 'a')      = ['b']
+     * ArrayUtils.removeElement(['a', 'b', 'a'], 'a') = ['b', 'a']
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static char[] removeElement(char[] array, char element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1.1], 0)           = []
+     * ArrayUtils.remove([2.5, 6.0], 0)      = [6.0]
+     * ArrayUtils.remove([2.5, 6.0], 1)      = [2.5]
+     * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static double[] remove(double[] array, int index) { + return (double[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1.1)            = null
+     * ArrayUtils.removeElement([], 1.1)              = []
+     * ArrayUtils.removeElement([1.1], 1.2)           = [1.1]
+     * ArrayUtils.removeElement([1.1, 2.3], 1.1)      = [2.3]
+     * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static double[] removeElement(double[] array, double element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1.1], 0)           = []
+     * ArrayUtils.remove([2.5, 6.0], 0)      = [6.0]
+     * ArrayUtils.remove([2.5, 6.0], 1)      = [2.5]
+     * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static float[] remove(float[] array, int index) { + return (float[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1.1)            = null
+     * ArrayUtils.removeElement([], 1.1)              = []
+     * ArrayUtils.removeElement([1.1], 1.2)           = [1.1]
+     * ArrayUtils.removeElement([1.1, 2.3], 1.1)      = [2.3]
+     * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static float[] removeElement(float[] array, float element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static int[] remove(int[] array, int index) { + return (int[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static int[] removeElement(int[] array, int element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static long[] remove(long[] array, int index) { + return (long[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static long[] removeElement(long[] array, long element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + public static short[] remove(short[] array, int index) { + return (short[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (substracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be null + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static short[] removeElement(short[] array, short element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (substracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is null, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + * @param array the array to remove the element from, may not be null + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is null. + * @since 2.1 + */ + private static Object remove(Object array, int index) { + int length = getLength(array); + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + + Object result = Array.newInstance(array.getClass().getComponentType(), length - 1); + System.arraycopy(array, 0, result, 0, index); + if (index < length - 1) { + System.arraycopy(array, index + 1, result, index, length - index - 1); + } + + return result; + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/BitField.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/BitField.java (.../BitField.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/BitField.java (.../BitField.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,66 +1,29 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; /** *

Operations on bit-mapped fields.

* + * @author Apache Software Foundation * @author Apache Jakarta POI * @author Scott Sanders (sanders at apache dot org) * @author Marc Johnson (mjohnson at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org) - * @author Stephen Colebourne * @author Pete Gieser * @author Gary Gregory * @since 2.0 @@ -78,7 +41,7 @@ * BitField. Bits that are set in this mask are the bits * that this BitField operates on */ - public BitField(final int mask) { + public BitField(int mask) { _mask = mask; int count = 0; int bit_pattern = mask; @@ -101,12 +64,12 @@ * value is stored as a BitField (and so shifted left so many * bits).

* - * @see #setValue + * @see #setValue(int,int) * @param holder the int data containing the bits we're interested * in * @return the selected bits, shifted right appropriately */ - public int getValue(final int holder) { + public int getValue(int holder) { return getRawValue(holder) >> _shift_count; } @@ -119,12 +82,12 @@ * value is stored as a BitField (and so shifted left so many * bits).

* - * @see #setShortValue + * @see #setShortValue(short,short) * @param holder the short data containing the bits we're * interested in * @return the selected bits, shifted right appropriately */ - public short getShortValue(final short holder) { + public short getShortValue(short holder) { return (short) getValue(holder); } @@ -135,8 +98,8 @@ * interested in * @return the selected bits */ - public int getRawValue(final int holder) { - return (holder & _mask); + public int getRawValue(int holder) { + return holder & _mask; } /** @@ -146,7 +109,7 @@ * interested in * @return the selected bits */ - public short getShortRawValue(final short holder) { + public short getShortRawValue(short holder) { return (short) getRawValue(holder); } @@ -163,7 +126,7 @@ * @return true if any of the bits are set, * else false */ - public boolean isSet(final int holder) { + public boolean isSet(int holder) { return (holder & _mask) != 0; } @@ -179,35 +142,35 @@ * @return true if all of the bits are set, * else false */ - public boolean isAllSet(final int holder) { + public boolean isAllSet(int holder) { return (holder & _mask) == _mask; } /** *

Replaces the bits with new values.

* - * @see #getValue - * @param holder the int data containint the bits we're + * @see #getValue(int) + * @param holder the int data containing the bits we're * interested in * @param value the new value for the specified bits * @return the value of holder with the bits from the value * parameter replacing the old bits */ - public int setValue(final int holder, final int value) { + public int setValue(int holder, int value) { return (holder & ~_mask) | ((value << _shift_count) & _mask); } /** *

Replaces the bits with new values.

* - * @see #getShortValue + * @see #getShortValue(short) * @param holder the short data containing the bits we're * interested in * @param value the new value for the specified bits * @return the value of holder with the bits from the value * parameter replacing the old bits */ - public short setShortValue(final short holder, final short value) { + public short setShortValue(short holder, short value) { return (short) setValue(holder, value); } @@ -219,7 +182,7 @@ * @return the value of holder with the specified bits cleared * (set to 0) */ - public int clear(final int holder) { + public int clear(int holder) { return holder & ~_mask; } @@ -231,7 +194,7 @@ * @return the value of holder with the specified bits cleared * (set to 0) */ - public short clearShort(final short holder) { + public short clearShort(short holder) { return (short) clear(holder); } @@ -244,7 +207,7 @@ * @return the value of holder with the specified bits cleared * (set to 0) */ - public byte clearByte(final byte holder) { + public byte clearByte(byte holder) { return (byte) clear(holder); } @@ -256,7 +219,7 @@ * @return the value of holder with the specified bits set * to 1 */ - public int set(final int holder) { + public int set(int holder) { return holder | _mask; } @@ -268,7 +231,7 @@ * @return the value of holder with the specified bits set * to 1 */ - public short setShort(final short holder) { + public short setShort(short holder) { return (short) set(holder); } @@ -281,7 +244,7 @@ * @return the value of holder with the specified bits set * to 1 */ - public byte setByte(final byte holder) { + public byte setByte(byte holder) { return (byte) set(holder); } @@ -294,7 +257,7 @@ * @return the value of holder with the specified bits set or * cleared */ - public int setBoolean(final int holder, final boolean flag) { + public int setBoolean(int holder, boolean flag) { return flag ? set(holder) : clear(holder); } @@ -307,7 +270,7 @@ * @return the value of holder with the specified bits set or * cleared */ - public short setShortBoolean(final short holder, final boolean flag) { + public short setShortBoolean(short holder, boolean flag) { return flag ? setShort(holder) : clearShort(holder); } @@ -320,7 +283,7 @@ * @return the value of holder with the specified bits set or * cleared */ - public byte setByteBoolean(final byte holder, final boolean flag) { + public byte setByteBoolean(byte holder, boolean flag) { return flag ? setByte(holder) : clearByte(holder); } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/BooleanUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/BooleanUtils.java (.../BooleanUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/BooleanUtils.java (.../BooleanUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -62,7 +25,8 @@ * An exception will not be thrown for a null input. * Each method documents its behaviour in more detail.

* - * @author Stephen Colebourne + *

#ThreadSafe#

+ * @author Apache Software Foundation * @author Matthew Hawthorne * @author Gary Gregory * @since 2.0 @@ -78,6 +42,7 @@ * to operate.

*/ public BooleanUtils() { + super(); } // Boolean utilities @@ -86,6 +51,12 @@ *

Negates the specified boolean.

* *

If null is passed in, null will be returned.

+ * + *
+     *   BooleanUtils.negate(Boolean.TRUE)  = Boolean.FALSE;
+     *   BooleanUtils.negate(Boolean.FALSE) = Boolean.TRUE;
+     *   BooleanUtils.negate(null)          = null;
+     * 
* * @param bool the Boolean to negate, may be null * @return the negated Boolean, or null if null input @@ -100,21 +71,111 @@ // boolean Boolean methods //----------------------------------------------------------------------- /** + *

Checks if a Boolean value is true, + * handling null by returning false.

+ * + *
+     *   BooleanUtils.isTrue(Boolean.TRUE)  = true
+     *   BooleanUtils.isTrue(Boolean.FALSE) = false
+     *   BooleanUtils.isTrue(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns false + * @return true only if the input is non-null and true + * @since 2.1 + */ + public static boolean isTrue(Boolean bool) { + if (bool == null) { + return false; + } + return bool.booleanValue() ? true : false; + } + + /** + *

Checks if a Boolean value is not true, + * handling null by returning true.

+ * + *
+     *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
+     *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
+     *   BooleanUtils.isNotTrue(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns true + * @return true if the input is null or false + * @since 2.3 + */ + public static boolean isNotTrue(Boolean bool) { + return !isTrue(bool); + } + + /** + *

Checks if a Boolean value is false, + * handling null by returning false.

+ * + *
+     *   BooleanUtils.isFalse(Boolean.TRUE)  = false
+     *   BooleanUtils.isFalse(Boolean.FALSE) = true
+     *   BooleanUtils.isFalse(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns false + * @return true only if the input is non-null and false + * @since 2.1 + */ + public static boolean isFalse(Boolean bool) { + if (bool == null) { + return false; + } + return bool.booleanValue() ? false : true; + } + + /** + *

Checks if a Boolean value is not false, + * handling null by returning true.

+ * + *
+     *   BooleanUtils.isNotFalse(Boolean.TRUE)  = true
+     *   BooleanUtils.isNotFalse(Boolean.FALSE) = false
+     *   BooleanUtils.isNotFalse(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns true + * @return true if the input is null or true + * @since 2.3 + */ + public static boolean isNotFalse(Boolean bool) { + return !isFalse(bool); + } + + //----------------------------------------------------------------------- + /** *

Boolean factory that avoids creating new Boolean objecs all the time.

* *

This method was added to JDK1.4 but is available here for earlier JDKs.

- * + * + *
+     *   BooleanUtils.toBooleanObject(false) = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject(true)  = Boolean.TRUE
+     * 
+ * * @param bool the boolean to convert * @return Boolean.TRUE or Boolean.FALSE as appropriate */ public static Boolean toBooleanObject(boolean bool) { - return (bool ? Boolean.TRUE : Boolean.FALSE); + return bool ? Boolean.TRUE : Boolean.FALSE; } /** *

Converts a Boolean to a boolean handling null * by returning false.

- * + * + *
+     *   BooleanUtils.toBoolean(Boolean.TRUE)  = true
+     *   BooleanUtils.toBoolean(Boolean.FALSE) = false
+     *   BooleanUtils.toBoolean(null)          = false
+     * 
+ * * @param bool the boolean to convert * @return true or false, * null returns false @@ -123,12 +184,18 @@ if (bool == null) { return false; } - return (bool.booleanValue() ? true : false); + return bool.booleanValue() ? true : false; } /** *

Converts a Boolean to a boolean handling null.

* + *
+     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false) = true
+     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true) = false
+     *   BooleanUtils.toBooleanDefaultIfNull(null, true)          = true
+     * 
+ * * @param bool the boolean to convert * @param valueIfNull the boolean value to return if null * @return true or false @@ -137,7 +204,7 @@ if (bool == null) { return valueIfNull; } - return (bool.booleanValue() ? true : false); + return bool.booleanValue() ? true : false; } // Integer to Boolean methods @@ -146,32 +213,50 @@ *

Converts an int to a boolean using the convention that zero * is false.

* + *
+     *   BooleanUtils.toBoolean(0) = false
+     *   BooleanUtils.toBoolean(1) = true
+     *   BooleanUtils.toBoolean(2) = true
+     * 
+ * * @param value the int to convert * @return true if non-zero, false * if zero */ public static boolean toBoolean(int value) { - return (value == 0 ? false : true); + return value == 0 ? false : true; } /** *

Converts an int to a Boolean using the convention that zero * is false.

* + *
+     *   BooleanUtils.toBoolean(0) = Boolean.FALSE
+     *   BooleanUtils.toBoolean(1) = Boolean.TRUE
+     *   BooleanUtils.toBoolean(2) = Boolean.TRUE
+     * 
+ * * @param value the int to convert * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, * null if null */ public static Boolean toBooleanObject(int value) { - return (value == 0 ? Boolean.FALSE : Boolean.TRUE); + return value == 0 ? Boolean.FALSE : Boolean.TRUE; } /** *

Converts an Integer to a Boolean using the convention that zero * is false.

* *

null will be converted to null.

- * + * + *
+     *   BooleanUtils.toBoolean(new Integer(0))    = Boolean.FALSE
+     *   BooleanUtils.toBoolean(new Integer(1))    = Boolean.TRUE
+     *   BooleanUtils.toBoolean(new Integer(null)) = null
+     * 
+ * * @param value the Integer to convert * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, * null if null input @@ -180,12 +265,19 @@ if (value == null) { return null; } - return (value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE); + return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE; } /** *

Converts an int to a boolean specifying the conversion values.

* + *
+     *   BooleanUtils.toBoolean(0, 1, 0) = false
+     *   BooleanUtils.toBoolean(1, 1, 0) = true
+     *   BooleanUtils.toBoolean(2, 1, 2) = false
+     *   BooleanUtils.toBoolean(2, 2, 0) = true
+     * 
+ * * @param value the Integer to convert * @param trueValue the value to match for true * @param falseValue the value to match for false @@ -205,6 +297,14 @@ /** *

Converts an Integer to a boolean specifying the conversion values.

* + *
+     *   BooleanUtils.toBoolean(new Integer(0), new Integer(1), new Integer(0)) = false
+     *   BooleanUtils.toBoolean(new Integer(1), new Integer(1), new Integer(0)) = true
+     *   BooleanUtils.toBoolean(new Integer(2), new Integer(1), new Integer(2)) = false
+     *   BooleanUtils.toBoolean(new Integer(2), new Integer(2), new Integer(0)) = true
+     *   BooleanUtils.toBoolean(null, null, new Integer(0))                     = true
+     * 
+ * * @param value the Integer to convert * @param trueValue the value to match for true, * may be null @@ -232,6 +332,12 @@ /** *

Converts an int to a Boolean specifying the conversion values.

* + *
+     *   BooleanUtils.toBooleanObject(0, 0, 2, 3) = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject(2, 1, 2, 3) = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject(3, 1, 2, 3) = null
+     * 
+ * * @param value the Integer to convert * @param trueValue the value to match for true * @param falseValue the value to match for false @@ -254,6 +360,12 @@ /** *

Converts an Integer to a Boolean specifying the conversion values.

* + *
+     *   BooleanUtils.toBooleanObject(new Integer(0), new Integer(0), new Integer(2), new Integer(3)) = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject(new Integer(2), new Integer(1), new Integer(2), new Integer(3)) = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject(new Integer(3), new Integer(1), new Integer(2), new Integer(3)) = null
+     * 
+ * * @param value the Integer to convert * @param trueValue the value to match for true, * may be null @@ -289,56 +401,82 @@ /** *

Converts a boolean to an int using the convention that * zero is false.

- * + * + *
+     *   BooleanUtils.toInteger(true)  = 1
+     *   BooleanUtils.toInteger(false) = 0
+     * 
+ * * @param bool the boolean to convert * @return one if true, zero if false */ public static int toInteger(boolean bool) { - return (bool ? 1 : 0); + return bool ? 1 : 0; } /** *

Converts a boolean to an Integer using the convention that * zero is false.

* + *
+     *   BooleanUtils.toIntegerObject(true)  = new Integer(1)
+     *   BooleanUtils.toIntegerObject(false) = new Integer(0)
+     * 
+ * * @param bool the boolean to convert * @return one if true, zero if false */ public static Integer toIntegerObject(boolean bool) { - return (bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO); + return bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; } /** *

Converts a Boolean to a Integer using the convention that * zero is false.

- * + * *

null will be converted to null.

- * + * + *
+     *   BooleanUtils.toIntegerObject(Boolean.TRUE)  = new Integer(1)
+     *   BooleanUtils.toIntegerObject(Boolean.FALSE) = new Integer(0)
+     * 
+ * * @param bool the Boolean to convert * @return one if Boolean.TRUE, zero if Boolean.FALSE, null if null */ public static Integer toIntegerObject(Boolean bool) { if (bool == null) { return null; } - return (bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO); + return bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; } /** *

Converts a boolean to an int specifying the conversion values.

* + *
+     *   BooleanUtils.toInteger(true, 1, 0)  = 1
+     *   BooleanUtils.toInteger(false, 1, 0) = 0
+     * 
+ * * @param bool the to convert * @param trueValue the value to return if true * @param falseValue the value to return if false * @return the appropriate value */ public static int toInteger(boolean bool, int trueValue, int falseValue) { - return (bool ? trueValue : falseValue); + return bool ? trueValue : falseValue; } /** *

Converts a Boolean to an int specifying the conversion values.

* + *
+     *   BooleanUtils.toInteger(Boolean.TRUE, 1, 0, 2)  = 1
+     *   BooleanUtils.toInteger(Boolean.FALSE, 1, 0, 2) = 0
+     *   BooleanUtils.toInteger(null, 1, 0, 2)          = 2
+     * 
+ * * @param bool the Boolean to convert * @param trueValue the value to return if true * @param falseValue the value to return if false @@ -349,12 +487,17 @@ if (bool == null) { return nullValue; } - return (bool.booleanValue() ? trueValue : falseValue); + return bool.booleanValue() ? trueValue : falseValue; } /** *

Converts a boolean to an Integer specifying the conversion values.

* + *
+     *   BooleanUtils.toIntegerObject(true, new Integer(1), new Integer(0))  = new Integer(1)
+     *   BooleanUtils.toIntegerObject(false, new Integer(1), new Integer(0)) = new Integer(0)
+     * 
+ * * @param bool the to convert * @param trueValue the value to return if true, * may be null @@ -363,12 +506,18 @@ * @return the appropriate value */ public static Integer toIntegerObject(boolean bool, Integer trueValue, Integer falseValue) { - return (bool ? trueValue : falseValue); + return bool ? trueValue : falseValue; } /** *

Converts a Boolean to an Integer specifying the conversion values.

* + *
+     *   BooleanUtils.toIntegerObject(Boolean.TRUE, new Integer(1), new Integer(0), new Integer(2))  = new Integer(1)
+     *   BooleanUtils.toIntegerObject(Boolean.FALSE, new Integer(1), new Integer(0), new Integer(2)) = new Integer(0)
+     *   BooleanUtils.toIntegerObject(null, new Integer(1), new Integer(0), new Integer(2))          = new Integer(2)
+     * 
+ * * @param bool the Boolean to convert * @param trueValue the value to return if true, * may be null @@ -382,7 +531,7 @@ if (bool == null) { return nullValue; } - return (bool.booleanValue() ? trueValue : falseValue); + return bool.booleanValue() ? trueValue : falseValue; } // String to Boolean methods @@ -396,31 +545,126 @@ * (case insensitive) will return false. * Otherwise, null is returned.

* + *
+     *   BooleanUtils.toBooleanObject(null)    = null
+     *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("false") = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("on")    = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("ON")    = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("off")   = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("blue")  = null
+     * 
+ * * @param str the String to check * @return the Boolean value of the string, * null if no match or null input */ public static Boolean toBooleanObject(String str) { - if ("true".equalsIgnoreCase(str)) { + // Previously used equalsIgnoreCase, which was fast for interned 'true'. + // Non interned 'true' matched 15 times slower. + // + // Optimisation provides same performance as before for interned 'true'. + // Similar performance for null, 'false', and other strings not length 2/3/4. + // 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower. + if (str == "true") { return Boolean.TRUE; - } else if ("false".equalsIgnoreCase(str)) { - return Boolean.FALSE; - } else if ("on".equalsIgnoreCase(str)) { - return Boolean.TRUE; - } else if ("off".equalsIgnoreCase(str)) { - return Boolean.FALSE; - } else if ("yes".equalsIgnoreCase(str)) { - return Boolean.TRUE; - } else if ("no".equalsIgnoreCase(str)) { - return Boolean.FALSE; } - // no match + if (str == null) { + return null; + } + switch (str.length()) { + case 1: { + char ch0 = str.charAt(0); + if ((ch0 == 'y' || ch0 == 'Y') || + (ch0 == 't' || ch0 == 'T')) + { + return Boolean.TRUE; + } + if ((ch0 == 'n' || ch0 == 'N') || + (ch0 == 'f' || ch0 == 'F')) + { + return Boolean.FALSE; + } + break; + } + case 2: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'n' || ch1 == 'N') ) + { + return Boolean.TRUE; + } + if ((ch0 == 'n' || ch0 == 'N') && + (ch1 == 'o' || ch1 == 'O') ) + { + return Boolean.FALSE; + } + break; + } + case 3: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + if ((ch0 == 'y' || ch0 == 'Y') && + (ch1 == 'e' || ch1 == 'E') && + (ch2 == 's' || ch2 == 'S') ) + { + return Boolean.TRUE; + } + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'f' || ch1 == 'F') && + (ch2 == 'f' || ch2 == 'F') ) + { + return Boolean.FALSE; + } + break; + } + case 4: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + char ch3 = str.charAt(3); + if ((ch0 == 't' || ch0 == 'T') && + (ch1 == 'r' || ch1 == 'R') && + (ch2 == 'u' || ch2 == 'U') && + (ch3 == 'e' || ch3 == 'E') ) + { + return Boolean.TRUE; + } + break; + } + case 5: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + char ch3 = str.charAt(3); + char ch4 = str.charAt(4); + if ((ch0 == 'f' || ch0 == 'F') && + (ch1 == 'a' || ch1 == 'A') && + (ch2 == 'l' || ch2 == 'L') && + (ch3 == 's' || ch3 == 'S') && + (ch4 == 'e' || ch4 == 'E') ) + { + return Boolean.FALSE; + } + break; + } + } + return null; } /** *

Converts a String to a Boolean throwing an exception if no match.

* + *
+     *   BooleanUtils.toBooleanObject("true", "true", "false", "null")  = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("false", "true", "false", "null") = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("null", "true", "false", "null")  = null
+     * 
+ * * @param str the String to check * @param trueString the String to match for true * (case sensitive), may be null @@ -429,7 +673,10 @@ * @param nullString the String to match for null * (case sensitive), may be null * @return the Boolean value of the string, - * null if no match or null input + * null if either the String matches nullString + * or if null input and nullString is + * null + * @throws IllegalArgumentException if the String doesn't match */ public static Boolean toBooleanObject(String str, String trueString, String falseString, String nullString) { if (str == null) { @@ -454,32 +701,44 @@ // String to boolean methods //----------------------------------------------------------------------- /** - *

Converts a String to a boolean.

+ *

Converts a String to a boolean (optimised for performance).

* *

'true', 'on' or 'yes' * (case insensitive) will return true. Otherwise, * false is returned.

+ * + *

This method performs 4 times faster (JDK1.4) than + * Boolean.valueOf(String). However, this method accepts + * 'on' and 'yes' as true values. * + *

+     *   BooleanUtils.toBoolean(null)    = false
+     *   BooleanUtils.toBoolean("true")  = true
+     *   BooleanUtils.toBoolean("TRUE")  = true
+     *   BooleanUtils.toBoolean("tRUe")  = true
+     *   BooleanUtils.toBoolean("on")    = true
+     *   BooleanUtils.toBoolean("yes")   = true
+     *   BooleanUtils.toBoolean("false") = false
+     *   BooleanUtils.toBoolean("x gti") = false
+     * 
+ * * @param str the String to check - * @return the boolean value of the string, false if no match + * @return the boolean value of the string, false if no match or the String is null */ public static boolean toBoolean(String str) { - if ("true".equalsIgnoreCase(str)) { - return true; - } else if ("on".equalsIgnoreCase(str)) { - return true; - } else if ("yes".equalsIgnoreCase(str)) { - return true; - } - // no match - return false; + return toBoolean(toBooleanObject(str)); } - + /** *

Converts a String to a Boolean throwing an exception if no match found.

* *

null is returned if there is no match.

* + *
+     *   BooleanUtils.toBoolean("true", "true", "false")  = true
+     *   BooleanUtils.toBoolean("false", "true", "false") = false
+     * 
+ * * @param str the String to check * @param trueString the String to match for true * (case sensitive), may be null @@ -510,6 +769,12 @@ *

Converts a Boolean to a String returning 'true', * 'false', or null.

* + *
+     *   BooleanUtils.toStringTrueFalse(Boolean.TRUE)  = "true"
+     *   BooleanUtils.toStringTrueFalse(Boolean.FALSE) = "false"
+     *   BooleanUtils.toStringTrueFalse(null)          = null;
+     * 
+ * * @param bool the Boolean to check * @return 'true', 'false', * or null @@ -522,6 +787,12 @@ *

Converts a Boolean to a String returning 'on', * 'off', or null.

* + *
+     *   BooleanUtils.toStringOnOff(Boolean.TRUE)  = "on"
+     *   BooleanUtils.toStringOnOff(Boolean.FALSE) = "off"
+     *   BooleanUtils.toStringOnOff(null)          = null;
+     * 
+ * * @param bool the Boolean to check * @return 'on', 'off', * or null @@ -534,6 +805,12 @@ *

Converts a Boolean to a String returning 'yes', * 'no', or null.

* + *
+     *   BooleanUtils.toStringYesNo(Boolean.TRUE)  = "yes"
+     *   BooleanUtils.toStringYesNo(Boolean.FALSE) = "no"
+     *   BooleanUtils.toStringYesNo(null)          = null;
+     * 
+ * * @param bool the Boolean to check * @return 'yes', 'no', * or null @@ -545,6 +822,12 @@ /** *

Converts a Boolean to a String returning one of the input Strings.

* + *
+     *   BooleanUtils.toString(Boolean.TRUE, "true", "false", null)   = "true"
+     *   BooleanUtils.toString(Boolean.FALSE, "true", "false", null)  = "false"
+     *   BooleanUtils.toString(null, "true", "false", null)           = null;
+     * 
+ * * @param bool the Boolean to check * @param trueString the String to return if true, * may be null @@ -558,7 +841,7 @@ if (bool == null) { return nullString; } - return (bool.booleanValue() ? trueString : falseString); + return bool.booleanValue() ? trueString : falseString; } // boolean to String methods @@ -567,6 +850,11 @@ *

Converts a boolean to a String returning 'true' * or 'false'.

* + *
+     *   BooleanUtils.toStringTrueFalse(true)   = "true"
+     *   BooleanUtils.toStringTrueFalse(false)  = "false"
+     * 
+ * * @param bool the Boolean to check * @return 'true', 'false', * or null @@ -579,6 +867,11 @@ *

Converts a boolean to a String returning 'on' * or 'off'.

* + *
+     *   BooleanUtils.toStringOnOff(true)   = "on"
+     *   BooleanUtils.toStringOnOff(false)  = "off"
+     * 
+ * * @param bool the Boolean to check * @return 'on', 'off', * or null @@ -591,6 +884,11 @@ *

Converts a boolean to a String returning 'yes' * or 'no'.

* + *
+     *   BooleanUtils.toStringYesNo(true)   = "yes"
+     *   BooleanUtils.toStringYesNo(false)  = "no"
+     * 
+ * * @param bool the Boolean to check * @return 'yes', 'no', * or null @@ -602,6 +900,11 @@ /** *

Converts a boolean to a String returning one of the input Strings.

* + *
+     *   BooleanUtils.toString(true, "true", "false")   = "true"
+     *   BooleanUtils.toString(false, "true", "false")  = "false"
+     * 
+ * * @param bool the Boolean to check * @param trueString the String to return if true, * may be null @@ -610,14 +913,20 @@ * @return one of the two input Strings */ public static String toString(boolean bool, String trueString, String falseString) { - return (bool ? trueString : falseString); + return bool ? trueString : falseString; } // xor methods // ---------------------------------------------------------------------- /** *

Performs an xor on a set of booleans.

- * + * + *
+     *   BooleanUtils.xor(new boolean[] { true, true })   = false
+     *   BooleanUtils.xor(new boolean[] { false, false }) = false
+     *   BooleanUtils.xor(new boolean[] { true, false })  = true
+     * 
+ * * @param array an array of booleans * @return true if the xor is successful. * @throws IllegalArgumentException if array is null @@ -652,6 +961,12 @@ /** *

Performs an xor on an array of Booleans.

* + *
+     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.TRUE })   = Boolean.FALSE
+     *   BooleanUtils.xor(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) = Boolean.FALSE
+     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.FALSE })  = Boolean.TRUE
+     * 
+ * * @param array an array of Booleans * @return true if the xor is successful. * @throws IllegalArgumentException if array is null @@ -668,9 +983,9 @@ try { primitive = ArrayUtils.toPrimitive(array); } catch (NullPointerException ex) { - throw new IllegalArgumentException("The array must not conatin any null elements"); + throw new IllegalArgumentException("The array must not contain any null elements"); } - return (xor(primitive) ? Boolean.TRUE : Boolean.FALSE); + return xor(primitive) ? Boolean.TRUE : Boolean.FALSE; } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/CharEncoding.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/CharEncoding.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/CharEncoding.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang; + +import java.io.UnsupportedEncodingException; + +/** + *

+ * Character encoding names required of every implementation of the Java platform. + *

+ * + *

+ * According to JRE character + * encoding names: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. + *

+ *

+ * + * @see JRE character encoding + * names + * @author Apache Software Foundation + * @since 2.1 + * @version $Id$ + */ +public class CharEncoding { + + /** + *

+ * ISO Latin Alphabet #1, also known as ISO-LATIN-1. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + *

+ * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String US_ASCII = "US-ASCII"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either + * order accepted on input, big-endian used on output). + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String UTF_16 = "UTF-16"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + *

+ * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + *

+ * Eight-bit Unicode Transformation Format. + *

+ *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see JRE character + * encoding names + */ + public static final String UTF_8 = "UTF-8"; + + /** + *

+ * Returns whether the named charset is supported. + *

+ *

+ * This is similar to + * java.nio.charset.Charset.isSupported(String) + *

+ * + * @param name + * the name of the requested charset; may be either a canonical name or an alias + * @return true if, and only if, support for the named charset is available in the current Java + * virtual machine + * + * @see JRE character + * encoding names + */ + public static boolean isSupported(String name) { + if (name == null) { + return false; + } + try { + new String(ArrayUtils.EMPTY_BYTE_ARRAY, name); + } catch (UnsupportedEncodingException e) { + return false; + } + return true; + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/CharRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/CharRange.java (.../CharRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/CharRange.java (.../CharRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,75 +1,46 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.io.Serializable; +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.apache.commons.lang.text.StrBuilder; + /** *

A contiguous range of characters, optionally negated.

* *

Instances are immutable.

* - * @author Henri Yandell - * @author Stephen Colebourne + *

#ThreadSafe#

+ * @author Apache Software Foundation * @author Chris Feldhacker * @author Gary Gregory * @since 1.0 * @version $Id$ */ public final class CharRange implements Serializable { - /** Serialization lock, Lang version 2.0. */ + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 8270183163158333422L; /** The first character, inclusive, in the range. */ @@ -82,12 +53,64 @@ /** Cached toString. */ private transient String iToString; + // Static //----------------------------------------------------------------------- /** *

Constructs a CharRange over a single character.

* * @param ch only character in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 */ + public static CharRange is(char ch) { + return new CharRange(ch, ch, false); + } + + /** + *

Constructs a negated CharRange over a single character.

+ * + * @param ch only character in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isNot(char ch) { + return new CharRange(ch, ch, true); + } + + /** + *

Constructs a CharRange over a set of characters.

+ * + * @param start first character, inclusive, in this range + * @param end last character, inclusive, in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isIn(char start, char end) { + return new CharRange(start, end, false); + } + + /** + *

Constructs a negated CharRange over a set of characters.

+ * + * @param start first character, inclusive, in this range + * @param end last character, inclusive, in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isNotIn(char start, char end) { + return new CharRange(start, end, true); + } + + //----------------------------------------------------------------------- + /** + *

Constructs a CharRange over a single character.

+ * + * @param ch only character in this range + */ public CharRange(char ch) { this(ch, ch, false); } @@ -183,7 +206,7 @@ * @return true if this range contains the input character */ public boolean contains(char ch) { - return ((ch >= start && ch <= end) != negated); + return (ch >= start && ch <= end) != negated; } /** @@ -200,17 +223,14 @@ } if (negated) { if (range.negated) { - return (start >= range.start && end <= range.end); - } else { - return (range.end < start || range.start > end); + return start >= range.start && end <= range.end; } - } else { - if (range.negated) { - return (start == 0 && end == Character.MAX_VALUE); - } else { - return (start <= range.start && end >= range.end); - } + return range.end < start || range.start > end; } + if (range.negated) { + return start == 0 && end == Character.MAX_VALUE; + } + return start <= range.start && end >= range.end; } // Basics @@ -230,11 +250,11 @@ return false; } CharRange other = (CharRange) obj; - return (start == other.start && end == other.end && negated == other.negated); + return start == other.start && end == other.end && negated == other.negated; } /** - *

Gets a hashCode compatable with the equals method.

+ *

Gets a hashCode compatible with the equals method.

* * @return a suitable hashCode */ @@ -249,7 +269,7 @@ */ public String toString() { if (iToString == null) { - StringBuffer buf = new StringBuffer(4); + StrBuilder buf = new StrBuilder(4); if (isNegated()) { buf.append('^'); } @@ -262,5 +282,110 @@ } return iToString; } - + + // Expansions + //----------------------------------------------------------------------- + /** + *

Returns an iterator which can be used to walk through the characters described by this range.

+ * + *

#NotThreadSafe# the iterator is not threadsafe

+ * @return an iterator to the chars represented by this range + * @since 2.5 + */ + public Iterator iterator() { + return new CharacterIterator(this); + } + + /** + * Character {@link Iterator}. + *

#NotThreadSafe#

+ */ + private static class CharacterIterator implements Iterator { + /** The current character */ + private char current; + + private final CharRange range; + private boolean hasNext; + + /** + * Construct a new iterator for the character range. + * + * @param r The character range + */ + private CharacterIterator(CharRange r) { + range = r; + hasNext = true; + + if (range.negated) { + if (range.start == 0) { + if (range.end == Character.MAX_VALUE) { + // This range is an empty set + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = 0; + } + } else { + current = range.start; + } + } + + /** + * Prepare the next character in the range. + */ + private void prepareNext() { + if (range.negated) { + if (current == Character.MAX_VALUE) { + hasNext = false; + } else if (current + 1 == range.start) { + if (range.end == Character.MAX_VALUE) { + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = (char) (current + 1); + } + } else if (current < range.end) { + current = (char) (current + 1); + } else { + hasNext = false; + } + } + + /** + * Has the iterator not reached the end character yet? + * + * @return true if the iterator has yet to reach the character date + */ + public boolean hasNext() { + return hasNext; + } + + /** + * Return the next character in the iteration + * + * @return Character for the next character + */ + public Object next() { + if (hasNext == false) { + throw new NoSuchElementException(); + } + char cur = current; + prepareNext(); + return new Character(cur); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException(); + } + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSet.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSet.java (.../CharSet.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSet.java (.../CharSet.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,59 +1,23 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.io.Serializable; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -65,8 +29,8 @@ * *

Instances are immutable, but instances of subclasses may not be.

* - * @author Henri Yandell - * @author Stephen Colebourne + *

#ThreadSafe#

+ * @author Apache Software Foundation * @author Phil Steitz * @author Pete Gieser * @author Gary Gregory @@ -75,7 +39,11 @@ */ public class CharSet implements Serializable { - /** Serialization lock, Lang version 2.0. */ + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 5947847346149275958L; /** @@ -110,10 +78,10 @@ /** * A Map of the common cases used in the factory. - * Subclasses can add more common patterns if desired. + * Subclasses can add more common patterns if desired * @since 2.0 */ - protected static final Map COMMON = new HashMap(); + protected static final Map COMMON = Collections.synchronizedMap(new HashMap()); static { COMMON.put(null, EMPTY); @@ -126,7 +94,7 @@ } /** The set of CharRange objects. */ - private Set set = new HashSet(); + private final Set set = Collections.synchronizedSet(new HashSet()); //----------------------------------------------------------------------- /** @@ -180,6 +148,21 @@ return new CharSet(setStr); } + /** + *

Constructs a new CharSet using the set syntax. + * Each string is merged in with the set.

+ * + * @param setStrs Strings to merge into the initial set, may be null + * @return a CharSet instance + * @since 2.4 + */ + public static CharSet getInstance(String[] setStrs) { + if (setStrs == null) { + return null; + } + return new CharSet(setStrs); + } + //----------------------------------------------------------------------- /** *

Constructs a new CharSet using the set syntax.

@@ -224,19 +207,19 @@ int remainder = (len - pos); if (remainder >= 4 && str.charAt(pos) == '^' && str.charAt(pos + 2) == '-') { // negated range - set.add(new CharRange(str.charAt(pos + 1), str.charAt(pos + 3), true)); + set.add(CharRange.isNotIn(str.charAt(pos + 1), str.charAt(pos + 3))); pos += 4; } else if (remainder >= 3 && str.charAt(pos + 1) == '-') { // range - set.add(new CharRange(str.charAt(pos), str.charAt(pos + 2))); + set.add(CharRange.isIn(str.charAt(pos), str.charAt(pos + 2))); pos += 3; } else if (remainder >= 2 && str.charAt(pos) == '^') { // negated char - set.add(new CharRange(str.charAt(pos + 1), true)); + set.add(CharRange.isNot(str.charAt(pos + 1))); pos += 2; } else { // char - set.add(new CharRange(str.charAt(pos))); + set.add(CharRange.is(str.charAt(pos))); pos += 1; } } @@ -292,11 +275,11 @@ return false; } CharSet other = (CharSet) obj; - return (set.equals(other.set)); + return set.equals(other.set); } /** - *

Gets a hashCode compatable with the equals method.

+ *

Gets a hashCode compatible with the equals method.

* * @return a suitable hashCode * @since 2.0 Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSetUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSetUtils.java (.../CharSetUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/CharSetUtils.java (.../CharSetUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,68 +1,33 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import org.apache.commons.lang.text.StrBuilder; + /** *

Operations on CharSets.

* *

This class handles null input gracefully. * An exception will not be thrown for a null input. * Each method documents its behaviour in more detail.

* + *

#ThreadSafe#

* @see CharSet - * @author Henri Yandell - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Phil Steitz * @author Gary Gregory * @since 1.0 @@ -78,6 +43,7 @@ * to operate.

*/ public CharSetUtils() { + super(); } // Factory @@ -89,7 +55,7 @@ *
    *
  • "aeio" which implies 'a','e',..
  • *
  • "^e" implies not e.
  • - *
  • "ej-m" implies e,j->m. e,j,k,l,m.
  • + *
  • "ej-m" implies e,j->m. e,j,k,l,m.
  • *
* *
@@ -100,7 +66,7 @@
      *
      * @param set  the set, may be null
      * @return a CharSet instance, null if null input
-     * @deprecated Use {@link CharSet#getInstance(String)}.
+     * @deprecated Use {@link CharSet#getInstance(String[])}.
      *             Method will be removed in Commons Lang 3.0.
      */
     public static CharSet evaluateSet(String[] set) {
@@ -113,7 +79,7 @@
     // Squeeze
     //-----------------------------------------------------------------------
     /**
-     * 

Squeezes any repititions of a character that is mentioned in the + *

Squeezes any repetitions of a character that is mentioned in the * supplied set.

* *
@@ -125,13 +91,13 @@
      * CharSetUtils.squeeze("hello", "a-e") = "hello"
      * 
* - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str the string to squeeze, may be null * @param set the character set to use for manipulation, may be null * @return modified String, null if null string input */ public static String squeeze(String str, String set) { - if (str == null || str.length() == 0 || set == null || set.length() == 0) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(set)) { return str; } String[] strs = new String[1]; @@ -140,25 +106,25 @@ } /** - *

Squeezes any repititions of a character that is mentioned in the + *

Squeezes any repetitions of a character that is mentioned in the * supplied set.

* *

An example is:

*
    *
  • squeeze("hello", {"el"}) => "helo"
  • *
* - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str the string to squeeze, may be null * @param set the character set to use for manipulation, may be null * @return modified String, null if null string input */ public static String squeeze(String str, String[] set) { - if (str == null || str.length() == 0 || set == null || set.length == 0) { + if (StringUtils.isEmpty(str) || ArrayUtils.isEmpty(set)) { return str; } - CharSet chars = evaluateSet(set); - StringBuffer buffer = new StringBuffer(str.length()); + CharSet chars = CharSet.getInstance(set); + StrBuilder buffer = new StrBuilder(str.length()); char[] chrs = str.toCharArray(); int sz = chrs.length; char lastChar = ' '; @@ -191,13 +157,13 @@ * CharSetUtils.count("hello", "a-e") = 1 *
* - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to count characters in, may be null * @param set String set of characters to count, may be null * @return character count, zero if null string input */ public static int count(String str, String set) { - if (str == null || str.length() == 0 || set == null || set.length() == 0) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(set)) { return 0; } String[] strs = new String[1]; @@ -214,16 +180,16 @@ *
  • count("hello", {"c-f", "o"}) returns 2.
  • * * - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to count characters in, may be null * @param set String[] set of characters to count, may be null * @return character count, zero if null string input */ public static int count(String str, String[] set) { - if (str == null || str.length() == 0 || set == null || set.length == 0) { + if (StringUtils.isEmpty(str) || ArrayUtils.isEmpty(set)) { return 0; } - CharSet chars = evaluateSet(set); + CharSet chars = CharSet.getInstance(set); int count = 0; char[] chrs = str.toCharArray(); int sz = chrs.length; @@ -246,11 +212,11 @@ * CharSetUtils.keep("", *) = "" * CharSetUtils.keep(*, null) = "" * CharSetUtils.keep(*, "") = "" - * CharSetUtils.keep("hello", "hl") = "hll" - * CharSetUtils.keep("hello", "le") = "ell" + * CharSetUtils.keep("hello", "hl") = "hll" + * CharSetUtils.keep("hello", "le") = "ell" * * - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to keep characters from, may be null * @param set String set of characters to keep, may be null * @return modified String, null if null string input @@ -260,7 +226,7 @@ if (str == null) { return null; } - if (str.length() == 0 || set == null || set.length() == 0) { + if (str.length() == 0 || StringUtils.isEmpty(set)) { return ""; } String[] strs = new String[1]; @@ -275,10 +241,10 @@ *

    An example would be:

    *
      *
    • keep("hello", {"c-f", "o"}) - * returns "hll"
    • + * returns "eo" *
    * - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to keep characters from, may be null * @param set String[] set of characters to keep, may be null * @return modified String, null if null string input @@ -288,7 +254,7 @@ if (str == null) { return null; } - if (str.length() == 0 || set == null || set.length == 0) { + if (str.length() == 0 || ArrayUtils.isEmpty(set)) { return ""; } return modify(str, set, true); @@ -305,17 +271,17 @@ * CharSetUtils.delete("", *) = "" * CharSetUtils.delete(*, null) = * * CharSetUtils.delete(*, "") = * - * CharSetUtils.delete("hello", "hl") = "hll" - * CharSetUtils.delete("hello", "le") = "ell" + * CharSetUtils.delete("hello", "hl") = "eo" + * CharSetUtils.delete("hello", "le") = "ho" * * - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to delete characters from, may be null * @param set String set of characters to delete, may be null * @return modified String, null if null string input */ public static String delete(String str, String set) { - if (str == null || str.length() == 0 || set == null || set.length() == 0) { + if (StringUtils.isEmpty(str) || StringUtils.isEmpty(set)) { return str; } String[] strs = new String[1]; @@ -333,23 +299,30 @@ * "hll" * * - * @see #evaluateSet(java.lang.String[]) for set-syntax. + * @see CharSet#getInstance(java.lang.String) for set-syntax. * @param str String to delete characters from, may be null * @param set String[] set of characters to delete, may be null * @return modified String, null if null string input */ public static String delete(String str, String[] set) { - if (str == null || str.length() == 0 || set == null || set.length == 0) { + if (StringUtils.isEmpty(str) || ArrayUtils.isEmpty(set)) { return str; } return modify(str, set, false); } //----------------------------------------------------------------------- - // Implementation of delete and keep + /** + * Implementation of delete and keep + * + * @param str String to modify characters within + * @param set String[] set of characters to modify + * @param expect whether to evaluate on match, or non-match + * @return modified String + */ private static String modify(String str, String[] set, boolean expect) { - CharSet chars = evaluateSet(set); - StringBuffer buffer = new StringBuffer(str.length()); + CharSet chars = CharSet.getInstance(set); + StrBuilder buffer = new StrBuilder(str.length()); char[] chrs = str.toCharArray(); int sz = chrs.length; for(int i=0; iAn example is:

    *
      *
    • translate("hello", "ho", "jy") - * => jelly
    • + * => jelly *
    * *

    If the length of characters to search for is greater than the @@ -378,24 +351,27 @@ * *

          * CharSetUtils.translate(null, *, *) = null
    -     * CharSetUtils.translate("", *, *) = ""
    +     * CharSetUtils.translate("", *, *)   = ""
          * 
    * * @param str String to replace characters in, may be null * @param searchChars a set of characters to search for, must not be null - * @param replaceChars a set of characters to replace, must not be null or empty ("") + * @param replaceChars a set of characters to replace, must not be null or empty ("") * @return translated String, null if null string input - * @throws NullPointerException if with or repl + * @throws NullPointerException if searchChars or replaceChars * is null - * @throws ArrayIndexOutOfBoundsException if with is empty ("") + * @throws ArrayIndexOutOfBoundsException if replaceChars is empty ("") * @deprecated Use {@link StringUtils#replaceChars(String, String, String)}. * Method will be removed in Commons Lang 3.0. + * NOTE: StringUtils#replaceChars behaves differently when 'searchChars' is longer + * than 'replaceChars'. CharSetUtils#translate will use the last char of the replacement + * string whereas StringUtils#replaceChars will delete */ public static String translate(String str, String searchChars, String replaceChars) { - if (str == null || str.length() == 0) { + if (StringUtils.isEmpty(str)) { return str; } - StringBuffer buffer = new StringBuffer(str.length()); + StrBuilder buffer = new StrBuilder(str.length()); char[] chrs = str.toCharArray(); char[] withChrs = replaceChars.toCharArray(); int sz = chrs.length; Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/CharUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/CharUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/CharUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,576 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang; + +/** + *

    Operations on char primitives and Character objects.

    + * + *

    This class tries to handle null input gracefully. + * An exception will not be thrown for a null input. + * Each method documents its behaviour in more detail.

    + * + *

    #ThreadSafe#

    + * @author Apache Software Foundation + * @since 2.1 + * @version $Id$ + */ +public class CharUtils { + + private static final String CHAR_STRING = + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\b\t\n\u000b\f\r\u000e\u000f" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f" + + "\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027" + + "\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037" + + "\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f" + + "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047" + + "\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f" + + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057" + + "\u0058\u0059\u005a\u005b\\\u005d\u005e\u005f" + + "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067" + + "\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f" + + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077" + + "\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u007f"; + + private static final String[] CHAR_STRING_ARRAY = new String[128]; + private static final Character[] CHAR_ARRAY = new Character[128]; + + /** + * \u000a linefeed LF ('\n'). + * + * @see JLF: Escape Sequences + * for Character and String Literals + * @since 2.2 + */ + public static final char LF = '\n'; + + /** + * \u000d carriage return CR ('\r'). + * + * @see JLF: Escape Sequences + * for Character and String Literals + * @since 2.2 + */ + public static final char CR = '\r'; + + + static { + for (int i = 127; i >= 0; i--) { + CHAR_STRING_ARRAY[i] = CHAR_STRING.substring(i, i + 1); + CHAR_ARRAY[i] = new Character((char) i); + } + } + + /** + *

    CharUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as CharUtils.toString('c');.

    + * + *

    This constructor is public to permit tools that require a JavaBean instance + * to operate.

    + */ + public CharUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

    Converts the character to a Character.

    + * + *

    For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.

    + * + *
    +     *   CharUtils.toCharacterObject(' ')  = ' '
    +     *   CharUtils.toCharacterObject('A')  = 'A'
    +     * 
    + * + * @param ch the character to convert + * @return a Character of the specified character + */ + public static Character toCharacterObject(char ch) { + if (ch < CHAR_ARRAY.length) { + return CHAR_ARRAY[ch]; + } + return new Character(ch); + } + + /** + *

    Converts the String to a Character using the first character, returning + * null for empty Strings.

    + * + *

    For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.

    + * + *
    +     *   CharUtils.toCharacterObject(null) = null
    +     *   CharUtils.toCharacterObject("")   = null
    +     *   CharUtils.toCharacterObject("A")  = 'A'
    +     *   CharUtils.toCharacterObject("BA") = 'B'
    +     * 
    + * + * @param str the character to convert + * @return the Character value of the first letter of the String + */ + public static Character toCharacterObject(String str) { + if (StringUtils.isEmpty(str)) { + return null; + } + return toCharacterObject(str.charAt(0)); + } + + //----------------------------------------------------------------------- + /** + *

    Converts the Character to a char throwing an exception for null.

    + * + *
    +     *   CharUtils.toChar(null) = IllegalArgumentException
    +     *   CharUtils.toChar(' ')  = ' '
    +     *   CharUtils.toChar('A')  = 'A'
    +     * 
    + * + * @param ch the character to convert + * @return the char value of the Character + * @throws IllegalArgumentException if the Character is null + */ + public static char toChar(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The Character must not be null"); + } + return ch.charValue(); + } + + /** + *

    Converts the Character to a char handling null.

    + * + *
    +     *   CharUtils.toChar(null, 'X') = 'X'
    +     *   CharUtils.toChar(' ', 'X')  = ' '
    +     *   CharUtils.toChar('A', 'X')  = 'A'
    +     * 
    + * + * @param ch the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the Character or the default if null + */ + public static char toChar(Character ch, char defaultValue) { + if (ch == null) { + return defaultValue; + } + return ch.charValue(); + } + + //----------------------------------------------------------------------- + /** + *

    Converts the String to a char using the first character, throwing + * an exception on empty Strings.

    + * + *
    +     *   CharUtils.toChar(null) = IllegalArgumentException
    +     *   CharUtils.toChar("")   = IllegalArgumentException
    +     *   CharUtils.toChar("A")  = 'A'
    +     *   CharUtils.toChar("BA") = 'B'
    +     * 
    + * + * @param str the character to convert + * @return the char value of the first letter of the String + * @throws IllegalArgumentException if the String is empty + */ + public static char toChar(String str) { + if (StringUtils.isEmpty(str)) { + throw new IllegalArgumentException("The String must not be empty"); + } + return str.charAt(0); + } + + /** + *

    Converts the String to a char using the first character, defaulting + * the value on empty Strings.

    + * + *
    +     *   CharUtils.toChar(null, 'X') = 'X'
    +     *   CharUtils.toChar("", 'X')   = 'X'
    +     *   CharUtils.toChar("A", 'X')  = 'A'
    +     *   CharUtils.toChar("BA", 'X') = 'B'
    +     * 
    + * + * @param str the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the first letter of the String or the default if null + */ + public static char toChar(String str, char defaultValue) { + if (StringUtils.isEmpty(str)) { + return defaultValue; + } + return str.charAt(0); + } + + //----------------------------------------------------------------------- + /** + *

    Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

    + * + *

    This method coverts the char '1' to the int 1 and so on.

    + * + *
    +     *   CharUtils.toIntValue('3')  = 3
    +     *   CharUtils.toIntValue('A')  = IllegalArgumentException
    +     * 
    + * + * @param ch the character to convert + * @return the int value of the character + * @throws IllegalArgumentException if the character is not ASCII numeric + */ + public static int toIntValue(char ch) { + if (isAsciiNumeric(ch) == false) { + throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'"); + } + return ch - 48; + } + + /** + *

    Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

    + * + *

    This method coverts the char '1' to the int 1 and so on.

    + * + *
    +     *   CharUtils.toIntValue('3', -1)  = 3
    +     *   CharUtils.toIntValue('A', -1)  = -1
    +     * 
    + * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(char ch, int defaultValue) { + if (isAsciiNumeric(ch) == false) { + return defaultValue; + } + return ch - 48; + } + + /** + *

    Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

    + * + *

    This method coverts the char '1' to the int 1 and so on.

    + * + *
    +     *   CharUtils.toIntValue(null) = IllegalArgumentException
    +     *   CharUtils.toIntValue('3')  = 3
    +     *   CharUtils.toIntValue('A')  = IllegalArgumentException
    +     * 
    + * + * @param ch the character to convert, not null + * @return the int value of the character + * @throws IllegalArgumentException if the Character is not ASCII numeric or is null + */ + public static int toIntValue(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The character must not be null"); + } + return toIntValue(ch.charValue()); + } + + /** + *

    Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

    + * + *

    This method coverts the char '1' to the int 1 and so on.

    + * + *
    +     *   CharUtils.toIntValue(null, -1) = -1
    +     *   CharUtils.toIntValue('3', -1)  = 3
    +     *   CharUtils.toIntValue('A', -1)  = -1
    +     * 
    + * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(Character ch, int defaultValue) { + if (ch == null) { + return defaultValue; + } + return toIntValue(ch.charValue(), defaultValue); + } + + //----------------------------------------------------------------------- + /** + *

    Converts the character to a String that contains the one character.

    + * + *

    For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.

    + * + *
    +     *   CharUtils.toString(' ')  = " "
    +     *   CharUtils.toString('A')  = "A"
    +     * 
    + * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(char ch) { + if (ch < 128) { + return CHAR_STRING_ARRAY[ch]; + } + return new String(new char[] {ch}); + } + + /** + *

    Converts the character to a String that contains the one character.

    + * + *

    For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.

    + * + *

    If null is passed in, null will be returned.

    + * + *
    +     *   CharUtils.toString(null) = null
    +     *   CharUtils.toString(' ')  = " "
    +     *   CharUtils.toString('A')  = "A"
    +     * 
    + * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(Character ch) { + if (ch == null) { + return null; + } + return toString(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + *

    Converts the string to the unicode format '\u0020'.

    + * + *

    This format is the Java source code format.

    + * + *
    +     *   CharUtils.unicodeEscaped(' ') = "\u0020"
    +     *   CharUtils.unicodeEscaped('A') = "\u0041"
    +     * 
    + * + * @param ch the character to convert + * @return the escaped unicode string + */ + public static String unicodeEscaped(char ch) { + if (ch < 0x10) { + return "\\u000" + Integer.toHexString(ch); + } else if (ch < 0x100) { + return "\\u00" + Integer.toHexString(ch); + } else if (ch < 0x1000) { + return "\\u0" + Integer.toHexString(ch); + } + return "\\u" + Integer.toHexString(ch); + } + + /** + *

    Converts the string to the unicode format '\u0020'.

    + * + *

    This format is the Java source code format.

    + * + *

    If null is passed in, null will be returned.

    + * + *
    +     *   CharUtils.unicodeEscaped(null) = null
    +     *   CharUtils.unicodeEscaped(' ')  = "\u0020"
    +     *   CharUtils.unicodeEscaped('A')  = "\u0041"
    +     * 
    + * + * @param ch the character to convert, may be null + * @return the escaped unicode string, null if null input + */ + public static String unicodeEscaped(Character ch) { + if (ch == null) { + return null; + } + return unicodeEscaped(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + *

    Checks whether the character is ASCII 7 bit.

    + * + *
    +     *   CharUtils.isAscii('a')  = true
    +     *   CharUtils.isAscii('A')  = true
    +     *   CharUtils.isAscii('3')  = true
    +     *   CharUtils.isAscii('-')  = true
    +     *   CharUtils.isAscii('\n') = true
    +     *   CharUtils.isAscii('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if less than 128 + */ + public static boolean isAscii(char ch) { + return ch < 128; + } + + /** + *

    Checks whether the character is ASCII 7 bit printable.

    + * + *
    +     *   CharUtils.isAsciiPrintable('a')  = true
    +     *   CharUtils.isAsciiPrintable('A')  = true
    +     *   CharUtils.isAsciiPrintable('3')  = true
    +     *   CharUtils.isAsciiPrintable('-')  = true
    +     *   CharUtils.isAsciiPrintable('\n') = false
    +     *   CharUtils.isAsciiPrintable('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 32 and 126 inclusive + */ + public static boolean isAsciiPrintable(char ch) { + return ch >= 32 && ch < 127; + } + + /** + *

    Checks whether the character is ASCII 7 bit control.

    + * + *
    +     *   CharUtils.isAsciiControl('a')  = false
    +     *   CharUtils.isAsciiControl('A')  = false
    +     *   CharUtils.isAsciiControl('3')  = false
    +     *   CharUtils.isAsciiControl('-')  = false
    +     *   CharUtils.isAsciiControl('\n') = true
    +     *   CharUtils.isAsciiControl('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if less than 32 or equals 127 + */ + public static boolean isAsciiControl(char ch) { + return ch < 32 || ch == 127; + } + + /** + *

    Checks whether the character is ASCII 7 bit alphabetic.

    + * + *
    +     *   CharUtils.isAsciiAlpha('a')  = true
    +     *   CharUtils.isAsciiAlpha('A')  = true
    +     *   CharUtils.isAsciiAlpha('3')  = false
    +     *   CharUtils.isAsciiAlpha('-')  = false
    +     *   CharUtils.isAsciiAlpha('\n') = false
    +     *   CharUtils.isAsciiAlpha('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlpha(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + + /** + *

    Checks whether the character is ASCII 7 bit alphabetic upper case.

    + * + *
    +     *   CharUtils.isAsciiAlphaUpper('a')  = false
    +     *   CharUtils.isAsciiAlphaUpper('A')  = true
    +     *   CharUtils.isAsciiAlphaUpper('3')  = false
    +     *   CharUtils.isAsciiAlphaUpper('-')  = false
    +     *   CharUtils.isAsciiAlphaUpper('\n') = false
    +     *   CharUtils.isAsciiAlphaUpper('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 65 and 90 inclusive + */ + public static boolean isAsciiAlphaUpper(char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + *

    Checks whether the character is ASCII 7 bit alphabetic lower case.

    + * + *
    +     *   CharUtils.isAsciiAlphaLower('a')  = true
    +     *   CharUtils.isAsciiAlphaLower('A')  = false
    +     *   CharUtils.isAsciiAlphaLower('3')  = false
    +     *   CharUtils.isAsciiAlphaLower('-')  = false
    +     *   CharUtils.isAsciiAlphaLower('\n') = false
    +     *   CharUtils.isAsciiAlphaLower('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 97 and 122 inclusive + */ + public static boolean isAsciiAlphaLower(char ch) { + return ch >= 'a' && ch <= 'z'; + } + + /** + *

    Checks whether the character is ASCII 7 bit numeric.

    + * + *
    +     *   CharUtils.isAsciiNumeric('a')  = false
    +     *   CharUtils.isAsciiNumeric('A')  = false
    +     *   CharUtils.isAsciiNumeric('3')  = true
    +     *   CharUtils.isAsciiNumeric('-')  = false
    +     *   CharUtils.isAsciiNumeric('\n') = false
    +     *   CharUtils.isAsciiNumeric('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 48 and 57 inclusive + */ + public static boolean isAsciiNumeric(char ch) { + return ch >= '0' && ch <= '9'; + } + + /** + *

    Checks whether the character is ASCII 7 bit numeric.

    + * + *
    +     *   CharUtils.isAsciiAlphanumeric('a')  = true
    +     *   CharUtils.isAsciiAlphanumeric('A')  = true
    +     *   CharUtils.isAsciiAlphanumeric('3')  = true
    +     *   CharUtils.isAsciiAlphanumeric('-')  = false
    +     *   CharUtils.isAsciiAlphanumeric('\n') = false
    +     *   CharUtils.isAsciiAlphanumeric('©') = false
    +     * 
    + * + * @param ch the character to check + * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlphanumeric(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + } + + // ----------------- Following code copied from Apache Harmony (Character class) + /** + * Indicates whether {@code ch} is a high- (or leading-) surrogate code unit + * that is used for representing supplementary characters in UTF-16 + * encoding. + * + * @param ch + * the character to test. + * @return {@code true} if {@code ch} is a high-surrogate code unit; + * {@code false} otherwise. + */ + static boolean isHighSurrogate(char ch) { + return ('\uD800' <= ch && '\uDBFF' >= ch); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/ClassUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/ClassUtils.java (.../ClassUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/ClassUtils.java (.../ClassUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,95 +1,138 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.text.StrBuilder; + /** *

    Operates on classes without using reflection.

    * *

    This class handles invalid null inputs as best it can. * Each method documents its behaviour in more detail.

    * - * @author Stephen Colebourne + *

    The notion of a canonical name includes the human + * readable name for the type, for example int[]. The + * non-canonical method variants work with the JVM names, such as + * [I.

    + * + * @author Apache Software Foundation * @author Gary Gregory + * @author Norm Deane + * @author Alban Peignier + * @author Tomasz Blachowicz * @since 2.0 * @version $Id$ */ public class ClassUtils { /** - *

    The package separator character: ..

    + *

    The package separator character: '.' == {@value}.

    */ public static final char PACKAGE_SEPARATOR_CHAR = '.'; - + /** - *

    The package separator String: ..

    + *

    The package separator String: ".".

    */ public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); - + /** - *

    The inner class separator character: $.

    + *

    The inner class separator character: '$' == {@value}.

    */ public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; - + /** - *

    The inner class separator String: $.

    + *

    The inner class separator String: "$".

    */ public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); - + /** + * Maps primitive Classes to their corresponding wrapper Class. + */ + private static final Map primitiveWrapperMap = new HashMap(); + static { + primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); + primitiveWrapperMap.put(Byte.TYPE, Byte.class); + primitiveWrapperMap.put(Character.TYPE, Character.class); + primitiveWrapperMap.put(Short.TYPE, Short.class); + primitiveWrapperMap.put(Integer.TYPE, Integer.class); + primitiveWrapperMap.put(Long.TYPE, Long.class); + primitiveWrapperMap.put(Double.TYPE, Double.class); + primitiveWrapperMap.put(Float.TYPE, Float.class); + primitiveWrapperMap.put(Void.TYPE, Void.TYPE); + } + + /** + * Maps wrapper Classes to their corresponding primitive types. + */ + private static final Map wrapperPrimitiveMap = new HashMap(); + static { + for (Iterator it = primitiveWrapperMap.keySet().iterator(); it.hasNext();) { + Class primitiveClass = (Class) it.next(); + Class wrapperClass = (Class) primitiveWrapperMap.get(primitiveClass); + if (!primitiveClass.equals(wrapperClass)) { + wrapperPrimitiveMap.put(wrapperClass, primitiveClass); + } + } + } + + /** + * Maps a primitive class name to its corresponding abbreviation used in array class names. + */ + private static final Map abbreviationMap = new HashMap(); + + /** + * Maps an abbreviation used in array class names to corresponding primitive class name. + */ + private static final Map reverseAbbreviationMap = new HashMap(); + + /** + * Add primitive type abbreviation to maps of abbreviations. + * + * @param primitive Canonical name of primitive type + * @param abbreviation Corresponding abbreviation of primitive type + */ + private static void addAbbreviation(String primitive, String abbreviation) { + abbreviationMap.put(primitive, abbreviation); + reverseAbbreviationMap.put(abbreviation, primitive); + } + + /** + * Feed abbreviation maps + */ + static { + addAbbreviation("int", "I"); + addAbbreviation("boolean", "Z"); + addAbbreviation("float", "F"); + addAbbreviation("long", "J"); + addAbbreviation("short", "S"); + addAbbreviation("byte", "B"); + addAbbreviation("double", "D"); + addAbbreviation("char", "C"); + } + + /** *

    ClassUtils instances should NOT be constructed in standard programming. * Instead, the class should be used as * ClassUtils.getShortClassName(cls).

    @@ -98,13 +141,14 @@ * instance to operate.

    */ public ClassUtils() { + super(); } // Short class name // ---------------------------------------------------------------------- /** *

    Gets the class name minus the package name for an Object.

    - * + * * @param object the class to get the short name for, may be null * @param valueIfNull the value to return if null * @return the class name of the object without the package name, or the null value @@ -113,55 +157,71 @@ if (object == null) { return valueIfNull; } - return getShortClassName(object.getClass().getName()); + return getShortClassName(object.getClass()); } - + /** *

    Gets the class name minus the package name from a Class.

    - * - * @param cls the class to get the short name for, must not be - * null - * @return the class name without the package name - * @throws IllegalArgumentException if the class is null + * + * @param cls the class to get the short name for. + * @return the class name without the package name or an empty string */ public static String getShortClassName(Class cls) { if (cls == null) { - throw new IllegalArgumentException("The class must not be null"); + return StringUtils.EMPTY; } return getShortClassName(cls.getName()); } - + /** *

    Gets the class name minus the package name from a String.

    * *

    The string passed in is assumed to be a class name - it is not checked.

    - * - * @param className the className to get the short name for, - * must not be empty or null - * @return the class name of the class without the package name - * @throws IllegalArgumentException if the className is empty + * + * @param className the className to get the short name for + * @return the class name of the class without the package name or an empty string */ public static String getShortClassName(String className) { - if (StringUtils.isEmpty(className)) { - throw new IllegalArgumentException("The class name must not be empty"); + if (className == null) { + return StringUtils.EMPTY; } - char[] chars = className.toCharArray(); - int lastDot = 0; - for (int i = 0; i < chars.length; i++) { - if (chars[i] == PACKAGE_SEPARATOR_CHAR) { - lastDot = i + 1; - } else if (chars[i] == INNER_CLASS_SEPARATOR_CHAR) { // handle inner classes - chars[i] = PACKAGE_SEPARATOR_CHAR; + if (className.length() == 0) { + return StringUtils.EMPTY; + } + + StrBuilder arrayPrefix = new StrBuilder(); + + // Handle array encoding + if (className.startsWith("[")) { + while (className.charAt(0) == '[') { + className = className.substring(1); + arrayPrefix.append("[]"); } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1, className.length() - 1); + } } - return new String(chars, lastDot, chars.length - lastDot); + + if (reverseAbbreviationMap.containsKey(className)) { + className = (String)reverseAbbreviationMap.get(className); + } + + int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + int innerIdx = className.indexOf( + INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); + String out = className.substring(lastDotIdx + 1); + if (innerIdx != -1) { + out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); + } + return out + arrayPrefix; } - + // Package name // ---------------------------------------------------------------------- /** *

    Gets the package name of an Object.

    - * + * * @param object the class to get the package name for, may be null * @param valueIfNull the value to return if null * @return the package name of the object, or the null value @@ -170,51 +230,58 @@ if (object == null) { return valueIfNull; } - return getPackageName(object.getClass().getName()); + return getPackageName(object.getClass()); } - + /** *

    Gets the package name of a Class.

    - * - * @param cls the class to get the package name for, - * must not be null - * @return the package name - * @throws IllegalArgumentException if the class is null + * + * @param cls the class to get the package name for, may be null. + * @return the package name or an empty string */ public static String getPackageName(Class cls) { if (cls == null) { - throw new IllegalArgumentException("The class must not be null"); + return StringUtils.EMPTY; } return getPackageName(cls.getName()); } - + /** *

    Gets the package name from a String.

    * *

    The string passed in is assumed to be a class name - it is not checked.

    - * - * @param className the className to get the package name for, - * must not be empty or null - * @return the package name - * @throws IllegalArgumentException if the className is empty + *

    If the class is unpackaged, return an empty string.

    + * + * @param className the className to get the package name for, may be null + * @return the package name or an empty string */ public static String getPackageName(String className) { - if (StringUtils.isEmpty(className)) { - throw new IllegalArgumentException("The class name must not be empty"); + if (className == null || className.length() == 0) { + return StringUtils.EMPTY; } + + // Strip array encoding + while (className.charAt(0) == '[') { + className = className.substring(1); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1); + } + int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); if (i == -1) { - return ""; + return StringUtils.EMPTY; } return className.substring(0, i); } - + // Superclasses/Superinterfaces // ---------------------------------------------------------------------- /** *

    Gets a List of superclasses for the given class.

    - * - * @param cls the class to look up, must not be null + * + * @param cls the class to look up, may be null * @return the List of superclasses in order going up from this one * null if null input */ @@ -230,97 +297,52 @@ } return classes; } - + /** *

    Gets a List of all interfaces implemented by the given * class and its superclasses.

    * *

    The order is determined by looking through each interface in turn as - * declared in the source file and following its hieracrchy up. Then each - * superclass is considered in the same way. Later duplicates are ignored, + * declared in the source file and following its hierarchy up. Then each + * superclass is considered in the same way. Later duplicates are ignored, * so the order is maintained.

    - * - * @param cls the class to look up, must not be null + * + * @param cls the class to look up, may be null * @return the List of interfaces in order, * null if null input */ public static List getAllInterfaces(Class cls) { if (cls == null) { return null; } - List list = new ArrayList(); + + List interfacesFound = new ArrayList(); + getAllInterfaces(cls, interfacesFound); + + return interfacesFound; + } + + /** + * Get the interfaces for the specified class. + * + * @param cls the class to look up, may be null + * @param interfacesFound the Set of interfaces for the class + */ + private static void getAllInterfaces(Class cls, List interfacesFound) { while (cls != null) { Class[] interfaces = cls.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { - if (list.contains(interfaces[i]) == false) { - list.add(interfaces[i]); + if (!interfacesFound.contains(interfaces[i])) { + interfacesFound.add(interfaces[i]); + getAllInterfaces(interfaces[i], interfacesFound); } - List superInterfaces = getAllInterfaces(interfaces[i]); - for (Iterator it = superInterfaces.iterator(); it.hasNext();) { - Class intface = (Class) it.next(); - if (list.contains(intface) == false) { - list.add(intface); - } - } } + cls = cls.getSuperclass(); - } - return list; - } - -// /** -// *

    Gets a List of subclasses of the specified class.

    -// * -// *

    This method searches the classpath to find all the subclasses -// * of a particular class available. No classes are loaded, the -// * returned list contains class names, not classes.

    -// * -// * @param cls the class to find subclasses for -// * @return the List of subclass String class names -// * @throws IllegalArgumentException if the class is null -// */ -// public static List getAllSubclassNames(Class cls) { -// if (cls == null) { -// throw new IllegalArgumentException("The class must not be null"); -// } -// // TODO Use JavaWorld tip for searching the classpath -// return null; -// } + } + } -// /** -// *

    Gets a List of subclasses of the specified class.

    -// * -// *

    This method searches the classpath to find all the subclasses -// * of a particular class available.

    -// * -// * @param cls the class to find subclasses for -// * @return the List of subclasses -// * @throws IllegalArgumentException if the class is null -// */ -// public static List getAllSubclasses(Class cls) { -// List names = getAllSubclassNames(cls); -// return convertClassNamesToClasses(names); -// } - -// /** -// *

    Gets a List of implementations of the specified interface.

    -// * -// *

    This method searches the classpath to find all the implementations -// * of a particular interface available. No classes are loaded, the -// * returned list contains class names, not classes.

    -// * -// * @param cls the class to find sub classes for -// * @return the List of implementation String class names -// * @throws IllegalArgumentException if the class is null -// */ -// public static List getAllImplementationClassNames(Class cls) { -// if (cls == null) { -// throw new IllegalArgumentException("The class must not be null"); -// } -// // TODO Use JavaWorld tip for searching the classpath -// return null; -// } - // Convert list // ---------------------------------------------------------------------- /** @@ -329,7 +351,7 @@ *

    A new List is returned. If the class name cannot be found, null * is stored in the List. If the class name in the List is * null, null is stored in the output List.

    - * + * * @param classNames the classNames to change * @return a List of Class objects corresponding to the class names, * null if null input @@ -350,18 +372,18 @@ } return classes; } - + /** *

    Given a List of Class objects, this method converts * them into class names.

    * *

    A new List is returned. null objects will be copied into * the returned list as null.

    - * + * * @param classes the classes to change - * @return a List of Class objects corresponding to the class names, + * @return a List of class names corresponding to the Class objects, * null if null input - * @throws ClassCastException if classNames contains a non Class or null entry + * @throws ClassCastException if classes contains a non-Class entry */ public static List convertClassesToClassNames(List classes) { if (classes == null) { @@ -378,15 +400,15 @@ } return classNames; } - + // Is assignable // ---------------------------------------------------------------------- /** *

    Checks if an array of Classes can be assigned to another array of Classes.

    * *

    This method calls {@link #isAssignable(Class, Class) isAssignable} for each * Class pair in the input arrays. It can be used to check if a set of arguments - * (the first parameter) are suitably compatable with a set of method parameter types + * (the first parameter) are suitably compatible with a set of method parameter types * (the second parameter).

    * *

    Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this @@ -404,7 +426,7 @@ *

    Specifically, this method tests whether the type represented by the * specified Class parameter can be converted to the type * represented by this Class object via an identity conversion - * widening primitive or widening reference conversion. See + * widening primitive or widening reference conversion. See * The Java Language Specification, * sections 5.1.1, 5.1.2 and 5.1.4 for details.

    * @@ -413,6 +435,43 @@ * @return true if assignment possible */ public static boolean isAssignable(Class[] classArray, Class[] toClassArray) { + return isAssignable(classArray, toClassArray, false); + } + + /** + *

    Checks if an array of Classes can be assigned to another array of Classes.

    + * + *

    This method calls {@link #isAssignable(Class, Class) isAssignable} for each + * Class pair in the input arrays. It can be used to check if a set of arguments + * (the first parameter) are suitably compatible with a set of method parameter types + * (the second parameter).

    + * + *

    Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this + * method takes into account widenings of primitive classes and + * nulls.

    + * + *

    Primitive widenings allow an int to be assigned to a long, + * float or double. This method returns the correct + * result for these cases.

    + * + *

    Null may be assigned to any reference type. This method will + * return true if null is passed in and the toClass is + * non-primitive.

    + * + *

    Specifically, this method tests whether the type represented by the + * specified Class parameter can be converted to the type + * represented by this Class object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

    + * + * @param classArray the array of Classes to check, may be null + * @param toClassArray the array of Classes to try to assign into, may be null + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return true if assignment possible + * @since 2.5 + */ + public static boolean isAssignable(Class[] classArray, Class[] toClassArray, boolean autoboxing) { if (ArrayUtils.isSameLength(classArray, toClassArray) == false) { return false; } @@ -423,13 +482,13 @@ toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; } for (int i = 0; i < classArray.length; i++) { - if (isAssignable(classArray[i], toClassArray[i]) == false) { + if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { return false; } } return true; } - + /** *

    Checks if one Class can be assigned to a variable of * another Class.

    @@ -448,7 +507,7 @@ *

    Specifically, this method tests whether the type represented by the * specified Class parameter can be converted to the type * represented by this Class object via an identity conversion - * widening primitive or widening reference conversion. See + * widening primitive or widening reference conversion. See * The Java Language Specification, * sections 5.1.1, 5.1.2 and 5.1.4 for details.

    * @@ -457,13 +516,60 @@ * @return true if assignment possible */ public static boolean isAssignable(Class cls, Class toClass) { + return isAssignable(cls, toClass, false); + } + + /** + *

    Checks if one Class can be assigned to a variable of + * another Class.

    + * + *

    Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, + * this method takes into account widenings of primitive classes and + * nulls.

    + * + *

    Primitive widenings allow an int to be assigned to a long, float or + * double. This method returns the correct result for these cases.

    + * + *

    Null may be assigned to any reference type. This method + * will return true if null is passed in and the + * toClass is non-primitive.

    + * + *

    Specifically, this method tests whether the type represented by the + * specified Class parameter can be converted to the type + * represented by this Class object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

    + * + * @param cls the Class to check, may be null + * @param toClass the Class to try to assign into, returns false if null + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return true if assignment possible + * @since 2.5 + */ + public static boolean isAssignable(Class cls, Class toClass, boolean autoboxing) { if (toClass == null) { return false; } // have to check for null, as isAssignableFrom doesn't if (cls == null) { return !(toClass.isPrimitive()); } + //autoboxing: + if (autoboxing) { + if (cls.isPrimitive() && !toClass.isPrimitive()) { + cls = primitiveToWrapper(cls); + if (cls == null) { + return false; + } + } + if (toClass.isPrimitive() && !cls.isPrimitive()) { + cls = wrapperToPrimitive(cls); + if (cls == null) { + return false; + } + } + } if (cls.equals(toClass)) { return true; } @@ -472,12 +578,12 @@ return false; } if (Integer.TYPE.equals(cls)) { - return Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) + return Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Long.TYPE.equals(cls)) { - return Float.TYPE.equals(toClass) + return Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Boolean.TYPE.equals(cls)) { @@ -490,44 +596,474 @@ return Double.TYPE.equals(toClass); } if (Character.TYPE.equals(cls)) { - return Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Short.TYPE.equals(cls)) { - return Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } if (Byte.TYPE.equals(cls)) { - return Short.TYPE.equals(toClass) - || Integer.TYPE.equals(toClass) - || Long.TYPE.equals(toClass) - || Float.TYPE.equals(toClass) + return Short.TYPE.equals(toClass) + || Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass); } // should never get here return false; } return toClass.isAssignableFrom(cls); } - + + /** + *

    Converts the specified primitive Class object to its corresponding + * wrapper Class object.

    + * + *

    NOTE: From v2.2, this method handles Void.TYPE, + * returning Void.TYPE.

    + * + * @param cls the class to convert, may be null + * @return the wrapper class for cls or cls if + * cls is not a primitive. null if null input. + * @since 2.1 + */ + public static Class primitiveToWrapper(Class cls) { + Class convertedClass = cls; + if (cls != null && cls.isPrimitive()) { + convertedClass = (Class) primitiveWrapperMap.get(cls); + } + return convertedClass; + } + + /** + *

    Converts the specified array of primitive Class objects to an array of + * its corresponding wrapper Class objects.

    + * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the wrapper class or + * the original class if class is not a primitive. null if null input. + * Empty array if an empty array passed in. + * @since 2.1 + */ + public static Class[] primitivesToWrappers(Class[] classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = primitiveToWrapper(classes[i]); + } + return convertedClasses; + } + + /** + *

    Converts the specified wrapper class to its corresponding primitive + * class.

    + * + *

    This method is the counter part of primitiveToWrapper(). + * If the passed in class is a wrapper class for a primitive type, this + * primitive type will be returned (e.g. Integer.TYPE for + * Integer.class). For other classes, or if the parameter is + * null, the return value is null.

    + * + * @param cls the class to convert, may be null + * @return the corresponding primitive type if cls is a + * wrapper class, null otherwise + * @see #primitiveToWrapper(Class) + * @since 2.4 + */ + public static Class wrapperToPrimitive(Class cls) { + return (Class) wrapperPrimitiveMap.get(cls); + } + + /** + *

    Converts the specified array of wrapper Class objects to an array of + * its corresponding primitive Class objects.

    + * + *

    This method invokes wrapperToPrimitive() for each element + * of the passed in array.

    + * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the primitive class or + * null if the original class is not a wrapper class. null if null input. + * Empty array if an empty array passed in. + * @see #wrapperToPrimitive(Class) + * @since 2.4 + */ + public static Class[] wrappersToPrimitives(Class[] classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = wrapperToPrimitive(classes[i]); + } + return convertedClasses; + } + // Inner class // ---------------------------------------------------------------------- /** *

    Is the specified class an inner class or static nested class.

    - * - * @param cls the class to check + * + * @param cls the class to check, may be null * @return true if the class is an inner or static nested class, * false if not or null */ public static boolean isInnerClass(Class cls) { if (cls == null) { return false; } - return (cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0); + return cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0; } - + + // Class loading + // ---------------------------------------------------------------------- + /** + * Returns the class represented by className using the + * classLoader. This implementation supports the syntaxes + * "java.util.Map.Entry[]", "java.util.Map$Entry[]", + * "[Ljava.util.Map.Entry;", and "[Ljava.util.Map$Entry;". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by className using the classLoader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass( + ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException { + try { + Class clazz; + if (abbreviationMap.containsKey(className)) { + String clsName = "[" + abbreviationMap.get(className); + clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); + } else { + clazz = Class.forName(toCanonicalName(className), initialize, classLoader); + } + return clazz; + } catch (ClassNotFoundException ex) { + // allow path separators (.) as inner class name separators + int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + + if (lastDotIndex != -1) { + try { + return getClass(classLoader, className.substring(0, lastDotIndex) + + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), + initialize); + } catch (ClassNotFoundException ex2) { + } + } + + throw ex; + } + } + + /** + * Returns the (initialized) class represented by className + * using the classLoader. This implementation supports + * the syntaxes "java.util.Map.Entry[]", + * "java.util.Map$Entry[]", "[Ljava.util.Map.Entry;", + * and "[Ljava.util.Map$Entry;". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @return the class represented by className using the classLoader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(ClassLoader classLoader, String className) throws ClassNotFoundException { + return getClass(classLoader, className, true); + } + + /** + * Returns the (initialized) class represented by className + * using the current thread's context class loader. This implementation + * supports the syntaxes "java.util.Map.Entry[]", + * "java.util.Map$Entry[]", "[Ljava.util.Map.Entry;", + * and "[Ljava.util.Map$Entry;". + * + * @param className the class name + * @return the class represented by className using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(String className) throws ClassNotFoundException { + return getClass(className, true); + } + + /** + * Returns the class represented by className using the + * current thread's context class loader. This implementation supports the + * syntaxes "java.util.Map.Entry[]", "java.util.Map$Entry[]", + * "[Ljava.util.Map.Entry;", and "[Ljava.util.Map$Entry;". + * + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by className using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(String className, boolean initialize) throws ClassNotFoundException { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; + return getClass(loader, className, initialize ); + } + + // Public method + // ---------------------------------------------------------------------- + /** + *

    Returns the desired Method much like Class.getMethod, however + * it ensures that the returned Method is from a public class or interface and not + * from an anonymous inner class. This means that the Method is invokable and + * doesn't fall foul of Java bug + * 4071957). + * + *

    Set set = Collections.unmodifiableSet(...);
    +     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
    +     *  Object result = method.invoke(set, new Object[]);
    + *

    + * + * @param cls the class to check, not null + * @param methodName the name of the method + * @param parameterTypes the list of parameters + * @return the method + * @throws NullPointerException if the class is null + * @throws SecurityException if a a security violation occured + * @throws NoSuchMethodException if the method is not found in the given class + * or if the metothod doen't conform with the requirements + */ + public static Method getPublicMethod(Class cls, String methodName, Class parameterTypes[]) + throws SecurityException, NoSuchMethodException { + + Method declaredMethod = cls.getMethod(methodName, parameterTypes); + if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { + return declaredMethod; + } + + List candidateClasses = new ArrayList(); + candidateClasses.addAll(getAllInterfaces(cls)); + candidateClasses.addAll(getAllSuperclasses(cls)); + + for (Iterator it = candidateClasses.iterator(); it.hasNext(); ) { + Class candidateClass = (Class) it.next(); + if (!Modifier.isPublic(candidateClass.getModifiers())) { + continue; + } + Method candidateMethod; + try { + candidateMethod = candidateClass.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException ex) { + continue; + } + if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { + return candidateMethod; + } + } + + throw new NoSuchMethodException("Can't find a public method for " + + methodName + " " + ArrayUtils.toString(parameterTypes)); + } + + // ---------------------------------------------------------------------- + /** + * Converts a class name to a JLS style class name. + * + * @param className the class name + * @return the converted name + */ + private static String toCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + throw new NullArgumentException("className"); + } else if (className.endsWith("[]")) { + StrBuilder classNameBuffer = new StrBuilder(); + while (className.endsWith("[]")) { + className = className.substring(0, className.length() - 2); + classNameBuffer.append("["); + } + String abbreviation = (String) abbreviationMap.get(className); + if (abbreviation != null) { + classNameBuffer.append(abbreviation); + } else { + classNameBuffer.append("L").append(className).append(";"); + } + className = classNameBuffer.toString(); + } + return className; + } + + /** + *

    Converts an array of Object in to an array of Class objects. + * If any of these objects is null, a null element will be inserted into the array.

    + * + *

    This method returns null for a null input array.

    + * + * @param array an Object array + * @return a Class array, null if null array input + * @since 2.4 + */ + public static Class[] toClass(Object[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return ArrayUtils.EMPTY_CLASS_ARRAY; + } + Class[] classes = new Class[array.length]; + for (int i = 0; i < array.length; i++) { + classes[i] = array[i] == null ? null : array[i].getClass(); + } + return classes; + } + + // Short canonical name + // ---------------------------------------------------------------------- + /** + *

    Gets the canonical name minus the package name for an Object.

    + * + * @param object the class to get the short name for, may be null + * @param valueIfNull the value to return if null + * @return the canonical name of the object without the package name, or the null value + * @since 2.4 + */ + public static String getShortCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortCanonicalName(object.getClass().getName()); + } + + /** + *

    Gets the canonical name minus the package name from a Class.

    + * + * @param cls the class to get the short name for. + * @return the canonical name without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getShortCanonicalName(cls.getName()); + } + + /** + *

    Gets the canonical name minus the package name from a String.

    + * + *

    The string passed in is assumed to be a canonical name - it is not checked.

    + * + * @param canonicalName the class name to get the short name for + * @return the canonical name of the class without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(String canonicalName) { + return ClassUtils.getShortClassName(getCanonicalName(canonicalName)); + } + + // Package name + // ---------------------------------------------------------------------- + /** + *

    Gets the package name from the canonical name of an Object.

    + * + * @param object the class to get the package name for, may be null + * @param valueIfNull the value to return if null + * @return the package name of the object, or the null value + * @since 2.4 + */ + public static String getPackageCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageCanonicalName(object.getClass().getName()); + } + + /** + *

    Gets the package name from the canonical name of a Class.

    + * + * @param cls the class to get the package name for, may be null. + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageCanonicalName(cls.getName()); + } + + /** + *

    Gets the package name from the canonical name.

    + * + *

    The string passed in is assumed to be a canonical name - it is not checked.

    + *

    If the class is unpackaged, return an empty string.

    + * + * @param canonicalName the canonical name to get the package name for, may be null + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(String canonicalName) { + return ClassUtils.getPackageName(getCanonicalName(canonicalName)); + } + + /** + *

    Converts a given name of class into canonical format. + * If name of class is not a name of array class it returns + * unchanged name.

    + *

    Example: + *

      + *
    • getCanonicalName("[I") = "int[]"
    • + *
    • getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"
    • + *
    • getCanonicalName("java.lang.String") = "java.lang.String"
    • + *
    + *

    + * + * @param className the name of class + * @return canonical form of class name + * @since 2.4 + */ + private static String getCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + return null; + } else { + int dim = 0; + while (className.startsWith("[")) { + dim++; + className = className.substring(1); + } + if (dim < 1) { + return className; + } else { + if (className.startsWith("L")) { + className = className.substring( + 1, + className.endsWith(";") + ? className.length() - 1 + : className.length()); + } else { + if (className.length() > 0) { + className = (String) reverseAbbreviationMap.get( + className.substring(0, 1)); + } + } + StrBuilder canonicalClassNameBuffer = new StrBuilder(className); + for (int i = 0; i < dim; i++) { + canonicalClassNameBuffer.append("[]"); + } + return canonicalClassNameBuffer.toString(); + } + } + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/Entities.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/Entities.java (.../Entities.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/Entities.java (.../Entities.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,123 +1,89 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.apache.commons.lang; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; /** - *

    Provides HTML and XML entity utilities.

    - * + *

    + * Provides HTML and XML entity utilities. + *

    + * * @see ISO Entities - * @see
    HTML 3.2 Character Entities for ISO Latin-1 - * @see
    HTML 4.0 Character entity references - * @see
    HTML 4.01 Character References - * @see
    HTML 4.01 Code positions - * + * @see HTML 3.2 Character Entities for ISO Latin-1 + * @see HTML 4.0 Character entity references + * @see HTML 4.01 Character References + * @see HTML 4.01 Code positions + * * @author Alexander Day Chaffee * @author Gary Gregory * @since 2.0 * @version $Id$ */ class Entities { - private static final String[][] BASIC_ARRAY = { - {"quot", "34"}, // " - double-quote + private static final String[][] BASIC_ARRAY = {{"quot", "34"}, // " - double-quote {"amp", "38"}, // & - ampersand {"lt", "60"}, // < - less-than {"gt", "62"}, // > - greater-than }; - private static final String[][] APOS_ARRAY = { - {"apos", "39"}, // XML apostrophe + private static final String[][] APOS_ARRAY = {{"apos", "39"}, // XML apostrophe }; // package scoped for testing - static final String[][] ISO8859_1_ARRAY = { - {"nbsp", "160"}, // non-breaking space - {"iexcl", "161"}, //inverted exclamation mark - {"cent", "162"}, //cent sign - {"pound", "163"}, //pound sign - {"curren", "164"}, //currency sign - {"yen", "165"}, //yen sign = yuan sign - {"brvbar", "166"}, //broken bar = broken vertical bar - {"sect", "167"}, //section sign - {"uml", "168"}, //diaeresis = spacing diaeresis + static final String[][] ISO8859_1_ARRAY = {{"nbsp", "160"}, // non-breaking space + {"iexcl", "161"}, // inverted exclamation mark + {"cent", "162"}, // cent sign + {"pound", "163"}, // pound sign + {"curren", "164"}, // currency sign + {"yen", "165"}, // yen sign = yuan sign + {"brvbar", "166"}, // broken bar = broken vertical bar + {"sect", "167"}, // section sign + {"uml", "168"}, // diaeresis = spacing diaeresis {"copy", "169"}, // � - copyright sign - {"ordf", "170"}, //feminine ordinal indicator - {"laquo", "171"}, //left-pointing double angle quotation mark = left pointing guillemet - {"not", "172"}, //not sign - {"shy", "173"}, //soft hyphen = discretionary hyphen + {"ordf", "170"}, // feminine ordinal indicator + {"laquo", "171"}, // left-pointing double angle quotation mark = left pointing guillemet + {"not", "172"}, // not sign + {"shy", "173"}, // soft hyphen = discretionary hyphen {"reg", "174"}, // � - registered trademark sign - {"macr", "175"}, //macron = spacing macron = overline = APL overbar - {"deg", "176"}, //degree sign - {"plusmn", "177"}, //plus-minus sign = plus-or-minus sign - {"sup2", "178"}, //superscript two = superscript digit two = squared - {"sup3", "179"}, //superscript three = superscript digit three = cubed - {"acute", "180"}, //acute accent = spacing acute - {"micro", "181"}, //micro sign - {"para", "182"}, //pilcrow sign = paragraph sign - {"middot", "183"}, //middle dot = Georgian comma = Greek middle dot - {"cedil", "184"}, //cedilla = spacing cedilla - {"sup1", "185"}, //superscript one = superscript digit one - {"ordm", "186"}, //masculine ordinal indicator - {"raquo", "187"}, //right-pointing double angle quotation mark = right pointing guillemet - {"frac14", "188"}, //vulgar fraction one quarter = fraction one quarter - {"frac12", "189"}, //vulgar fraction one half = fraction one half - {"frac34", "190"}, //vulgar fraction three quarters = fraction three quarters - {"iquest", "191"}, //inverted question mark = turned question mark + {"macr", "175"}, // macron = spacing macron = overline = APL overbar + {"deg", "176"}, // degree sign + {"plusmn", "177"}, // plus-minus sign = plus-or-minus sign + {"sup2", "178"}, // superscript two = superscript digit two = squared + {"sup3", "179"}, // superscript three = superscript digit three = cubed + {"acute", "180"}, // acute accent = spacing acute + {"micro", "181"}, // micro sign + {"para", "182"}, // pilcrow sign = paragraph sign + {"middot", "183"}, // middle dot = Georgian comma = Greek middle dot + {"cedil", "184"}, // cedilla = spacing cedilla + {"sup1", "185"}, // superscript one = superscript digit one + {"ordm", "186"}, // masculine ordinal indicator + {"raquo", "187"}, // right-pointing double angle quotation mark = right pointing guillemet + {"frac14", "188"}, // vulgar fraction one quarter = fraction one quarter + {"frac12", "189"}, // vulgar fraction one half = fraction one half + {"frac34", "190"}, // vulgar fraction three quarters = fraction three quarters + {"iquest", "191"}, // inverted question mark = turned question mark {"Agrave", "192"}, // � - uppercase A, grave accent {"Aacute", "193"}, // � - uppercase A, acute accent {"Acirc", "194"}, // � - uppercase A, circumflex accent @@ -141,7 +107,7 @@ {"Ocirc", "212"}, // � - uppercase O, circumflex accent {"Otilde", "213"}, // � - uppercase O, tilde {"Ouml", "214"}, // � - uppercase O, umlaut - {"times", "215"}, //multiplication sign + {"times", "215"}, // multiplication sign {"Oslash", "216"}, // � - uppercase O, slash {"Ugrave", "217"}, // � - uppercase U, grave accent {"Uacute", "218"}, // � - uppercase U, acute accent @@ -187,310 +153,422 @@ // http://www.w3.org/TR/REC-html40/sgml/entities.html // package scoped for testing static final String[][] HTML40_ARRAY = { -// - {"fnof", "402"}, //latin small f with hook = function= florin, U+0192 ISOtech --> -// - {"Alpha", "913"}, //greek capital letter alpha, U+0391 --> - {"Beta", "914"}, //greek capital letter beta, U+0392 --> - {"Gamma", "915"}, //greek capital letter gamma,U+0393 ISOgrk3 --> - {"Delta", "916"}, //greek capital letter delta,U+0394 ISOgrk3 --> - {"Epsilon", "917"}, //greek capital letter epsilon, U+0395 --> - {"Zeta", "918"}, //greek capital letter zeta, U+0396 --> - {"Eta", "919"}, //greek capital letter eta, U+0397 --> - {"Theta", "920"}, //greek capital letter theta,U+0398 ISOgrk3 --> - {"Iota", "921"}, //greek capital letter iota, U+0399 --> - {"Kappa", "922"}, //greek capital letter kappa, U+039A --> - {"Lambda", "923"}, //greek capital letter lambda,U+039B ISOgrk3 --> - {"Mu", "924"}, //greek capital letter mu, U+039C --> - {"Nu", "925"}, //greek capital letter nu, U+039D --> - {"Xi", "926"}, //greek capital letter xi, U+039E ISOgrk3 --> - {"Omicron", "927"}, //greek capital letter omicron, U+039F --> - {"Pi", "928"}, //greek capital letter pi, U+03A0 ISOgrk3 --> - {"Rho", "929"}, //greek capital letter rho, U+03A1 --> -// - {"Sigma", "931"}, //greek capital letter sigma,U+03A3 ISOgrk3 --> - {"Tau", "932"}, //greek capital letter tau, U+03A4 --> - {"Upsilon", "933"}, //greek capital letter upsilon,U+03A5 ISOgrk3 --> - {"Phi", "934"}, //greek capital letter phi,U+03A6 ISOgrk3 --> - {"Chi", "935"}, //greek capital letter chi, U+03A7 --> - {"Psi", "936"}, //greek capital letter psi,U+03A8 ISOgrk3 --> - {"Omega", "937"}, //greek capital letter omega,U+03A9 ISOgrk3 --> - {"alpha", "945"}, //greek small letter alpha,U+03B1 ISOgrk3 --> - {"beta", "946"}, //greek small letter beta, U+03B2 ISOgrk3 --> - {"gamma", "947"}, //greek small letter gamma,U+03B3 ISOgrk3 --> - {"delta", "948"}, //greek small letter delta,U+03B4 ISOgrk3 --> - {"epsilon", "949"}, //greek small letter epsilon,U+03B5 ISOgrk3 --> - {"zeta", "950"}, //greek small letter zeta, U+03B6 ISOgrk3 --> - {"eta", "951"}, //greek small letter eta, U+03B7 ISOgrk3 --> - {"theta", "952"}, //greek small letter theta,U+03B8 ISOgrk3 --> - {"iota", "953"}, //greek small letter iota, U+03B9 ISOgrk3 --> - {"kappa", "954"}, //greek small letter kappa,U+03BA ISOgrk3 --> - {"lambda", "955"}, //greek small letter lambda,U+03BB ISOgrk3 --> - {"mu", "956"}, //greek small letter mu, U+03BC ISOgrk3 --> - {"nu", "957"}, //greek small letter nu, U+03BD ISOgrk3 --> - {"xi", "958"}, //greek small letter xi, U+03BE ISOgrk3 --> - {"omicron", "959"}, //greek small letter omicron, U+03BF NEW --> - {"pi", "960"}, //greek small letter pi, U+03C0 ISOgrk3 --> - {"rho", "961"}, //greek small letter rho, U+03C1 ISOgrk3 --> - {"sigmaf", "962"}, //greek small letter final sigma,U+03C2 ISOgrk3 --> - {"sigma", "963"}, //greek small letter sigma,U+03C3 ISOgrk3 --> - {"tau", "964"}, //greek small letter tau, U+03C4 ISOgrk3 --> - {"upsilon", "965"}, //greek small letter upsilon,U+03C5 ISOgrk3 --> - {"phi", "966"}, //greek small letter phi, U+03C6 ISOgrk3 --> - {"chi", "967"}, //greek small letter chi, U+03C7 ISOgrk3 --> - {"psi", "968"}, //greek small letter psi, U+03C8 ISOgrk3 --> - {"omega", "969"}, //greek small letter omega,U+03C9 ISOgrk3 --> - {"thetasym", "977"}, //greek small letter theta symbol,U+03D1 NEW --> - {"upsih", "978"}, //greek upsilon with hook symbol,U+03D2 NEW --> - {"piv", "982"}, //greek pi symbol, U+03D6 ISOgrk3 --> -// - {"bull", "8226"}, //bullet = black small circle,U+2022 ISOpub --> -// - {"hellip", "8230"}, //horizontal ellipsis = three dot leader,U+2026 ISOpub --> - {"prime", "8242"}, //prime = minutes = feet, U+2032 ISOtech --> - {"Prime", "8243"}, //double prime = seconds = inches,U+2033 ISOtech --> - {"oline", "8254"}, //overline = spacing overscore,U+203E NEW --> - {"frasl", "8260"}, //fraction slash, U+2044 NEW --> -// - {"weierp", "8472"}, //script capital P = power set= Weierstrass p, U+2118 ISOamso --> - {"image", "8465"}, //blackletter capital I = imaginary part,U+2111 ISOamso --> - {"real", "8476"}, //blackletter capital R = real part symbol,U+211C ISOamso --> - {"trade", "8482"}, //trade mark sign, U+2122 ISOnum --> - {"alefsym", "8501"}, //alef symbol = first transfinite cardinal,U+2135 NEW --> -// -// - {"larr", "8592"}, //leftwards arrow, U+2190 ISOnum --> - {"uarr", "8593"}, //upwards arrow, U+2191 ISOnum--> - {"rarr", "8594"}, //rightwards arrow, U+2192 ISOnum --> - {"darr", "8595"}, //downwards arrow, U+2193 ISOnum --> - {"harr", "8596"}, //left right arrow, U+2194 ISOamsa --> - {"crarr", "8629"}, //downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> - {"lArr", "8656"}, //leftwards double arrow, U+21D0 ISOtech --> -// - {"uArr", "8657"}, //upwards double arrow, U+21D1 ISOamsa --> - {"rArr", "8658"}, //rightwards double arrow,U+21D2 ISOtech --> -// - {"dArr", "8659"}, //downwards double arrow, U+21D3 ISOamsa --> - {"hArr", "8660"}, //left right double arrow,U+21D4 ISOamsa --> -// - {"forall", "8704"}, //for all, U+2200 ISOtech --> - {"part", "8706"}, //partial differential, U+2202 ISOtech --> - {"exist", "8707"}, //there exists, U+2203 ISOtech --> - {"empty", "8709"}, //empty set = null set = diameter,U+2205 ISOamso --> - {"nabla", "8711"}, //nabla = backward difference,U+2207 ISOtech --> - {"isin", "8712"}, //element of, U+2208 ISOtech --> - {"notin", "8713"}, //not an element of, U+2209 ISOtech --> - {"ni", "8715"}, //contains as member, U+220B ISOtech --> -// - {"prod", "8719"}, //n-ary product = product sign,U+220F ISOamsb --> -// - {"sum", "8721"}, //n-ary sumation, U+2211 ISOamsb --> -// - {"minus", "8722"}, //minus sign, U+2212 ISOtech --> - {"lowast", "8727"}, //asterisk operator, U+2217 ISOtech --> - {"radic", "8730"}, //square root = radical sign,U+221A ISOtech --> - {"prop", "8733"}, //proportional to, U+221D ISOtech --> - {"infin", "8734"}, //infinity, U+221E ISOtech --> - {"ang", "8736"}, //angle, U+2220 ISOamso --> - {"and", "8743"}, //logical and = wedge, U+2227 ISOtech --> - {"or", "8744"}, //logical or = vee, U+2228 ISOtech --> - {"cap", "8745"}, //intersection = cap, U+2229 ISOtech --> - {"cup", "8746"}, //union = cup, U+222A ISOtech --> - {"int", "8747"}, //integral, U+222B ISOtech --> - {"there4", "8756"}, //therefore, U+2234 ISOtech --> - {"sim", "8764"}, //tilde operator = varies with = similar to,U+223C ISOtech --> -// - {"cong", "8773"}, //approximately equal to, U+2245 ISOtech --> - {"asymp", "8776"}, //almost equal to = asymptotic to,U+2248 ISOamsr --> - {"ne", "8800"}, //not equal to, U+2260 ISOtech --> - {"equiv", "8801"}, //identical to, U+2261 ISOtech --> - {"le", "8804"}, //less-than or equal to, U+2264 ISOtech --> - {"ge", "8805"}, //greater-than or equal to,U+2265 ISOtech --> - {"sub", "8834"}, //subset of, U+2282 ISOtech --> - {"sup", "8835"}, //superset of, U+2283 ISOtech --> -// - {"sube", "8838"}, //subset of or equal to, U+2286 ISOtech --> - {"supe", "8839"}, //superset of or equal to,U+2287 ISOtech --> - {"oplus", "8853"}, //circled plus = direct sum,U+2295 ISOamsb --> - {"otimes", "8855"}, //circled times = vector product,U+2297 ISOamsb --> - {"perp", "8869"}, //up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> - {"sdot", "8901"}, //dot operator, U+22C5 ISOamsb --> -// -// - {"lceil", "8968"}, //left ceiling = apl upstile,U+2308 ISOamsc --> - {"rceil", "8969"}, //right ceiling, U+2309 ISOamsc --> - {"lfloor", "8970"}, //left floor = apl downstile,U+230A ISOamsc --> - {"rfloor", "8971"}, //right floor, U+230B ISOamsc --> - {"lang", "9001"}, //left-pointing angle bracket = bra,U+2329 ISOtech --> -// - {"rang", "9002"}, //right-pointing angle bracket = ket,U+232A ISOtech --> -// -// - {"loz", "9674"}, //lozenge, U+25CA ISOpub --> -// - {"spades", "9824"}, //black spade suit, U+2660 ISOpub --> -// - {"clubs", "9827"}, //black club suit = shamrock,U+2663 ISOpub --> - {"hearts", "9829"}, //black heart suit = valentine,U+2665 ISOpub --> - {"diams", "9830"}, //black diamond suit, U+2666 ISOpub --> + // + {"fnof", "402"}, // latin small f with hook = function= florin, U+0192 ISOtech --> + // + {"Alpha", "913"}, // greek capital letter alpha, U+0391 --> + {"Beta", "914"}, // greek capital letter beta, U+0392 --> + {"Gamma", "915"}, // greek capital letter gamma,U+0393 ISOgrk3 --> + {"Delta", "916"}, // greek capital letter delta,U+0394 ISOgrk3 --> + {"Epsilon", "917"}, // greek capital letter epsilon, U+0395 --> + {"Zeta", "918"}, // greek capital letter zeta, U+0396 --> + {"Eta", "919"}, // greek capital letter eta, U+0397 --> + {"Theta", "920"}, // greek capital letter theta,U+0398 ISOgrk3 --> + {"Iota", "921"}, // greek capital letter iota, U+0399 --> + {"Kappa", "922"}, // greek capital letter kappa, U+039A --> + {"Lambda", "923"}, // greek capital letter lambda,U+039B ISOgrk3 --> + {"Mu", "924"}, // greek capital letter mu, U+039C --> + {"Nu", "925"}, // greek capital letter nu, U+039D --> + {"Xi", "926"}, // greek capital letter xi, U+039E ISOgrk3 --> + {"Omicron", "927"}, // greek capital letter omicron, U+039F --> + {"Pi", "928"}, // greek capital letter pi, U+03A0 ISOgrk3 --> + {"Rho", "929"}, // greek capital letter rho, U+03A1 --> + // + {"Sigma", "931"}, // greek capital letter sigma,U+03A3 ISOgrk3 --> + {"Tau", "932"}, // greek capital letter tau, U+03A4 --> + {"Upsilon", "933"}, // greek capital letter upsilon,U+03A5 ISOgrk3 --> + {"Phi", "934"}, // greek capital letter phi,U+03A6 ISOgrk3 --> + {"Chi", "935"}, // greek capital letter chi, U+03A7 --> + {"Psi", "936"}, // greek capital letter psi,U+03A8 ISOgrk3 --> + {"Omega", "937"}, // greek capital letter omega,U+03A9 ISOgrk3 --> + {"alpha", "945"}, // greek small letter alpha,U+03B1 ISOgrk3 --> + {"beta", "946"}, // greek small letter beta, U+03B2 ISOgrk3 --> + {"gamma", "947"}, // greek small letter gamma,U+03B3 ISOgrk3 --> + {"delta", "948"}, // greek small letter delta,U+03B4 ISOgrk3 --> + {"epsilon", "949"}, // greek small letter epsilon,U+03B5 ISOgrk3 --> + {"zeta", "950"}, // greek small letter zeta, U+03B6 ISOgrk3 --> + {"eta", "951"}, // greek small letter eta, U+03B7 ISOgrk3 --> + {"theta", "952"}, // greek small letter theta,U+03B8 ISOgrk3 --> + {"iota", "953"}, // greek small letter iota, U+03B9 ISOgrk3 --> + {"kappa", "954"}, // greek small letter kappa,U+03BA ISOgrk3 --> + {"lambda", "955"}, // greek small letter lambda,U+03BB ISOgrk3 --> + {"mu", "956"}, // greek small letter mu, U+03BC ISOgrk3 --> + {"nu", "957"}, // greek small letter nu, U+03BD ISOgrk3 --> + {"xi", "958"}, // greek small letter xi, U+03BE ISOgrk3 --> + {"omicron", "959"}, // greek small letter omicron, U+03BF NEW --> + {"pi", "960"}, // greek small letter pi, U+03C0 ISOgrk3 --> + {"rho", "961"}, // greek small letter rho, U+03C1 ISOgrk3 --> + {"sigmaf", "962"}, // greek small letter final sigma,U+03C2 ISOgrk3 --> + {"sigma", "963"}, // greek small letter sigma,U+03C3 ISOgrk3 --> + {"tau", "964"}, // greek small letter tau, U+03C4 ISOgrk3 --> + {"upsilon", "965"}, // greek small letter upsilon,U+03C5 ISOgrk3 --> + {"phi", "966"}, // greek small letter phi, U+03C6 ISOgrk3 --> + {"chi", "967"}, // greek small letter chi, U+03C7 ISOgrk3 --> + {"psi", "968"}, // greek small letter psi, U+03C8 ISOgrk3 --> + {"omega", "969"}, // greek small letter omega,U+03C9 ISOgrk3 --> + {"thetasym", "977"}, // greek small letter theta symbol,U+03D1 NEW --> + {"upsih", "978"}, // greek upsilon with hook symbol,U+03D2 NEW --> + {"piv", "982"}, // greek pi symbol, U+03D6 ISOgrk3 --> + // + {"bull", "8226"}, // bullet = black small circle,U+2022 ISOpub --> + // + {"hellip", "8230"}, // horizontal ellipsis = three dot leader,U+2026 ISOpub --> + {"prime", "8242"}, // prime = minutes = feet, U+2032 ISOtech --> + {"Prime", "8243"}, // double prime = seconds = inches,U+2033 ISOtech --> + {"oline", "8254"}, // overline = spacing overscore,U+203E NEW --> + {"frasl", "8260"}, // fraction slash, U+2044 NEW --> + // + {"weierp", "8472"}, // script capital P = power set= Weierstrass p, U+2118 ISOamso --> + {"image", "8465"}, // blackletter capital I = imaginary part,U+2111 ISOamso --> + {"real", "8476"}, // blackletter capital R = real part symbol,U+211C ISOamso --> + {"trade", "8482"}, // trade mark sign, U+2122 ISOnum --> + {"alefsym", "8501"}, // alef symbol = first transfinite cardinal,U+2135 NEW --> + // + // + {"larr", "8592"}, // leftwards arrow, U+2190 ISOnum --> + {"uarr", "8593"}, // upwards arrow, U+2191 ISOnum--> + {"rarr", "8594"}, // rightwards arrow, U+2192 ISOnum --> + {"darr", "8595"}, // downwards arrow, U+2193 ISOnum --> + {"harr", "8596"}, // left right arrow, U+2194 ISOamsa --> + {"crarr", "8629"}, // downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> + {"lArr", "8656"}, // leftwards double arrow, U+21D0 ISOtech --> + // + {"uArr", "8657"}, // upwards double arrow, U+21D1 ISOamsa --> + {"rArr", "8658"}, // rightwards double arrow,U+21D2 ISOtech --> + // + {"dArr", "8659"}, // downwards double arrow, U+21D3 ISOamsa --> + {"hArr", "8660"}, // left right double arrow,U+21D4 ISOamsa --> + // + {"forall", "8704"}, // for all, U+2200 ISOtech --> + {"part", "8706"}, // partial differential, U+2202 ISOtech --> + {"exist", "8707"}, // there exists, U+2203 ISOtech --> + {"empty", "8709"}, // empty set = null set = diameter,U+2205 ISOamso --> + {"nabla", "8711"}, // nabla = backward difference,U+2207 ISOtech --> + {"isin", "8712"}, // element of, U+2208 ISOtech --> + {"notin", "8713"}, // not an element of, U+2209 ISOtech --> + {"ni", "8715"}, // contains as member, U+220B ISOtech --> + // + {"prod", "8719"}, // n-ary product = product sign,U+220F ISOamsb --> + // + {"sum", "8721"}, // n-ary summation, U+2211 ISOamsb --> + // + {"minus", "8722"}, // minus sign, U+2212 ISOtech --> + {"lowast", "8727"}, // asterisk operator, U+2217 ISOtech --> + {"radic", "8730"}, // square root = radical sign,U+221A ISOtech --> + {"prop", "8733"}, // proportional to, U+221D ISOtech --> + {"infin", "8734"}, // infinity, U+221E ISOtech --> + {"ang", "8736"}, // angle, U+2220 ISOamso --> + {"and", "8743"}, // logical and = wedge, U+2227 ISOtech --> + {"or", "8744"}, // logical or = vee, U+2228 ISOtech --> + {"cap", "8745"}, // intersection = cap, U+2229 ISOtech --> + {"cup", "8746"}, // union = cup, U+222A ISOtech --> + {"int", "8747"}, // integral, U+222B ISOtech --> + {"there4", "8756"}, // therefore, U+2234 ISOtech --> + {"sim", "8764"}, // tilde operator = varies with = similar to,U+223C ISOtech --> + // + {"cong", "8773"}, // approximately equal to, U+2245 ISOtech --> + {"asymp", "8776"}, // almost equal to = asymptotic to,U+2248 ISOamsr --> + {"ne", "8800"}, // not equal to, U+2260 ISOtech --> + {"equiv", "8801"}, // identical to, U+2261 ISOtech --> + {"le", "8804"}, // less-than or equal to, U+2264 ISOtech --> + {"ge", "8805"}, // greater-than or equal to,U+2265 ISOtech --> + {"sub", "8834"}, // subset of, U+2282 ISOtech --> + {"sup", "8835"}, // superset of, U+2283 ISOtech --> + // + {"sube", "8838"}, // subset of or equal to, U+2286 ISOtech --> + {"supe", "8839"}, // superset of or equal to,U+2287 ISOtech --> + {"oplus", "8853"}, // circled plus = direct sum,U+2295 ISOamsb --> + {"otimes", "8855"}, // circled times = vector product,U+2297 ISOamsb --> + {"perp", "8869"}, // up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> + {"sdot", "8901"}, // dot operator, U+22C5 ISOamsb --> + // + // + {"lceil", "8968"}, // left ceiling = apl upstile,U+2308 ISOamsc --> + {"rceil", "8969"}, // right ceiling, U+2309 ISOamsc --> + {"lfloor", "8970"}, // left floor = apl downstile,U+230A ISOamsc --> + {"rfloor", "8971"}, // right floor, U+230B ISOamsc --> + {"lang", "9001"}, // left-pointing angle bracket = bra,U+2329 ISOtech --> + // + {"rang", "9002"}, // right-pointing angle bracket = ket,U+232A ISOtech --> + // + // + {"loz", "9674"}, // lozenge, U+25CA ISOpub --> + // + {"spades", "9824"}, // black spade suit, U+2660 ISOpub --> + // + {"clubs", "9827"}, // black club suit = shamrock,U+2663 ISOpub --> + {"hearts", "9829"}, // black heart suit = valentine,U+2665 ISOpub --> + {"diams", "9830"}, // black diamond suit, U+2666 ISOpub --> -// - {"OElig", "338"}, // -- latin capital ligature OE,U+0152 ISOlat2 --> - {"oelig", "339"}, // -- latin small ligature oe, U+0153 ISOlat2 --> -// - {"Scaron", "352"}, // -- latin capital letter S with caron,U+0160 ISOlat2 --> - {"scaron", "353"}, // -- latin small letter s with caron,U+0161 ISOlat2 --> - {"Yuml", "376"}, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> -// - {"circ", "710"}, // -- modifier letter circumflex accent,U+02C6 ISOpub --> - {"tilde", "732"}, //small tilde, U+02DC ISOdia --> -// - {"ensp", "8194"}, //en space, U+2002 ISOpub --> - {"emsp", "8195"}, //em space, U+2003 ISOpub --> - {"thinsp", "8201"}, //thin space, U+2009 ISOpub --> - {"zwnj", "8204"}, //zero width non-joiner,U+200C NEW RFC 2070 --> - {"zwj", "8205"}, //zero width joiner, U+200D NEW RFC 2070 --> - {"lrm", "8206"}, //left-to-right mark, U+200E NEW RFC 2070 --> - {"rlm", "8207"}, //right-to-left mark, U+200F NEW RFC 2070 --> - {"ndash", "8211"}, //en dash, U+2013 ISOpub --> - {"mdash", "8212"}, //em dash, U+2014 ISOpub --> - {"lsquo", "8216"}, //left single quotation mark,U+2018 ISOnum --> - {"rsquo", "8217"}, //right single quotation mark,U+2019 ISOnum --> - {"sbquo", "8218"}, //single low-9 quotation mark, U+201A NEW --> - {"ldquo", "8220"}, //left double quotation mark,U+201C ISOnum --> - {"rdquo", "8221"}, //right double quotation mark,U+201D ISOnum --> - {"bdquo", "8222"}, //double low-9 quotation mark, U+201E NEW --> - {"dagger", "8224"}, //dagger, U+2020 ISOpub --> - {"Dagger", "8225"}, //double dagger, U+2021 ISOpub --> - {"permil", "8240"}, //per mille sign, U+2030 ISOtech --> - {"lsaquo", "8249"}, //single left-pointing angle quotation mark,U+2039 ISO proposed --> -// - {"rsaquo", "8250"}, //single right-pointing angle quotation mark,U+203A ISO proposed --> -// - {"euro", "8364"}, // -- euro sign, U+20AC NEW --> + // + {"OElig", "338"}, // -- latin capital ligature OE,U+0152 ISOlat2 --> + {"oelig", "339"}, // -- latin small ligature oe, U+0153 ISOlat2 --> + // + {"Scaron", "352"}, // -- latin capital letter S with caron,U+0160 ISOlat2 --> + {"scaron", "353"}, // -- latin small letter s with caron,U+0161 ISOlat2 --> + {"Yuml", "376"}, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> + // + {"circ", "710"}, // -- modifier letter circumflex accent,U+02C6 ISOpub --> + {"tilde", "732"}, // small tilde, U+02DC ISOdia --> + // + {"ensp", "8194"}, // en space, U+2002 ISOpub --> + {"emsp", "8195"}, // em space, U+2003 ISOpub --> + {"thinsp", "8201"}, // thin space, U+2009 ISOpub --> + {"zwnj", "8204"}, // zero width non-joiner,U+200C NEW RFC 2070 --> + {"zwj", "8205"}, // zero width joiner, U+200D NEW RFC 2070 --> + {"lrm", "8206"}, // left-to-right mark, U+200E NEW RFC 2070 --> + {"rlm", "8207"}, // right-to-left mark, U+200F NEW RFC 2070 --> + {"ndash", "8211"}, // en dash, U+2013 ISOpub --> + {"mdash", "8212"}, // em dash, U+2014 ISOpub --> + {"lsquo", "8216"}, // left single quotation mark,U+2018 ISOnum --> + {"rsquo", "8217"}, // right single quotation mark,U+2019 ISOnum --> + {"sbquo", "8218"}, // single low-9 quotation mark, U+201A NEW --> + {"ldquo", "8220"}, // left double quotation mark,U+201C ISOnum --> + {"rdquo", "8221"}, // right double quotation mark,U+201D ISOnum --> + {"bdquo", "8222"}, // double low-9 quotation mark, U+201E NEW --> + {"dagger", "8224"}, // dagger, U+2020 ISOpub --> + {"Dagger", "8225"}, // double dagger, U+2021 ISOpub --> + {"permil", "8240"}, // per mille sign, U+2030 ISOtech --> + {"lsaquo", "8249"}, // single left-pointing angle quotation mark,U+2039 ISO proposed --> + // + {"rsaquo", "8250"}, // single right-pointing angle quotation mark,U+203A ISO proposed --> + // + {"euro", "8364"}, // -- euro sign, U+20AC NEW --> }; /** - *

    The set of entities supported by standard XML.

    + *

    + * The set of entities supported by standard XML. + *

    */ public static final Entities XML; /** - *

    The set of entities supported by HTML 3.2.

    + *

    + * The set of entities supported by HTML 3.2. + *

    */ public static final Entities HTML32; /** - *

    The set of entities supported by HTML 4.0.

    + *

    + * The set of entities supported by HTML 4.0. + *

    */ public static final Entities HTML40; static { - XML = new Entities(); - XML.addEntities(BASIC_ARRAY); - XML.addEntities(APOS_ARRAY); + Entities xml = new Entities(); + xml.addEntities(BASIC_ARRAY); + xml.addEntities(APOS_ARRAY); + XML = xml; } static { - HTML32 = new Entities(); - HTML32.addEntities(BASIC_ARRAY); - HTML32.addEntities(ISO8859_1_ARRAY); + Entities html32 = new Entities(); + html32.addEntities(BASIC_ARRAY); + html32.addEntities(ISO8859_1_ARRAY); + HTML32 = html32; } static { - HTML40 = new Entities(); - fillWithHtml40Entities(HTML40); + Entities html40 = new Entities(); + fillWithHtml40Entities(html40); + HTML40 = html40; } + /** + *

    + * Fills the specified entities instance with HTML 40 entities. + *

    + * + * @param entities + * the instance to be filled. + */ static void fillWithHtml40Entities(Entities entities) { entities.addEntities(BASIC_ARRAY); entities.addEntities(ISO8859_1_ARRAY); entities.addEntities(HTML40_ARRAY); } static interface EntityMap { + /** + *

    + * Add an entry to this entity map. + *

    + * + * @param name + * the entity name + * @param value + * the entity value + */ void add(String name, int value); + /** + *

    + * Returns the name of the entity identified by the specified value. + *

    + * + * @param value + * the value to locate + * @return entity name associated with the specified value + */ String name(int value); + /** + *

    + * Returns the value of the entity identified by the specified name. + *

    + * + * @param name + * the name to locate + * @return entity value associated with the specified name + */ int value(String name); } static class PrimitiveEntityMap implements EntityMap { - private Map mapNameToValue = new HashMap(); - private IntHashMap mapValueToName = new IntHashMap(); + private final Map mapNameToValue = new HashMap(); + private final IntHashMap mapValueToName = new IntHashMap(); + + /** + * {@inheritDoc} + */ + // TODO not thread-safe as there is a window between changing the two maps public void add(String name, int value) { mapNameToValue.put(name, new Integer(value)); mapValueToName.put(value, name); } + /** + * {@inheritDoc} + */ public String name(int value) { return (String) mapValueToName.get(value); } + /** + * {@inheritDoc} + */ public int value(String name) { Object value = mapNameToValue.get(name); - if (value == null) + if (value == null) { return -1; + } return ((Integer) value).intValue(); } } - static abstract class MapIntMap implements Entities.EntityMap { - protected Map mapNameToValue; - protected Map mapValueToName; + protected final Map mapNameToValue; + protected final Map mapValueToName; + + /** + * Construct a new instance with specified maps. + * + * @param nameToValue name to value map + * @param valueToName value to namee map + */ + MapIntMap(Map nameToValue, Map valueToName){ + mapNameToValue = nameToValue; + mapValueToName = valueToName; + } + + /** + * {@inheritDoc} + */ public void add(String name, int value) { mapNameToValue.put(name, new Integer(value)); mapValueToName.put(new Integer(value), name); } + /** + * {@inheritDoc} + */ public String name(int value) { return (String) mapValueToName.get(new Integer(value)); } + /** + * {@inheritDoc} + */ public int value(String name) { Object value = mapNameToValue.get(name); - if (value == null) + if (value == null) { return -1; + } return ((Integer) value).intValue(); } } static class HashEntityMap extends MapIntMap { + /** + * Constructs a new instance of HashEntityMap. + */ public HashEntityMap() { - mapNameToValue = new HashMap(); - mapValueToName = new HashMap(); + super(new HashMap(), new HashMap()); } } static class TreeEntityMap extends MapIntMap { + /** + * Constructs a new instance of TreeEntityMap. + */ public TreeEntityMap() { - mapNameToValue = new TreeMap(); - mapValueToName = new TreeMap(); + super(new TreeMap(), new TreeMap()); } } static class LookupEntityMap extends PrimitiveEntityMap { + // TODO this class is not thread-safe private String[] lookupTable; - private int LOOKUP_TABLE_SIZE = 256; + private static final int LOOKUP_TABLE_SIZE = 256; + + /** + * {@inheritDoc} + */ public String name(int value) { if (value < LOOKUP_TABLE_SIZE) { return lookupTable()[value]; } return super.name(value); } + /** + *

    + * Returns the lookup table for this entity map. The lookup table is created if it has not been previously. + *

    + * + * @return the lookup table + */ private String[] lookupTable() { if (lookupTable == null) { createLookupTable(); } return lookupTable; } + /** + *

    + * Creates an entity lookup table of LOOKUP_TABLE_SIZE elements, initialized with entity names. + *

    + */ private void createLookupTable() { lookupTable = new String[LOOKUP_TABLE_SIZE]; for (int i = 0; i < LOOKUP_TABLE_SIZE; ++i) { @@ -500,29 +578,53 @@ } static class ArrayEntityMap implements EntityMap { - protected int growBy = 100; + // TODO this class is not thread-safe + protected final int growBy; + protected int size = 0; + protected String[] names; + protected int[] values; + /** + * Constructs a new instance of ArrayEntityMap. + */ public ArrayEntityMap() { + this.growBy = 100; names = new String[growBy]; values = new int[growBy]; } + /** + * Constructs a new instance of ArrayEntityMap specifying the size by which the array should + * grow. + * + * @param growBy + * array will be initialized to and will grow by this amount + */ public ArrayEntityMap(int growBy) { this.growBy = growBy; names = new String[growBy]; values = new int[growBy]; } + /** + * {@inheritDoc} + */ public void add(String name, int value) { ensureCapacity(size + 1); names[size] = name; values[size] = value; size++; } + /** + * Verifies the capacity of the entity array, adjusting the size if necessary. + * + * @param capacity + * size the array should be + */ protected void ensureCapacity(int capacity) { if (capacity > names.length) { int newSize = Math.max(capacity, size + growBy); @@ -535,6 +637,9 @@ } } + /** + * {@inheritDoc} + */ public String name(int value) { for (int i = 0; i < size; ++i) { if (values[i] == value) { @@ -544,6 +649,9 @@ return null; } + /** + * {@inheritDoc} + */ public int value(String name) { for (int i = 0; i < size; ++i) { if (names[i].equals(name)) { @@ -556,147 +664,360 @@ static class BinaryEntityMap extends ArrayEntityMap { + // TODO - not thread-safe, because parent is not. Also references size. + + /** + * Constructs a new instance of BinaryEntityMap. + */ public BinaryEntityMap() { + super(); } + /** + * Constructs a new instance of ArrayEntityMap specifying the size by which the underlying array + * should grow. + * + * @param growBy + * array will be initialized to and will grow by this amount + */ public BinaryEntityMap(int growBy) { super(growBy); } - // based on code in java.util.Arrays + /** + * Performs a binary search of the entity array for the specified key. This method is based on code in + * {@link java.util.Arrays}. + * + * @param key + * the key to be found + * @return the index of the entity array matching the specified key + */ private int binarySearch(int key) { int low = 0; int high = size - 1; while (low <= high) { - int mid = (low + high) >> 1; + int mid = (low + high) >>> 1; int midVal = values[mid]; - if (midVal < key) + if (midVal < key) { low = mid + 1; - else if (midVal > key) + } else if (midVal > key) { high = mid - 1; - else + } else { return mid; // key found + } } - return -(low + 1); // key not found. + return -(low + 1); // key not found. } + /** + * {@inheritDoc} + */ public void add(String name, int value) { ensureCapacity(size + 1); int insertAt = binarySearch(value); - if (insertAt > 0) return; // note: this means you can't insert the same value twice - insertAt = -(insertAt + 1); // binarySearch returns it negative and off-by-one + if (insertAt > 0) { + return; // note: this means you can't insert the same value twice + } + insertAt = -(insertAt + 1); // binarySearch returns it negative and off-by-one System.arraycopy(values, insertAt, values, insertAt + 1, size - insertAt); values[insertAt] = value; System.arraycopy(names, insertAt, names, insertAt + 1, size - insertAt); names[insertAt] = name; size++; } + /** + * {@inheritDoc} + */ public String name(int value) { int index = binarySearch(value); - if (index < 0) return null; + if (index < 0) { + return null; + } return names[index]; } } - // package scoped for testing - EntityMap map = new Entities.LookupEntityMap(); + private final EntityMap map; + /** + * Default constructor. + */ + public Entities(){ + map = new Entities.LookupEntityMap(); + } + + /** + * package scoped constructor for testing. + * + * @param emap entity map. + */ + Entities(EntityMap emap){ + map = emap; + } + + /** + *

    + * Adds entities to this entity. + *

    + * + * @param entityArray + * array of entities to be added + */ public void addEntities(String[][] entityArray) { for (int i = 0; i < entityArray.length; ++i) { addEntity(entityArray[i][0], Integer.parseInt(entityArray[i][1])); } } + /** + *

    + * Add an entity to this entity. + *

    + * + * @param name + * name of the entity + * @param value + * vale of the entity + */ public void addEntity(String name, int value) { map.add(name, value); } + /** + *

    + * Returns the name of the entity identified by the specified value. + *

    + * + * @param value + * the value to locate + * @return entity name associated with the specified value + */ public String entityName(int value) { return map.name(value); } - + /** + *

    + * Returns the value of the entity identified by the specified name. + *

    + * + * @param name + * the name to locate + * @return entity value associated with the specified name + */ public int entityValue(String name) { return map.value(name); } /** - *

    Escapes the characters in a String.

    - * - *

    For example, if you have called addEntity("foo", 0xA1), - * escape("\u00A1") will return "&foo;"

    - * - * @param str The String to escape. + *

    + * Escapes the characters in a String. + *

    + * + *

    + * For example, if you have called addEntity("foo", 0xA1), escape("\u00A1") will return + * "&foo;" + *

    + * + * @param str + * The String to escape. * @return A new escaped String. */ public String escape(String str) { - //todo: rewrite to use a Writer - StringBuffer buf = new StringBuffer(str.length() * 2); - int i; - for (i = 0; i < str.length(); ++i) { - char ch = str.charAt(i); - String entityName = this.entityName(ch); + StringWriter stringWriter = createStringWriter(str); + try { + this.escape(stringWriter, str); + } catch (IOException e) { + // This should never happen because ALL the StringWriter methods called by #escape(Writer, String) do not + // throw IOExceptions. + throw new UnhandledException(e); + } + return stringWriter.toString(); + } + + /** + *

    + * Escapes the characters in the String passed and writes the result to the Writer + * passed. + *

    + * + * @param writer + * The Writer to write the results of the escaping to. Assumed to be a non-null value. + * @param str + * The String to escape. Assumed to be a non-null value. + * @throws IOException + * when Writer passed throws the exception from calls to the {@link Writer#write(int)} + * methods. + * + * @see #escape(String) + * @see Writer + */ + public void escape(Writer writer, String str) throws IOException { + int len = str.length(); + for (int i = 0; i < len; i++) { + char c = str.charAt(i); + String entityName = this.entityName(c); if (entityName == null) { - if (ch > 0x7F) { - int intValue = ch; - buf.append("&#"); - buf.append(intValue); - buf.append(';'); + if (c > 0x7F) { + writer.write("&#"); + writer.write(Integer.toString(c, 10)); + writer.write(';'); } else { - buf.append(ch); + writer.write(c); } } else { - buf.append('&'); - buf.append(entityName); - buf.append(';'); + writer.write('&'); + writer.write(entityName); + writer.write(';'); } } - return buf.toString(); } /** - *

    Unescapes the entities in a String.

    - * - *

    For example, if you have called addEntity("foo", 0xA1), - * unescape("&foo;") will return "\u00A1"

    - * - * @param str The String to escape. + *

    + * Unescapes the entities in a String. + *

    + * + *

    + * For example, if you have called addEntity("foo", 0xA1), unescape("&foo;") will return + * "\u00A1" + *

    + * + * @param str + * The String to escape. * @return A new escaped String. */ public String unescape(String str) { - StringBuffer buf = new StringBuffer(str.length()); - int i; - for (i = 0; i < str.length(); ++i) { - char ch = str.charAt(i); - if (ch == '&') { - int semi = str.indexOf(';', i + 1); - if (semi == -1) { - buf.append(ch); + int firstAmp = str.indexOf('&'); + if (firstAmp < 0) { + return str; + } else { + StringWriter stringWriter = createStringWriter(str); + try { + this.doUnescape(stringWriter, str, firstAmp); + } catch (IOException e) { + // This should never happen because ALL the StringWriter methods called by #escape(Writer, String) + // do not throw IOExceptions. + throw new UnhandledException(e); + } + return stringWriter.toString(); + } + } + + /** + * Make the StringWriter 10% larger than the source String to avoid growing the writer + * + * @param str The source string + * @return A newly created StringWriter + */ + private StringWriter createStringWriter(String str) { + return new StringWriter((int) (str.length() + (str.length() * 0.1))); + } + + /** + *

    + * Unescapes the escaped entities in the String passed and writes the result to the + * Writer passed. + *

    + * + * @param writer + * The Writer to write the results to; assumed to be non-null. + * @param str + * The source String to unescape; assumed to be non-null. + * @throws IOException + * when Writer passed throws the exception from calls to the {@link Writer#write(int)} + * methods. + * + * @see #escape(String) + * @see Writer + */ + public void unescape(Writer writer, String str) throws IOException { + int firstAmp = str.indexOf('&'); + if (firstAmp < 0) { + writer.write(str); + return; + } else { + doUnescape(writer, str, firstAmp); + } + } + + /** + * Underlying unescape method that allows the optimisation of not starting from the 0 index again. + * + * @param writer + * The Writer to write the results to; assumed to be non-null. + * @param str + * The source String to unescape; assumed to be non-null. + * @param firstAmp + * The int index of the first ampersand in the source String. + * @throws IOException + * when Writer passed throws the exception from calls to the {@link Writer#write(int)} + * methods. + */ + private void doUnescape(Writer writer, String str, int firstAmp) throws IOException { + writer.write(str, 0, firstAmp); + int len = str.length(); + for (int i = firstAmp; i < len; i++) { + char c = str.charAt(i); + if (c == '&') { + int nextIdx = i + 1; + int semiColonIdx = str.indexOf(';', nextIdx); + if (semiColonIdx == -1) { + writer.write(c); continue; } - String entityName = str.substring(i + 1, semi); - int entityValue; - if (entityName.charAt(0) == '#') { - entityValue = Integer.parseInt(entityName.substring(1)); - } else { - entityValue = this.entityValue(entityName); + int amphersandIdx = str.indexOf('&', i + 1); + if (amphersandIdx != -1 && amphersandIdx < semiColonIdx) { + // Then the text looks like &...&...; + writer.write(c); + continue; } + String entityContent = str.substring(nextIdx, semiColonIdx); + int entityValue = -1; + int entityContentLen = entityContent.length(); + if (entityContentLen > 0) { + if (entityContent.charAt(0) == '#') { // escaped value content is an integer (decimal or + // hexidecimal) + if (entityContentLen > 1) { + char isHexChar = entityContent.charAt(1); + try { + switch (isHexChar) { + case 'X' : + case 'x' : { + entityValue = Integer.parseInt(entityContent.substring(2), 16); + break; + } + default : { + entityValue = Integer.parseInt(entityContent.substring(1), 10); + } + } + if (entityValue > 0xFFFF) { + entityValue = -1; + } + } catch (NumberFormatException e) { + entityValue = -1; + } + } + } else { // escaped value content is an entity name + entityValue = this.entityValue(entityContent); + } + } + if (entityValue == -1) { - buf.append('&'); - buf.append(entityName); - buf.append(';'); + writer.write('&'); + writer.write(entityContent); + writer.write(';'); } else { - buf.append((char) (entityValue)); + writer.write(entityValue); } - i = semi; + i = semiColonIdx; // move index up to the semi-colon } else { - buf.append(ch); + writer.write(c); } } - return buf.toString(); } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/IllegalClassException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/IllegalClassException.java (.../IllegalClassException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/IllegalClassException.java (.../IllegalClassException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,61 +1,42 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; /** - *

    Thrown when an object is an instance of an unexpected type (a class or interface).

    + *

    Thrown when an object is an instance of an unexpected type (a class or interface). + * This exception supplements the standard IllegalArgumentException + * by providing a more semantically rich description of the problem.

    * + *

    IllegalClassException represents the case where a method takes + * in a genericly typed parameter like Object (typically because it has to due to some + * other interface it implements), but this implementation only actually accepts a specific + * type, for example String. This exception would be used in place of + * IllegalArgumentException, yet it still extends it.

    + * + *
    + * public void foo(Object obj) {
    + *   if (obj instanceof String == false) {
    + *     throw new IllegalClassException(String.class, obj);
    + *   }
    + *   // do something with the string
    + * }
    + * 
    + * + * @author Apache Software Foundation * @author Matthew Hawthorne * @author Gary Gregory * @since 2.0 @@ -64,9 +45,31 @@ public class IllegalClassException extends IllegalArgumentException { /** - *

    Instantiates with the specified types (classes or interfaces).

    + * Required for serialization support. * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 8063272569377254819L; + + /** + *

    Instantiates with the expected type, and actual object.

    + * * @param expected the expected type + * @param actual the actual object + * @since 2.1 + */ + public IllegalClassException(Class expected, Object actual) { + super( + "Expected: " + + safeGetClassName(expected) + + ", actual: " + + (actual == null ? "null" : actual.getClass().getName())); + } + + /** + *

    Instantiates with the expected and actual types.

    + * + * @param expected the expected type * @param actual the actual type */ public IllegalClassException(Class expected, Class actual) { Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/IncompleteArgumentException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/IncompleteArgumentException.java (.../IncompleteArgumentException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/IncompleteArgumentException.java (.../IncompleteArgumentException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,70 +1,60 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.util.Arrays; /** - *

    Thrown to indicate an incomplete argument to a method.

    + *

    Thrown to indicate an incomplete argument to a method. + * This exception supplements the standard IllegalArgumentException + * by providing a more semantically rich description of the problem.

    * + *

    IncompleteArgumentException represents the case where a method takes + * in a parameter that has a number of properties, some of which have not been set. + * A case might be a search requirements bean that must have three properties set + * in order for the method to run, but only one is actually set. + * This exception would be used in place of + * IllegalArgumentException, yet it still extends it.

    + * + *
    + * public void foo(PersonSearcher search) {
    + *   if (search.getSurname() == null ||
    + *       search.getForename() == null ||
    + *       search.getSex() == null) {
    + *     throw new IncompleteArgumentException("search");
    + *   }
    + *   // do something with the searcher
    + * }
    + * 
    + * * @author Matthew Hawthorne * @since 2.0 * @version $Id$ */ public class IncompleteArgumentException extends IllegalArgumentException { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 4954193403612068178L; + + /** *

    Instantiates with the specified description.

    * * @param argName a description of the incomplete argument @@ -87,7 +77,7 @@ } /** - *

    7Converts an array to a string without throwing an exception.

    + *

    Converts an array to a string without throwing an exception.

    * * @param array an array * @return the array as a string Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/IntHashMap.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/IntHashMap.java (.../IntHashMap.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/IntHashMap.java (.../IntHashMap.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ /* @@ -62,12 +25,12 @@ *

    A hash map that uses primitive ints for the key rather than objects.

    * *

    Note that this class is for internal optimization purposes only, and may - * not be supported in future releases of Jakarta Commons Lang. Utilities of - * this sort may be included in future releases of Jakarta Commons Collections.

    + * not be supported in future releases of Apache Commons Lang. Utilities of + * this sort may be included in future releases of Apache Commons Collections.

    * + * @author Apache Software Foundation * @author Justin Couch * @author Alex Chaffee (alex@apache.org) - * @author Stephen Colebourne * @since 2.0 * @version $Revision$ * @see java.util.HashMap @@ -97,15 +60,15 @@ * * @serial */ - private float loadFactor; + private final float loadFactor; /** *

    Innerclass that acts as a datastructure to create a new entry in the * table.

    */ private static class Entry { - int hash; - int key; + final int hash; + final int key; // TODO not read; seems to be always same as hash Object value; Entry next; @@ -232,6 +195,7 @@ * (which predates the Map interface).

    * * @param value value whose presence in this HashMap is to be tested. + * @return boolean true if the value is contained * @see java.util.Map * @since JDK1.2 */ Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/LocaleUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/LocaleUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/LocaleUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,316 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + *

    Operations to assist when working with a {@link Locale}.

    + * + *

    This class tries to handle null input gracefully. + * An exception will not be thrown for a null input. + * Each method documents its behaviour in more detail.

    + * + * @author Apache Software Foundation + * @since 2.2 + * @version $Id$ + */ +public class LocaleUtils { + + /** Unmodifiable list of available locales. */ + private static List cAvailableLocaleList; // lazily created by availableLocaleList() + + /** Unmodifiable set of available locales. */ + private static Set cAvailableLocaleSet; // lazily created by availableLocaleSet() + + /** Unmodifiable map of language locales by country. */ + private static final Map cLanguagesByCountry = Collections.synchronizedMap(new HashMap()); + + /** Unmodifiable map of country locales by language. */ + private static final Map cCountriesByLanguage = Collections.synchronizedMap(new HashMap()); + + /** + *

    LocaleUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as LocaleUtils.toLocale("en_GB");.

    + * + *

    This constructor is public to permit tools that require a JavaBean instance + * to operate.

    + */ + public LocaleUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

    Converts a String to a Locale.

    + * + *

    This method takes the string format of a locale and creates the + * locale object from it.

    + * + *
    +     *   LocaleUtils.toLocale("en")         = new Locale("en", "")
    +     *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
    +     *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
    +     * 
    + * + *

    (#) The behaviour of the JDK variant constructor changed between JDK1.3 and JDK1.4. + * In JDK1.3, the constructor upper cases the variant, in JDK1.4, it doesn't. + * Thus, the result from getVariant() may vary depending on your JDK.

    + * + *

    This method validates the input strictly. + * The language code must be lowercase. + * The country code must be uppercase. + * The separator must be an underscore. + * The length must be correct. + *

    + * + * @param str the locale String to convert, null returns null + * @return a Locale, null if null input + * @throws IllegalArgumentException if the string is an invalid format + */ + public static Locale toLocale(String str) { + if (str == null) { + return null; + } + int len = str.length(); + if (len != 2 && len != 5 && len < 7) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + if (ch0 < 'a' || ch0 > 'z' || ch1 < 'a' || ch1 > 'z') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + if (len == 2) { + return new Locale(str, ""); + } else { + if (str.charAt(2) != '_') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + char ch3 = str.charAt(3); + if (ch3 == '_') { + return new Locale(str.substring(0, 2), "", str.substring(4)); + } + char ch4 = str.charAt(4); + if (ch3 < 'A' || ch3 > 'Z' || ch4 < 'A' || ch4 > 'Z') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + if (len == 5) { + return new Locale(str.substring(0, 2), str.substring(3, 5)); + } else { + if (str.charAt(5) != '_') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + return new Locale(str.substring(0, 2), str.substring(3, 5), str.substring(6)); + } + } + } + + //----------------------------------------------------------------------- + /** + *

    Obtains the list of locales to search through when performing + * a locale search.

    + * + *
    +     * localeLookupList(Locale("fr","CA","xxx"))
    +     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr")]
    +     * 
    + * + * @param locale the locale to start from + * @return the unmodifiable list of Locale objects, 0 being locale, never null + */ + public static List localeLookupList(Locale locale) { + return localeLookupList(locale, locale); + } + + //----------------------------------------------------------------------- + /** + *

    Obtains the list of locales to search through when performing + * a locale search.

    + * + *
    +     * localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
    +     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr"), Locale("en"]
    +     * 
    + * + *

    The result list begins with the most specific locale, then the + * next more general and so on, finishing with the default locale. + * The list will never contain the same locale twice.

    + * + * @param locale the locale to start from, null returns empty list + * @param defaultLocale the default locale to use if no other is found + * @return the unmodifiable list of Locale objects, 0 being locale, never null + */ + public static List localeLookupList(Locale locale, Locale defaultLocale) { + List list = new ArrayList(4); + if (locale != null) { + list.add(locale); + if (locale.getVariant().length() > 0) { + list.add(new Locale(locale.getLanguage(), locale.getCountry())); + } + if (locale.getCountry().length() > 0) { + list.add(new Locale(locale.getLanguage(), "")); + } + if (list.contains(defaultLocale) == false) { + list.add(defaultLocale); + } + } + return Collections.unmodifiableList(list); + } + + //----------------------------------------------------------------------- + /** + *

    Obtains an unmodifiable list of installed locales.

    + * + *

    This method is a wrapper around {@link Locale#getAvailableLocales()}. + * It is more efficient, as the JDK method must create a new array each + * time it is called.

    + * + * @return the unmodifiable list of available locales + */ + public static List availableLocaleList() { + if(cAvailableLocaleList == null) { + initAvailableLocaleList(); + } + return cAvailableLocaleList; + } + + /** + * Initializes the availableLocaleList. It is separate from availableLocaleList() + * to avoid the synchronized block affecting normal use, yet synchronized and + * lazy loading to avoid a static block affecting other methods in this class. + */ + private static synchronized void initAvailableLocaleList() { + if(cAvailableLocaleList == null) { + List list = Arrays.asList(Locale.getAvailableLocales()); + cAvailableLocaleList = Collections.unmodifiableList(list); + } + } + + //----------------------------------------------------------------------- + /** + *

    Obtains an unmodifiable set of installed locales.

    + * + *

    This method is a wrapper around {@link Locale#getAvailableLocales()}. + * It is more efficient, as the JDK method must create a new array each + * time it is called.

    + * + * @return the unmodifiable set of available locales + */ + public static Set availableLocaleSet() { + if(cAvailableLocaleSet == null) { + initAvailableLocaleSet(); + } + return cAvailableLocaleSet; + } + + /** + * Initializes the availableLocaleSet. It is separate from availableLocaleSet() + * to avoid the synchronized block affecting normal use, yet synchronized and + * lazy loading to avoid a static block affecting other methods in this class. + */ + private static synchronized void initAvailableLocaleSet() { + if(cAvailableLocaleSet == null) { + cAvailableLocaleSet = Collections.unmodifiableSet( new HashSet(availableLocaleList()) ); + } + } + + //----------------------------------------------------------------------- + /** + *

    Checks if the locale specified is in the list of available locales.

    + * + * @param locale the Locale object to check if it is available + * @return true if the locale is a known locale + */ + public static boolean isAvailableLocale(Locale locale) { + return availableLocaleList().contains(locale); + } + + //----------------------------------------------------------------------- + /** + *

    Obtains the list of languages supported for a given country.

    + * + *

    This method takes a country code and searches to find the + * languages available for that country. Variant locales are removed.

    + * + * @param countryCode the 2 letter country code, null returns empty + * @return an unmodifiable List of Locale objects, never null + */ + public static List languagesByCountry(String countryCode) { + List langs = (List) cLanguagesByCountry.get(countryCode); //syncd + if (langs == null) { + if (countryCode != null) { + langs = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = (Locale) locales.get(i); + if (countryCode.equals(locale.getCountry()) && + locale.getVariant().length() == 0) { + langs.add(locale); + } + } + langs = Collections.unmodifiableList(langs); + } else { + langs = Collections.EMPTY_LIST; + } + cLanguagesByCountry.put(countryCode, langs); //syncd + } + return langs; + } + + //----------------------------------------------------------------------- + /** + *

    Obtains the list of countries supported for a given language.

    + * + *

    This method takes a language code and searches to find the + * countries available for that language. Variant locales are removed.

    + * + * @param languageCode the 2 letter language code, null returns empty + * @return an unmodifiable List of Locale objects, never null + */ + public static List countriesByLanguage(String languageCode) { + List countries = (List) cCountriesByLanguage.get(languageCode); //syncd + if (countries == null) { + if (languageCode != null) { + countries = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = (Locale) locales.get(i); + if (languageCode.equals(locale.getLanguage()) && + locale.getCountry().length() != 0 && + locale.getVariant().length() == 0) { + countries.add(locale); + } + } + countries = Collections.unmodifiableList(countries); + } else { + countries = Collections.EMPTY_LIST; + } + cCountriesByLanguage.put(languageCode, countries); //syncd + } + return countries; + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/NotImplementedException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/NotImplementedException.java (.../NotImplementedException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/NotImplementedException.java (.../NotImplementedException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,85 +1,295 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import java.io.PrintStream; +import java.io.PrintWriter; + +import org.apache.commons.lang.exception.Nestable; +import org.apache.commons.lang.exception.NestableDelegate; + /** - *

    Thrown to indicate that a method has not been implemented.

    + *

    Thrown to indicate that a block of code has not been implemented. + * This exception supplements UnsupportedOperationException + * by providing a more semantically rich description of the problem.

    * + *

    NotImplementedException represents the case where the + * author has yet to implement the logic at this point in the program. + * This can act as an exception based TODO tag. + * Because this logic might be within a catch block, this exception + * suports exception chaining.

    + * + *
    + * public void foo() {
    + *   try {
    + *     // do something that throws an Exception
    + *   } catch (Exception ex) {
    + *     // don't know what to do here yet
    + *     throw new NotImplementedException("TODO", ex);
    + *   }
    + * }
    + * 
    + * + * @author Apache Software Foundation * @author Matthew Hawthorne * @since 2.0 * @version $Id$ */ -public class NotImplementedException extends UnsupportedOperationException { +public class NotImplementedException + extends UnsupportedOperationException implements Nestable { + private static final String DEFAULT_MESSAGE = "Code is not implemented"; + /** - *

    Constructes the exception with the specified class.

    + * Required for serialization support. * - * @param clazz the Class that has not implemented the method + * @see java.io.Serializable */ + private static final long serialVersionUID = -6894122266938754088L; + + /** + * The exception helper to delegate nested exception handling to. + */ + private NestableDelegate delegate = new NestableDelegate(this); + + /** + * Holds the reference to the exception or error that caused + * this exception to be thrown. + */ + private Throwable cause; + + //----------------------------------------------------------------------- + /** + * Constructs a new NotImplementedException with default message. + * + * @since 2.1 + */ + public NotImplementedException() { + super(DEFAULT_MESSAGE); + } + + /** + * Constructs a new NotImplementedException with specified + * detail message. + * + * @param msg the error message. + */ + public NotImplementedException(String msg) { + super(msg == null ? DEFAULT_MESSAGE : msg); + } + + /** + * Constructs a new NotImplementedException with specified + * nested Throwable and default message. + * + * @param cause the exception that caused this exception to be thrown + * @since 2.1 + */ + public NotImplementedException(Throwable cause) { + super(DEFAULT_MESSAGE); + this.cause = cause; + } + + /** + * Constructs a new NotImplementedException with specified + * detail message and nested Throwable. + * + * @param msg the error message + * @param cause the exception that caused this exception to be thrown + * @since 2.1 + */ + public NotImplementedException(String msg, Throwable cause) { + super(msg == null ? DEFAULT_MESSAGE : msg); + this.cause = cause; + } + + /** + * Constructs a new NotImplementedException referencing the specified class. + * + * @param clazz + * the Class that has not implemented the method + */ public NotImplementedException(Class clazz) { - super( - "Method is not implemented in class " - + ((clazz == null) ? null : clazz.getName())); + super(clazz == null ? DEFAULT_MESSAGE : DEFAULT_MESSAGE + " in " + clazz); } + // ----------------------------------------------------------------------- /** - *

    Constructs the exception with the specified message.

    + * Gets the root cause of this exception. + * @return the root cause of this exception. * - * @param msg the exception message. + * @since 2.1 */ - public NotImplementedException(String msg) { - super(msg); + public Throwable getCause() { + return cause; } + /** + * Gets the combined the error message of this and any nested errors. + * + * @return the error message + * @since 2.1 + */ + public String getMessage() { + if (super.getMessage() != null) { + return super.getMessage(); + } else if (cause != null) { + return cause.toString(); + } else { + return null; + } + } + + /** + * Returns the error message of the Throwable in the chain + * of Throwables at the specified index, numbered from 0. + * + * @param index the index of the Throwable in the chain + * @return the error message, or null if the Throwable at the + * specified index in the chain does not contain a message + * @throws IndexOutOfBoundsException if the index argument is + * negative or not less than the count of Throwables in the chain + * @since 2.1 + */ + public String getMessage(int index) { + if (index == 0) { + return super.getMessage(); + } + return delegate.getMessage(index); + } + + /** + * Returns the error message of this and any nested Throwable objects. + * Each throwable returns a message, a null string is included in the array if + * there is no message for a particular Throwable. + * + * @return the error messages + * @since 2.1 + */ + public String[] getMessages() { + return delegate.getMessages(); + } + + /** + * Returns the Throwable in the chain by index. + * + * @param index the index to retrieve + * @return the Throwable + * @throws IndexOutOfBoundsException if the index argument is + * negative or not less than the count of Throwables in the chain + * @since 2.1 + */ + public Throwable getThrowable(int index) { + return delegate.getThrowable(index); + } + + /** + * Returns the number of nested Throwables represented by + * this Nestable, including this Nestable. + * + * @return the throwable count + * @since 2.1 + */ + public int getThrowableCount() { + return delegate.getThrowableCount(); + } + + /** + * Returns this Nestable and any nested Throwables + * in an array of Throwables, one element for each + * Throwable. + * + * @return the Throwables + * @since 2.1 + */ + public Throwable[] getThrowables() { + return delegate.getThrowables(); + } + + /** + * Returns the index of the first occurrence of the specified type. + * If there is no match, -1 is returned. + * + * @param type the type to search for + * @return index of the first occurrence of the type in the chain, or -1 if + * the type is not found + * @since 2.1 + */ + public int indexOfThrowable(Class type) { + return delegate.indexOfThrowable(type, 0); + } + + /** + * Returns the index of the first occurrence of the specified type starting + * from the specified index. If there is no match, -1 is returned. + * + * @param type the type to search for + * @param fromIndex the index of the starting position in the chain to be searched + * @return index of the first occurrence of the type in the chain, or -1 if + * the type is not found + * @throws IndexOutOfBoundsException if the fromIndex argument + * is negative or not less than the count of Throwables in the chain + * @since 2.1 + */ + public int indexOfThrowable(Class type, int fromIndex) { + return delegate.indexOfThrowable(type, fromIndex); + } + + /** + * Prints the stack trace of this exception. + * Includes information from the exception, if any, which caused this exception. + * + * @since 2.1 + */ + public void printStackTrace() { + delegate.printStackTrace(); + } + + /** + * Prints the stack trace of this exception to the specified stream. + * Includes information from the exception, if any, which caused this exception. + * + * @param out the stream to write to + * @since 2.1 + */ + public void printStackTrace(PrintStream out) { + delegate.printStackTrace(out); + } + + /** + * Prints the stack trace of this exception to the specified writer. + * Includes information from the exception, if any, which caused this exception. + * + * @param out the writer to write to + * @since 2.1 + */ + public void printStackTrace(PrintWriter out) { + delegate.printStackTrace(out); + } + + /** + * Prints the stack trace for this exception only (root cause not included) + * using the specified writer. + * + * @param out the writer to write to + * @since 2.1 + */ + public final void printPartialStackTrace(PrintWriter out) { + super.printStackTrace(out); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/NullArgumentException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/NullArgumentException.java (.../NullArgumentException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/NullArgumentException.java (.../NullArgumentException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,76 +1,64 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; /** *

    Thrown to indicate that an argument was null and should - * not have been.

    + * not have been. + * This exception supplements the standard IllegalArgumentException + * by providing a more semantically rich description of the problem.

    * + *

    NullArgumentException represents the case where a method takes + * in a parameter that must not be null. + * Some coding standards would use NullPointerException for this case, + * others will use IllegalArgumentException. + * Thus this exception would be used in place of + * IllegalArgumentException, yet it still extends it.

    + * + *
    + * public void foo(String str) {
    + *   if (str == null) {
    + *     throw new NullArgumentException("str");
    + *   }
    + *   // do something with the string
    + * }
    + * 
    + * + * @author Apache Software Foundation * @author Matthew Hawthorne - * @author Stephen Colebourne * @since 2.0 * @version $Id$ */ public class NullArgumentException extends IllegalArgumentException { - /** - *

    Instantiates with the given argument name.

    + /** + * Required for serialization support. * - * @param argName the name of the argument that was null. - */ - public NullArgumentException(String argName) { - super(argName + " must not be null."); - } + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1174360235354917591L; + /** + *

    Instantiates with the given argument name.

    + * + * @param argName the name of the argument that was null. + */ + public NullArgumentException(String argName) { + super((argName == null ? "Argument" : argName) + " must not be null."); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberRange.java (.../NumberRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberRange.java (.../NumberRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,67 +1,32 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import org.apache.commons.lang.text.StrBuilder; + /** *

    Represents a range of {@link Number} objects.

    * *

    This class uses double comparisons. This means that it * is unsuitable for dealing with large Long, BigDecimal * or BigInteger numbers.

    * + * @author Apache Software Foundation * @author Christopher Elkins - * @author Stephen Colebourne * @since 1.0 * @version $Revision$ $Date$ * @@ -231,7 +196,7 @@ * @return the string representation of this range */ public String toString() { - StringBuffer sb = new StringBuffer(); + StrBuilder sb = new StrBuilder(); if (min.doubleValue() < 0) { sb.append('(') Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberUtils.java (.../NumberUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/NumberUtils.java (.../NumberUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -59,9 +22,8 @@ /** *

    Provides extra functionality for Java Number classes.

    * - * @author Henri Yandell + * @author Apache Software Foundation * @author Rand McNeely - * @author Stephen Colebourne * @author Steve Downey * @author Eric Pugh * @author Phil Steitz @@ -82,6 +44,7 @@ * to operate.

    */ public NumberUtils() { + super(); } //-------------------------------------------------------------------- @@ -156,7 +119,7 @@ * *

    First, the value is examined for a type qualifier on the end * ('f','F','d','D','l','L'). If it is found, it starts - * trying to create succissively larger types from the type specified + * trying to create successively larger types from the type specified * until one is found that can hold the value.

    * *

    If a type specifier is not found, it will check for a decimal point @@ -179,6 +142,9 @@ if (val.length() == 0) { throw new NumberFormatException("\"\" is not a valid number."); } + if (val.length() == 1 && !Character.isDigit(val.charAt(0))) { + throw new NumberFormatException(val + " is not a valid number."); + } if (val.startsWith("--")) { // this is protection for poorness in java.lang.BigDecimal. // it accepts this as a legal value, but it does not appear @@ -229,8 +195,7 @@ case 'L' : if (dec == null && exp == null - && isDigits(numeric.substring(1)) - && (numeric.charAt(0) == '-' || Character.isDigit(numeric.charAt(0)))) { + && (numeric.charAt(0) == '-' && isDigits(numeric.substring(1)) || isDigits(numeric))) { try { return createLong(numeric); } catch (NumberFormatException nfe) { @@ -246,13 +211,14 @@ Float f = NumberUtils.createFloat(numeric); if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) { //If it's too big for a float or the float value = 0 and the string - //has non-zeros in it, then float doens't have the presision we want + //has non-zeros in it, then float does not have the precision we want return f; } - } catch (NumberFormatException nfe) { + } catch (NumberFormatException e) { + // ignore the bad number } - //Fall through + //$FALL-THROUGH$ case 'd' : case 'D' : try { @@ -261,12 +227,14 @@ return d; } } catch (NumberFormatException nfe) { + // empty catch } try { return createBigDecimal(numeric); } catch (NumberFormatException e) { + // empty catch } - //Fall through + //$FALL-THROUGH$ default : throw new NumberFormatException(val + " is not a valid number."); @@ -284,10 +252,12 @@ try { return createInteger(val); } catch (NumberFormatException nfe) { + // empty catch } try { return createLong(val); } catch (NumberFormatException nfe) { + // empty catch } return createBigInteger(val); @@ -300,13 +270,15 @@ return f; } } catch (NumberFormatException nfe) { + // empty catch } try { Double d = createDouble(val); if (!(d.isInfinite() || (d.doubleValue() == 0.0D && !allZeros))) { return d; } } catch (NumberFormatException nfe) { + // empty catch } return createBigDecimal(val); @@ -501,7 +473,7 @@ *

  • NaN *
  • Positive infinity *
  • Maximum double - *
  • Normal positve numbers + *
  • Normal positive numbers *
  • +0.0 *
  • -0.0 *
  • Normal negative numbers @@ -550,7 +522,7 @@ /** *

    Compares two floats for order.

    * - *

    This method is more comprhensive than the standard Java greater than, + *

    This method is more comprehensive than the standard Java greater than, * less than and equals operators.

    *
      *
    • It returns -1 if the first value is less than the second. @@ -563,7 +535,7 @@ *
    • NaN *
    • Positive infinity *
    • Maximum float - *
    • Normal positve numbers + *
    • Normal positive numbers *
    • +0.0 *
    • -0.0 *
    • Normal negative numbers @@ -646,7 +618,7 @@ * @return true if the string is a correctly formatted number */ public static boolean isNumber(String str) { - if ((str == null) || (str.length() == 0)) { + if (StringUtils.isEmpty(str)) { return false; } char[] chars = str.toCharArray(); @@ -730,7 +702,7 @@ } if (chars[i] == 'l' || chars[i] == 'L') { - // not allowing L with an exponoent + // not allowing L with an exponent return foundDigit && !hasExp; } // last character is illegal Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/ObjectUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/ObjectUtils.java (.../ObjectUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/ObjectUtils.java (.../ObjectUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,77 +1,49 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import org.apache.commons.lang.exception.CloneFailedException; +import org.apache.commons.lang.reflect.MethodUtils; + /** *

      Operations on Object.

      * *

      This class tries to handle null input gracefully. * An exception will generally not be thrown for a null input. * Each method documents its behaviour in more detail.

      * + *

      #ThreadSafe#

      + * @author Apache Software Foundation * @author Nissim Karpenstein * @author Janek Bogucki - * @author Daniel Rall - * @author Stephen Colebourne + * @author Daniel L. Rall * @author Gary Gregory + * @author Mario Winterer + * @author David J. M. Karlsen * @since 1.0 * @version $Id$ */ +//@Immutable public class ObjectUtils { - + /** *

      Singleton used as a null placeholder where * null has another meaning.

      @@ -99,6 +71,7 @@ * to operate.

      */ public ObjectUtils() { + super(); } // Defaulting @@ -120,7 +93,7 @@ * @return object if it is not null, defaultValue otherwise */ public static Object defaultIfNull(Object object, Object defaultValue) { - return (object != null ? object : defaultValue); + return object != null ? object : defaultValue; } /** @@ -151,7 +124,48 @@ } return object1.equals(object2); } - + + /** + *

      Compares two objects for inequality, where either one or both + * objects may be null.

      + * + *
      +     * ObjectUtils.notEqual(null, null)                  = false
      +     * ObjectUtils.notEqual(null, "")                    = true
      +     * ObjectUtils.notEqual("", null)                    = true
      +     * ObjectUtils.notEqual("", "")                      = false
      +     * ObjectUtils.notEqual(Boolean.TRUE, null)          = true
      +     * ObjectUtils.notEqual(Boolean.TRUE, "true")        = true
      +     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE)  = false
      +     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
      +     * 
      + * + * @param object1 the first object, may be null + * @param object2 the second object, may be null + * @return false if the values of both objects are the same + * @since 2.6 + */ + public static boolean notEqual(Object object1, Object object2) { + return ObjectUtils.equals(object1, object2) == false; + } + + /** + *

      Gets the hash code of an object returning zero when the + * object is null.

      + * + *
      +     * ObjectUtils.hashCode(null)   = 0
      +     * ObjectUtils.hashCode(obj)    = obj.hashCode()
      +     * 
      + * + * @param obj the object to obtain the hash code of, may be null + * @return the hash code of the object, or zero if null + * @since 2.1 + */ + public static int hashCode(Object obj) { + return (obj == null) ? 0 : obj.hashCode(); + } + // Identity ToString //----------------------------------------------------------------------- /** @@ -174,12 +188,38 @@ if (object == null) { return null; } - return appendIdentityToString(null, object).toString(); + StringBuffer buffer = new StringBuffer(); + identityToString(buffer, object); + return buffer.toString(); } /** *

      Appends the toString that would be produced by Object * if a class did not override toString itself. null + * will throw a NullPointerException for either of the two parameters.

      + * + *
      +     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
      +     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
      +     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
      +     * 
      + * + * @param buffer the buffer to append to + * @param object the object to create a toString for + * @since 2.4 + */ + public static void identityToString(StringBuffer buffer, Object object) { + if (object == null) { + throw new NullPointerException("Cannot get the toString of a null identity"); + } + buffer.append(object.getClass().getName()) + .append('@') + .append(Integer.toHexString(System.identityHashCode(object))); + } + + /** + *

      Appends the toString that would be produced by Object + * if a class did not override toString itself. null * will return null.

      * *
      @@ -194,6 +234,7 @@
            * @return the default toString text, or null if
            *  null passed in
            * @since 2.0
      +     * @deprecated The design of this method is bad - see LANG-360. Instead, use identityToString(StringBuffer, Object).
            */
           public static StringBuffer appendIdentityToString(StringBuffer buffer, Object object) {
               if (object == null) {
      @@ -228,7 +269,7 @@
            * @since 2.0
            */
           public static String toString(Object obj) {
      -        return (obj == null ? "" : obj.toString());
      +        return obj == null ? "" : obj.toString();
           }
       
           /**
      @@ -251,9 +292,144 @@
            * @since 2.0
            */
           public static String toString(Object obj, String nullStr) {
      -        return (obj == null ? nullStr : obj.toString());
      +        return obj == null ? nullStr : obj.toString();
           }
       
      +    // Min/Max
      +    //-----------------------------------------------------------------------
      +    /**
      +     * Null safe comparison of Comparables.
      +     * 
      +     * @param c1  the first comparable, may be null
      +     * @param c2  the second comparable, may be null
      +     * @return
      +     *  
        + *
      • If both objects are non-null and unequal, the lesser object. + *
      • If both objects are non-null and equal, c1. + *
      • If one of the comparables is null, the non-null object. + *
      • If both the comparables are null, null is returned. + *
      + */ + public static Object min(Comparable c1, Comparable c2) { + return (compare(c1, c2, true) <= 0 ? c1 : c2); + } + + /** + * Null safe comparison of Comparables. + * + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @return + *
        + *
      • If both objects are non-null and unequal, the greater object. + *
      • If both objects are non-null and equal, c1. + *
      • If one of the comparables is null, the non-null object. + *
      • If both the comparables are null, null is returned. + *
      + */ + public static Object max(Comparable c1, Comparable c2) { + return (compare(c1, c2, false) >= 0 ? c1 : c2); + } + + /** + * Null safe comparison of Comparables. + * {@code null} is assumed to be less than a non-{@code null} value. + * + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + * @since 2.6 + */ + public static int compare(Comparable c1, Comparable c2) { + return compare(c1, c2, false); + } + + /** + * Null safe comparison of Comparables. + * + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @param nullGreater if true null is considered greater + * than a Non-null value or if false null is + * considered less than a Non-null value + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + * @see java.util.Comparator#compare(Object, Object) + * @since 2.6 + */ + public static int compare(Comparable c1, Comparable c2, boolean nullGreater) { + if (c1 == c2) { + return 0; + } else if (c1 == null) { + return (nullGreater ? 1 : -1); + } else if (c2 == null) { + return (nullGreater ? -1 : 1); + } + return c1.compareTo(c2); + } + + /** + * Clone an object. + * + * @param o the object to clone + * @return the clone if the object implements {@link Cloneable} otherwise null + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 2.6 + */ + public static Object clone(final Object o) { + if (o instanceof Cloneable) { + final Object result; + if (o.getClass().isArray()) { + final Class componentType = o.getClass().getComponentType(); + if (!componentType.isPrimitive()) { + result = ((Object[])o).clone(); + } else { + int length = Array.getLength(o); + result = Array.newInstance(componentType, length); + while (length-- > 0) { + Array.set(result, length, Array.get(o, length)); + } + } + } else { + try { + result = MethodUtils.invokeMethod(o, "clone", null); + } catch (final NoSuchMethodException e) { + throw new CloneFailedException("Cloneable type " + + o.getClass().getName() + + " has no clone method", e); + } catch (final IllegalAccessException e) { + throw new CloneFailedException("Cannot clone Cloneable type " + + o.getClass().getName(), e); + } catch (final InvocationTargetException e) { + throw new CloneFailedException("Exception cloning Cloneable type " + + o.getClass().getName(), e.getTargetException()); + } + } + return result; + } + + return null; + } + + /** + * Clone an object if possible. This method is similar to {@link #clone(Object)}, but will + * return the provided instance as the return value instead of null if the instance + * is not cloneable. This is more convenient if the caller uses different + * implementations (e.g. of a service) and some of the implementations do not allow concurrent + * processing or have state. In such cases the implementation can simply provide a proper + * clone implementation and the caller's code does not have to change. + * + * @param o the object to clone + * @return the clone if the object implements {@link Cloneable} otherwise the object itself + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 2.6 + */ + public static Object cloneIfPossible(final Object o) { + final Object clone = clone(o); + return clone == null ? o : clone; + } + // Null //----------------------------------------------------------------------- /** @@ -271,13 +447,18 @@ * cannot be stored.

      */ public static class Null implements Serializable { - // declare serialization compatability with Commons Lang 1.0 + /** + * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0 + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 7092611880189329093L; /** * Restricted constructor - singleton. */ Null() { + super(); } /** @@ -289,5 +470,5 @@ return ObjectUtils.NULL; } } - + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/RandomStringUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/RandomStringUtils.java (.../RandomStringUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/RandomStringUtils.java (.../RandomStringUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,66 +1,36 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.util.Random; /** *

      Operations for random Strings.

      + *

      Currently private high surrogate characters are ignored. + * 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) + * then it is followed by a low surrogate. If a low surrogate is chosen, + * 56320 (dc00) to 57343 (dfff) then it is placed after a randomly + * chosen high surrogate.

      * - * @author GenerationJava Core library - * @author Henri Yandell + *

      #ThreadSafe#

      + * @author Apache Software Foundation * @author Steven Caswell - * @author Stephen Colebourne * @author Gary Gregory * @author Phil Steitz * @since 1.0 @@ -84,6 +54,7 @@ * to operate.

      */ public RandomStringUtils() { + super(); } // Random @@ -167,7 +138,7 @@ * @param count the length of random string to create * @param letters if true, generated string will include * alphabetic characters - * @param numbers if true, generatd string will include + * @param numbers if true, generated string will include * numeric characters * @return the random string */ @@ -252,7 +223,8 @@ * @throws IllegalArgumentException if count < 0. * @since 2.0 */ - public static String random(int count, int start, int end, boolean letters, boolean numbers, char[] chars, Random random) { + public static String random(int count, int start, int end, boolean letters, boolean numbers, + char[] chars, Random random) { if (count == 0) { return ""; } else if (count < 0) { @@ -267,7 +239,7 @@ } } - StringBuffer buffer = new StringBuffer(); + char[] buffer = new char[count]; int gap = end - start; while (count-- != 0) { @@ -277,16 +249,39 @@ } else { ch = chars[random.nextInt(gap) + start]; } - if ((letters && numbers && Character.isLetterOrDigit(ch)) - || (letters && Character.isLetter(ch)) + if ((letters && Character.isLetter(ch)) || (numbers && Character.isDigit(ch)) - || (!letters && !numbers)) { - buffer.append(ch); + || (!letters && !numbers)) + { + if(ch >= 56320 && ch <= 57343) { + if(count == 0) { + count++; + } else { + // low surrogate, insert high surrogate after putting it in + buffer[count] = ch; + count--; + buffer[count] = (char) (55296 + random.nextInt(128)); + } + } else if(ch >= 55296 && ch <= 56191) { + if(count == 0) { + count++; + } else { + // high surrogate, insert low surrogate before putting it in + buffer[count] = (char) (56320 + random.nextInt(128)); + count--; + buffer[count] = ch; + } + } else if(ch >= 56192 && ch <= 56319) { + // private high surrogate, no effing clue, so skip it + count++; + } else { + buffer[count] = ch; + } } else { count++; } } - return buffer.toString(); + return new String(buffer); } /** Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationException.java (.../SerializationException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationException.java (.../SerializationException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -60,13 +23,20 @@ * *

      The original error is wrapped within this one.

      * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 1.0 * @version $Id$ */ public class SerializationException extends NestableRuntimeException { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 4029025366392702726L; + + /** *

      Constructs a new SerializationException without specified * detail message.

      */ Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationUtils.java (.../SerializationUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/SerializationUtils.java (.../SerializationUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -75,10 +38,11 @@ *

      This class throws exceptions for invalid null inputs. * Each method documents its behaviour in more detail.

      * + *

      #ThreadSafe#

      + * @author Apache Software Foundation * @author Nissim Karpenstein * @author Janek Bogucki - * @author Daniel Rall - * @author Stephen Colebourne + * @author Daniel L. Rall * @author Jeff Varszegi * @author Gary Gregory * @since 1.0 @@ -152,7 +116,7 @@ out.close(); } } catch (IOException ex) { - // ignore; + // ignore close exception } } } @@ -208,7 +172,7 @@ in.close(); } } catch (IOException ex) { - // ignore + // ignore close exception } } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/StringEscapeUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/StringEscapeUtils.java (.../StringEscapeUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/StringEscapeUtils.java (.../StringEscapeUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,73 +1,39 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.io.IOException; +import java.io.StringWriter; import java.io.Writer; +import java.util.Locale; import org.apache.commons.lang.exception.NestableRuntimeException; +import org.apache.commons.lang.text.StrBuilder; /** *

      Escapes and unescapes Strings for * Java, Java Script, HTML, XML, and SQL.

      * + *

      #ThreadSafe#

      + * @author Apache Software Foundation * @author Apache Jakarta Turbine - * @author GenerationJavaCore library * @author Purple Technology - * @author Henri Yandell * @author Alexander Day Chaffee - * @author Antony Riley + * @author Antony Riley * @author Helge Tesgaard * @author Sean Brown * @author Gary Gregory @@ -78,6 +44,11 @@ */ public class StringEscapeUtils { + private static final char CSV_DELIMITER = ','; + private static final char CSV_QUOTE = '"'; + private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); + private static final char[] CSV_SEARCH_CHARS = new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF}; + /** *

      StringEscapeUtils instances should NOT be constructed in * standard programming.

      @@ -89,6 +60,7 @@ * instance to operate.

      */ public StringEscapeUtils() { + super(); } // Java and JavaScript @@ -115,7 +87,7 @@ * @return String with escaped values, null if null string input */ public static String escapeJava(String str) { - return escapeJavaStyleString(str, false); + return escapeJavaStyleString(str, false, false); } /** @@ -128,10 +100,10 @@ * @param out Writer to write escaped string into * @param str String to escape values in, may be null * @throws IllegalArgumentException if the Writer is null - * @throws IOException if error occurs on undelying Writer + * @throws IOException if error occurs on underlying Writer */ public static void escapeJava(Writer out, String str) throws IOException { - escapeJavaStyleString(out, str, false); + escapeJavaStyleString(out, str, false, false); } /** @@ -156,7 +128,7 @@ * @return String with escaped values, null if null string input */ public static String escapeJavaScript(String str) { - return escapeJavaStyleString(str, true); + return escapeJavaStyleString(str, true, true); } /** @@ -169,28 +141,45 @@ * @param out Writer to write escaped string into * @param str String to escape values in, may be null * @throws IllegalArgumentException if the Writer is null - * @throws IOException if error occurs on undelying Writer + * @throws IOException if error occurs on underlying Writer **/ public static void escapeJavaScript(Writer out, String str) throws IOException { - escapeJavaStyleString(out, str, true); + escapeJavaStyleString(out, str, true, true); } - private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes) { + /** + *

      Worker method for the {@link #escapeJavaScript(String)} method.

      + * + * @param str String to escape values in, may be null + * @param escapeSingleQuotes escapes single quotes if true + * @param escapeForwardSlash TODO + * @return the escaped string + */ + private static String escapeJavaStyleString(String str, boolean escapeSingleQuotes, boolean escapeForwardSlash) { if (str == null) { return null; } try { - StringPrintWriter writer = new StringPrintWriter(str.length() * 2); - escapeJavaStyleString(writer, str, escapeSingleQuotes); - return writer.getString(); + StringWriter writer = new StringWriter(str.length() * 2); + escapeJavaStyleString(writer, str, escapeSingleQuotes, escapeForwardSlash); + return writer.toString(); } catch (IOException ioe) { // this should never ever happen while writing to a StringWriter - ioe.printStackTrace(); - return null; + throw new UnhandledException(ioe); } } - private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote) throws IOException { + /** + *

      Worker method for the {@link #escapeJavaScript(String)} method.

      + * + * @param out write to receieve the escaped string + * @param str String to escape values in, may be null + * @param escapeSingleQuote escapes single quotes if true + * @param escapeForwardSlash TODO + * @throws IOException if an IOException occurs + */ + private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote, + boolean escapeForwardSlash) throws IOException { if (out == null) { throw new IllegalArgumentException("The Writer must not be null"); } @@ -211,23 +200,23 @@ out.write("\\u00" + hex(ch)); } else if (ch < 32) { switch (ch) { - case '\b': + case '\b' : out.write('\\'); out.write('b'); break; - case '\n': + case '\n' : out.write('\\'); out.write('n'); break; - case '\t': + case '\t' : out.write('\\'); out.write('t'); break; - case '\f': + case '\f' : out.write('\\'); out.write('f'); break; - case '\r': + case '\r' : out.write('\\'); out.write('r'); break; @@ -241,18 +230,26 @@ } } else { switch (ch) { - case '\'': - if (escapeSingleQuote) out.write('\\'); + case '\'' : + if (escapeSingleQuote) { + out.write('\\'); + } out.write('\''); break; - case '"': + case '"' : out.write('\\'); out.write('"'); break; - case '\\': + case '\\' : out.write('\\'); out.write('\\'); break; + case '/' : + if (escapeForwardSlash) { + out.write('\\'); + } + out.write('/'); + break; default : out.write(ch); break; @@ -269,7 +266,7 @@ * @return An upper case hexadecimal String */ private static String hex(char ch) { - return Integer.toHexString(ch).toUpperCase(); + return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); } /** @@ -286,13 +283,12 @@ return null; } try { - StringPrintWriter writer = new StringPrintWriter(str.length()); + StringWriter writer = new StringWriter(str.length()); unescapeJava(writer, str); - return writer.getString(); + return writer.toString(); } catch (IOException ioe) { // this should never ever happen while writing to a StringWriter - ioe.printStackTrace(); - return null; + throw new UnhandledException(ioe); } } @@ -309,7 +305,7 @@ * @param out the Writer used to output unescaped characters * @param str the String to unescape, may be null * @throws IllegalArgumentException if the Writer is null - * @throws IOException if error occurs on undelying Writer + * @throws IOException if error occurs on underlying Writer */ public static void unescapeJava(Writer out, String str) throws IOException { if (out == null) { @@ -319,7 +315,7 @@ return; } int sz = str.length(); - StringBuffer unicode = new StringBuffer(4); + StrBuilder unicode = new StrBuilder(4); boolean hadSlash = false; boolean inUnicode = false; for (int i = 0; i < sz; i++) { @@ -330,7 +326,7 @@ unicode.append(ch); if (unicode.length() == 4) { // unicode now contains the four hex digits - // which represents our unicode chacater + // which represents our unicode character try { int value = Integer.parseInt(unicode.toString(), 16); out.write((char) value); @@ -424,7 +420,7 @@ * @param out the Writer used to output unescaped characters * @param str the String to unescape, may be null * @throws IllegalArgumentException if the Writer is null - * @throws IOException if error occurs on undelying Writer + * @throws IOException if error occurs on underlying Writer */ public static void unescapeJavaScript(Writer out, String str) throws IOException { unescapeJava(out, str); @@ -436,31 +432,83 @@ *

      Escapes the characters in a String using HTML entities.

      * *

      - * For example: "bread" & "butter" => &quot;bread&quot; &amp; &quot;butter&quot;. + * For example: + *

      + *

      "bread" & "butter"

      + * becomes: + *

      + * &quot;bread&quot; &amp; &quot;butter&quot;. *

      * - *

      Supports all known HTML 4.0 entities, including funky accents.

      - * + *

      Supports all known HTML 4.0 entities, including funky accents. + * Note that the commonly used apostrophe escape character (&apos;) + * is not a legal entity and so is not supported).

      + * * @param str the String to escape, may be null * @return a new escaped String, null if null string input * * @see #unescapeHtml(String) - * @see
      ISO Entities - * @see
      HTML 3.2 Character Entities for ISO Latin-1 - * @see
      HTML 4.0 Character entity references - * @see
      HTML 4.01 Character References - * @see
      HTML 4.01 Code positions - **/ + * @see ISO Entities + * @see HTML 3.2 Character Entities for ISO Latin-1 + * @see HTML 4.0 Character entity references + * @see HTML 4.01 Character References + * @see HTML 4.01 Code positions + */ public static String escapeHtml(String str) { if (str == null) { return null; } - //todo: add a version that takes a Writer - //todo: rewrite underlying method to use a Writer instead of a StringBuffer - return Entities.HTML40.escape(str); + try { + StringWriter writer = new StringWriter ((int)(str.length() * 1.5)); + escapeHtml(writer, str); + return writer.toString(); + } catch (IOException ioe) { + //should be impossible + throw new UnhandledException(ioe); + } } /** + *

      Escapes the characters in a String using HTML entities and writes + * them to a Writer.

      + * + *

      + * For example: + *

      + * "bread" & "butter" + *

      becomes:

      + * &quot;bread&quot; &amp; &quot;butter&quot;. + * + *

      Supports all known HTML 4.0 entities, including funky accents. + * Note that the commonly used apostrophe escape character (&apos;) + * is not a legal entity and so is not supported).

      + * + * @param writer the writer receiving the escaped string, not null + * @param string the String to escape, may be null + * @throws IllegalArgumentException if the writer is null + * @throws IOException when Writer passed throws the exception from + * calls to the {@link Writer#write(int)} methods. + * + * @see #escapeHtml(String) + * @see #unescapeHtml(String) + * @see ISO Entities + * @see HTML 3.2 Character Entities for ISO Latin-1 + * @see HTML 4.0 Character entity references + * @see HTML 4.01 Character References + * @see HTML 4.01 Code positions + */ + public static void escapeHtml(Writer writer, String string) throws IOException { + if (writer == null ) { + throw new IllegalArgumentException ("The Writer must not be null."); + } + if (string == null) { + return; + } + Entities.HTML40.escape(writer, string); + } + + //----------------------------------------------------------------------- + /** *

      Unescapes a string containing entity escapes to a string * containing the actual Unicode characters corresponding to the * escapes. Supports HTML 4.0 entities.

      @@ -474,55 +522,155 @@ * * @param str the String to unescape, may be null * @return a new unescaped String, null if null string input - * @see #escapeHtml(String) - **/ + * @see #escapeHtml(Writer, String) + */ public static String unescapeHtml(String str) { if (str == null) { return null; } - return Entities.HTML40.unescape(str); + try { + StringWriter writer = new StringWriter ((int)(str.length() * 1.5)); + unescapeHtml(writer, str); + return writer.toString(); + } catch (IOException ioe) { + //should be impossible + throw new UnhandledException(ioe); + } } /** + *

      Unescapes a string containing entity escapes to a string + * containing the actual Unicode characters corresponding to the + * escapes. Supports HTML 4.0 entities.

      + * + *

      For example, the string "&lt;Fran&ccedil;ais&gt;" + * will become "<Français>"

      + * + *

      If an entity is unrecognized, it is left alone, and inserted + * verbatim into the result string. e.g. "&gt;&zzzz;x" will + * become ">&zzzz;x".

      + * + * @param writer the writer receiving the unescaped string, not null + * @param string the String to unescape, may be null + * @throws IllegalArgumentException if the writer is null + * @throws IOException if an IOException occurs + * @see #escapeHtml(String) + */ + public static void unescapeHtml(Writer writer, String string) throws IOException { + if (writer == null ) { + throw new IllegalArgumentException ("The Writer must not be null."); + } + if (string == null) { + return; + } + Entities.HTML40.unescape(writer, string); + } + + //----------------------------------------------------------------------- + /** *

      Escapes the characters in a String using XML entities.

      * *

      For example: "bread" & "butter" => * &quot;bread&quot; &amp; &quot;butter&quot;. *

      * - *

      Supports only the four basic XML entities (gt, lt, quot, amp). + *

      Supports only the five basic XML entities (gt, lt, quot, amp, apos). * Does not support DTDs or external entities.

      * + *

      Note that unicode characters greater than 0x7f are currently escaped to + * their numerical \\u equivalent. This may change in future releases.

      + * + * @param writer the writer receiving the unescaped string, not null * @param str the String to escape, may be null + * @throws IllegalArgumentException if the writer is null + * @throws IOException if there is a problem writing + * @see #unescapeXml(java.lang.String) + */ + public static void escapeXml(Writer writer, String str) throws IOException { + if (writer == null ) { + throw new IllegalArgumentException ("The Writer must not be null."); + } + if (str == null) { + return; + } + Entities.XML.escape(writer, str); + } + + /** + *

      Escapes the characters in a String using XML entities.

      + * + *

      For example: "bread" & "butter" => + * &quot;bread&quot; &amp; &quot;butter&quot;. + *

      + * + *

      Supports only the five basic XML entities (gt, lt, quot, amp, apos). + * Does not support DTDs or external entities.

      + * + *

      Note that unicode characters greater than 0x7f are currently escaped to + * their numerical \\u equivalent. This may change in future releases.

      + * + * @param str the String to escape, may be null * @return a new escaped String, null if null string input * @see #unescapeXml(java.lang.String) - **/ + */ public static String escapeXml(String str) { if (str == null) { return null; } return Entities.XML.escape(str); } + //----------------------------------------------------------------------- /** *

      Unescapes a string containing XML entity escapes to a string * containing the actual Unicode characters corresponding to the * escapes.

      * - *

      Supports only the four basic XML entities (gt, lt, quot, amp). + *

      Supports only the five basic XML entities (gt, lt, quot, amp, apos). * Does not support DTDs or external entities.

      * + *

      Note that numerical \\u unicode codes are unescaped to their respective + * unicode characters. This may change in future releases.

      + * + * @param writer the writer receiving the unescaped string, not null * @param str the String to unescape, may be null + * @throws IllegalArgumentException if the writer is null + * @throws IOException if there is a problem writing + * @see #escapeXml(String) + */ + public static void unescapeXml(Writer writer, String str) throws IOException { + if (writer == null ) { + throw new IllegalArgumentException ("The Writer must not be null."); + } + if (str == null) { + return; + } + Entities.XML.unescape(writer, str); + } + + /** + *

      Unescapes a string containing XML entity escapes to a string + * containing the actual Unicode characters corresponding to the + * escapes.

      + * + *

      Supports only the five basic XML entities (gt, lt, quot, amp, apos). + * Does not support DTDs or external entities.

      + * + *

      Note that numerical \\u unicode codes are unescaped to their respective + * unicode characters. This may change in future releases.

      + * + * @param str the String to unescape, may be null * @return a new unescaped String, null if null string input * @see #escapeXml(String) - **/ + */ public static String unescapeXml(String str) { if (str == null) { return null; } return Entities.XML.unescape(str); } + //----------------------------------------------------------------------- /** *

      Escapes the characters in a String to be suitable to pass to * an SQL query.

      @@ -548,5 +696,166 @@ return StringUtils.replace(str, "'", "''"); } -} + //----------------------------------------------------------------------- + /** + *

      Returns a String value for a CSV column enclosed in double quotes, + * if required.

      + * + *

      If the value contains a comma, newline or double quote, then the + * String value is returned enclosed in double quotes.

      + *

      + * + *

      Any double quote characters in the value are escaped with another double quote.

      + * + *

      If the value does not contain a comma, newline or double quote, then the + * String value is returned unchanged.

      + *

      + * + * see Wikipedia and + * RFC 4180. + * + * @param str the input CSV column String, may be null + * @return the input String, enclosed in double quotes if the value contains a comma, + * newline or double quote, null if null string input + * @since 2.4 + */ + public static String escapeCsv(String str) { + if (StringUtils.containsNone(str, CSV_SEARCH_CHARS)) { + return str; + } + try { + StringWriter writer = new StringWriter(); + escapeCsv(writer, str); + return writer.toString(); + } catch (IOException ioe) { + // this should never ever happen while writing to a StringWriter + throw new UnhandledException(ioe); + } + } + + /** + *

      Writes a String value for a CSV column enclosed in double quotes, + * if required.

      + * + *

      If the value contains a comma, newline or double quote, then the + * String value is written enclosed in double quotes.

      + *

      + * + *

      Any double quote characters in the value are escaped with another double quote.

      + * + *

      If the value does not contain a comma, newline or double quote, then the + * String value is written unchanged (null values are ignored).

      + *

      + * + * see Wikipedia and + * RFC 4180. + * + * @param str the input CSV column String, may be null + * @param out Writer to write input string to, enclosed in double quotes if it contains + * a comma, newline or double quote + * @throws IOException if error occurs on underlying Writer + * @since 2.4 + */ + public static void escapeCsv(Writer out, String str) throws IOException { + if (StringUtils.containsNone(str, CSV_SEARCH_CHARS)) { + if (str != null) { + out.write(str); + } + return; + } + out.write(CSV_QUOTE); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == CSV_QUOTE) { + out.write(CSV_QUOTE); // escape double quote + } + out.write(c); + } + out.write(CSV_QUOTE); + } + + /** + *

      Returns a String value for an unescaped CSV column.

      + * + *

      If the value is enclosed in double quotes, and contains a comma, newline + * or double quote, then quotes are removed. + *

      + * + *

      Any double quote escaped characters (a pair of double quotes) are unescaped + * to just one double quote.

      + * + *

      If the value is not enclosed in double quotes, or is and does not contain a + * comma, newline or double quote, then the String value is returned unchanged.

      + *

      + * + * see Wikipedia and + * RFC 4180. + * + * @param str the input CSV column String, may be null + * @return the input String, with enclosing double quotes removed and embedded double + * quotes unescaped, null if null string input + * @since 2.4 + */ + public static String unescapeCsv(String str) { + if (str == null) { + return null; + } + try { + StringWriter writer = new StringWriter(); + unescapeCsv(writer, str); + return writer.toString(); + } catch (IOException ioe) { + // this should never ever happen while writing to a StringWriter + throw new UnhandledException(ioe); + } + } + + /** + *

      Returns a String value for an unescaped CSV column.

      + * + *

      If the value is enclosed in double quotes, and contains a comma, newline + * or double quote, then quotes are removed. + *

      + * + *

      Any double quote escaped characters (a pair of double quotes) are unescaped + * to just one double quote.

      + * + *

      If the value is not enclosed in double quotes, or is and does not contain a + * comma, newline or double quote, then the String value is returned unchanged.

      + *

      + * + * see Wikipedia and + * RFC 4180. + * + * @param str the input CSV column String, may be null + * @param out Writer to write the input String to, with enclosing double quotes + * removed and embedded double quotes unescaped, null if null string input + * @throws IOException if error occurs on underlying Writer + * @since 2.4 + */ + public static void unescapeCsv(Writer out, String str) throws IOException { + if (str == null) { + return; + } + if (str.length() < 2) { + out.write(str); + return; + } + if ( str.charAt(0) != CSV_QUOTE || str.charAt(str.length() - 1) != CSV_QUOTE ) { + out.write(str); + return; + } + + // strip quotes + String quoteless = str.substring(1, str.length() - 1); + + if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) { + // deal with escaped quotes; ie) "" + str = StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR); + } + + out.write(str); + } + +} Fisheye: Tag 6aa36ddefbf750d2b246992fee82df738a66eefa refers to a dead (removed) revision in file `3rdParty_sources/commons-lang/org/apache/commons/lang/StringPrintWriter.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/StringUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/StringUtils.java (.../StringUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/StringUtils.java (.../StringUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,86 +1,59 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Locale; +import org.apache.commons.lang.text.StrBuilder; + /** - *

      Operations on {@link java.lang.String} that are + *

      Operations on {@link java.lang.String} that are * null safe.

      - * + * *
        *
      • IsEmpty/IsBlank * - checks if a String contains text
      • *
      • Trim/Strip * - removes leading and trailing whitespace
      • *
      • Equals * - compares two strings null-safe
      • + *
      • startsWith + * - check if a String starts with a prefix null-safe
      • + *
      • endsWith + * - check if a String ends with a suffix null-safe
      • *
      • IndexOf/LastIndexOf/Contains * - null-safe index-of checks *
      • IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut * - index-of any of a set of Strings
      • - *
      • ContainsOnly/ContainsNone - * - does String contains only/none of these characters
      • + *
      • ContainsOnly/ContainsNone/ContainsAny + * - does String contains only/none/any of these characters
      • *
      • Substring/Left/Right/Mid * - null-safe substring extractions
      • *
      • SubstringBefore/SubstringAfter/SubstringBetween * - substring extraction relative to other strings
      • *
      • Split/Join * - splits a String into an array of substrings and vice versa
      • - *
      • Replace/Delete/Overlay + *
      • Remove/Delete + * - removes part of a String
      • + *
      • Replace/Overlay * - Searches a String and replaces one String with another
      • *
      • Chomp/Chop * - removes the last part of a String
      • @@ -89,8 +62,8 @@ *
      • UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize * - changes the case of a String
      • *
      • CountMatches - * - counts the number of occurrances of one String in another
      • - *
      • IsAlpha/IsNumeric/IsWhitespace + * - counts the number of occurrences of one String in another
      • + *
      • IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable * - checks the characters in a String
      • *
      • DefaultString * - protects against a null input String
      • @@ -99,60 +72,65 @@ *
      • Abbreviate * - abbreviates a string using ellipsis
      • *
      • Difference - * - compares two Strings and reports on their differences
      • + * - compares Strings and reports on their differences *
      • LevensteinDistance * - the number of changes needed to change one String into another
      • *
      * *

      The StringUtils class defines certain words related to * String handling.

      - * + * *
        *
      • null - null
      • *
      • empty - a zero-length string ("")
      • *
      • space - the space character (' ', char 32)
      • *
      • whitespace - the characters defined by {@link Character#isWhitespace(char)}
      • *
      • trim - the characters <= 32 as in {@link String#trim()}
      • *
      - * + * *

      StringUtils handles null input Strings quietly. * That is to say that a null input will return null. * Where a boolean or int is being returned * details vary by method.

      - * - *

      A side effect of the null handling is that a + * + *

      A side effect of the null handling is that a * NullPointerException should be considered a bug in * StringUtils (except for deprecated methods).

      - * + * *

      Methods in this class give sample code to explain their operation. * The symbol * is used to indicate any input including null.

      * + *

      #ThreadSafe#

      * @see java.lang.String + * @author Apache Software Foundation * @author Apache Jakarta Turbine - * @author GenerationJavaCore * @author Jon S. Stevens - * @author Daniel Rall + * @author Daniel L. Rall * @author Greg Coladonato - * @author Henri Yandell * @author Ed Korthof * @author Rand McNeely - * @author Stephen Colebourne * @author Fredrik Westermarck * @author Holger Krauth * @author Alexander Day Chaffee * @author Henning P. Schmiedehausen * @author Arun Mammen Thomas * @author Gary Gregory * @author Phil Steitz + * @author Al Chou + * @author Michael Davey + * @author Reuben Sivan + * @author Chris Hyzer + * @author Scott Johnson * @since 1.0 * @version $Id$ */ +//@Immutable public class StringUtils { // Performance testing notes (JDK 1.4, Jul03, scolebourne) // Whitespace: // Character.isWhitespace() is faster than WHITESPACE.indexOf() // where WHITESPACE is a string of all whitespace characters - // + // // Character access: // String.charAt(n) versus toCharArray(), then array[n] // String.charAt(n) is about 15% worse for a 10K string @@ -163,29 +141,23 @@ // Append: // String.concat about twice as fast as StringBuffer.append // (not sure who tested this) - + /** * The empty String "". * @since 2.0 */ public static final String EMPTY = ""; - + /** - *

      The maximum size to which the padding constant(s) can expand.

      + * Represents a failed index search. + * @since 2.1 */ - private static final int PAD_LIMIT = 8192; + public static final int INDEX_NOT_FOUND = -1; /** - *

      An array of Strings used for padding.

      - * - *

      Used for efficient space padding. The length of each String expands as needed.

      + *

      The maximum size to which the padding constant(s) can expand.

      */ - private static final String[] PADDING = new String[Character.MAX_VALUE]; - - static { - // space padding is most common, start with 64 chars - PADDING[32] = " "; - } + private static final int PAD_LIMIT = 8192; /** *

      StringUtils instances should NOT be constructed in @@ -196,13 +168,14 @@ * instance to operate.

      */ public StringUtils() { + super(); } // Empty checks //----------------------------------------------------------------------- /** *

      Checks if a String is empty ("") or null.

      - * + * *
            * StringUtils.isEmpty(null)      = true
            * StringUtils.isEmpty("")        = true
      @@ -214,17 +187,17 @@
            * 

      NOTE: This method changed in Lang version 2.0. * It no longer trims the String. * That functionality is available in isBlank().

      - * + * * @param str the String to check, may be null * @return true if the String is empty or null */ public static boolean isEmpty(String str) { - return (str == null || str.length() == 0); + return str == null || str.length() == 0; } /** *

      Checks if a String is not empty ("") and not null.

      - * + * *
            * StringUtils.isNotEmpty(null)      = false
            * StringUtils.isNotEmpty("")        = false
      @@ -237,12 +210,12 @@
            * @return true if the String is not empty and not null
            */
           public static boolean isNotEmpty(String str) {
      -        return (str != null && str.length() > 0);
      +        return !StringUtils.isEmpty(str);
           }
       
           /**
            * 

      Checks if a String is whitespace, empty ("") or null.

      - * + * *
            * StringUtils.isBlank(null)      = true
            * StringUtils.isBlank("")        = true
      @@ -261,7 +234,7 @@
                   return true;
               }
               for (int i = 0; i < strLen; i++) {
      -            if ((Character.isWhitespace(str.charAt(i)) == false) ) {
      +            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                       return false;
                   }
               }
      @@ -270,7 +243,7 @@
       
           /**
            * 

      Checks if a String is not empty (""), not null and not whitespace only.

      - * + * *
            * StringUtils.isNotBlank(null)      = false
            * StringUtils.isNotBlank("")        = false
      @@ -280,21 +253,12 @@
            * 
      * * @param str the String to check, may be null - * @return true if the String is + * @return true if the String is * not empty and not null and not whitespace * @since 2.0 */ public static boolean isNotBlank(String str) { - int strLen; - if (str == null || (strLen = str.length()) == 0) { - return false; - } - for (int i = 0; i < strLen; i++) { - if ((Character.isWhitespace(str.charAt(i)) == false) ) { - return true; - } - } - return false; + return !StringUtils.isBlank(str); } // Trim @@ -303,7 +267,7 @@ *

      Removes control characters (char <= 32) from both * ends of this String, handling null by returning * an empty String ("").

      - * + * *
            * StringUtils.clean(null)          = ""
            * StringUtils.clean("")            = ""
      @@ -319,21 +283,21 @@
            *             Method will be removed in Commons Lang 3.0.
            */
           public static String clean(String str) {
      -        return (str == null ? EMPTY : str.trim());
      +        return str == null ? EMPTY : str.trim();
           }
       
           /**
            * 

      Removes control characters (char <= 32) from both * ends of this String, handling null by returning * null.

      - * + * *

      The String is trimmed using {@link String#trim()}. * Trim removes start and end characters <= 32. * To strip whitespace use {@link #strip(String)}.

      - * + * *

      To trim your choice of characters, use the * {@link #strip(String, String)} methods.

      - * + * *
            * StringUtils.trim(null)          = null
            * StringUtils.trim("")            = ""
      @@ -346,71 +310,71 @@
            * @return the trimmed string, null if null String input
            */
           public static String trim(String str) {
      -        return (str == null ? null : str.trim());
      +        return str == null ? null : str.trim();
           }
       
      -    /** 
      -     * 

      Removes control characters (char <= 32) from both - * ends of this String returning null if the String is + /** + *

      Removes control characters (char <= 32) from both + * ends of this String returning null if the String is * empty ("") after the trim or if it is null. - * + * *

      The String is trimmed using {@link String#trim()}. * Trim removes start and end characters <= 32. * To strip whitespace use {@link #stripToNull(String)}.

      - * + * *
            * StringUtils.trimToNull(null)          = null
            * StringUtils.trimToNull("")            = null
            * StringUtils.trimToNull("     ")       = null
            * StringUtils.trimToNull("abc")         = "abc"
            * StringUtils.trimToNull("    abc    ") = "abc"
            * 
      - * + * * @param str the String to be trimmed, may be null - * @return the trimmed String, + * @return the trimmed String, * null if only chars <= 32, empty or null String input * @since 2.0 */ public static String trimToNull(String str) { String ts = trim(str); - return (ts == null || ts.length() == 0 ? null : ts); + return isEmpty(ts) ? null : ts; } - /** - *

      Removes control characters (char <= 32) from both + /** + *

      Removes control characters (char <= 32) from both * ends of this String returning an empty String ("") if the String * is empty ("") after the trim or if it is null. - * + * *

      The String is trimmed using {@link String#trim()}. * Trim removes start and end characters <= 32. * To strip whitespace use {@link #stripToEmpty(String)}.

      - * + * *
            * StringUtils.trimToEmpty(null)          = ""
            * StringUtils.trimToEmpty("")            = ""
            * StringUtils.trimToEmpty("     ")       = ""
            * StringUtils.trimToEmpty("abc")         = "abc"
            * StringUtils.trimToEmpty("    abc    ") = "abc"
            * 
      - * + * * @param str the String to be trimmed, may be null * @return the trimmed String, or an empty String if null input * @since 2.0 */ public static String trimToEmpty(String str) { - return (str == null ? EMPTY : str.trim()); + return str == null ? EMPTY : str.trim(); } - + // Stripping //----------------------------------------------------------------------- /** *

      Strips whitespace from the start and end of a String.

      - * + * *

      This is similar to {@link #trim(String)} but removes whitespace. * Whitespace is defined by {@link Character#isWhitespace(char)}.

      - * + * *

      A null input String returns null.

      - * + * *
            * StringUtils.strip(null)     = null
            * StringUtils.strip("")       = ""
      @@ -421,34 +385,34 @@
            * StringUtils.strip(" abc ")  = "abc"
            * StringUtils.strip(" ab c ") = "ab c"
            * 
      - * + * * @param str the String to remove whitespace from, may be null * @return the stripped String, null if null String input */ public static String strip(String str) { return strip(str, null); } - - /** + + /** *

      Strips whitespace from the start and end of a String returning * null if the String is empty ("") after the strip.

      - * + * *

      This is similar to {@link #trimToNull(String)} but removes whitespace. * Whitespace is defined by {@link Character#isWhitespace(char)}.

      - * + * *
      -     * StringUtils.strip(null)     = null
      -     * StringUtils.strip("")       = null
      -     * StringUtils.strip("   ")    = null
      -     * StringUtils.strip("abc")    = "abc"
      -     * StringUtils.strip("  abc")  = "abc"
      -     * StringUtils.strip("abc  ")  = "abc"
      -     * StringUtils.strip(" abc ")  = "abc"
      -     * StringUtils.strip(" ab c ") = "ab c"
      +     * StringUtils.stripToNull(null)     = null
      +     * StringUtils.stripToNull("")       = null
      +     * StringUtils.stripToNull("   ")    = null
      +     * StringUtils.stripToNull("abc")    = "abc"
      +     * StringUtils.stripToNull("  abc")  = "abc"
      +     * StringUtils.stripToNull("abc  ")  = "abc"
      +     * StringUtils.stripToNull(" abc ")  = "abc"
      +     * StringUtils.stripToNull(" ab c ") = "ab c"
            * 
      - * + * * @param str the String to be stripped, may be null - * @return the stripped String, + * @return the stripped String, * null if whitespace, empty or null String input * @since 2.0 */ @@ -457,47 +421,47 @@ return null; } str = strip(str, null); - return (str.length() == 0 ? null : str); + return str.length() == 0 ? null : str; } - /** + /** *

      Strips whitespace from the start and end of a String returning * an empty String if null input.

      - * + * *

      This is similar to {@link #trimToEmpty(String)} but removes whitespace. * Whitespace is defined by {@link Character#isWhitespace(char)}.

      - * + * *
      -     * StringUtils.strip(null)     = ""
      -     * StringUtils.strip("")       = ""
      -     * StringUtils.strip("   ")    = ""
      -     * StringUtils.strip("abc")    = "abc"
      -     * StringUtils.strip("  abc")  = "abc"
      -     * StringUtils.strip("abc  ")  = "abc"
      -     * StringUtils.strip(" abc ")  = "abc"
      -     * StringUtils.strip(" ab c ") = "ab c"
      +     * StringUtils.stripToEmpty(null)     = ""
      +     * StringUtils.stripToEmpty("")       = ""
      +     * StringUtils.stripToEmpty("   ")    = ""
      +     * StringUtils.stripToEmpty("abc")    = "abc"
      +     * StringUtils.stripToEmpty("  abc")  = "abc"
      +     * StringUtils.stripToEmpty("abc  ")  = "abc"
      +     * StringUtils.stripToEmpty(" abc ")  = "abc"
      +     * StringUtils.stripToEmpty(" ab c ") = "ab c"
            * 
      - * + * * @param str the String to be stripped, may be null * @return the trimmed String, or an empty String if null input * @since 2.0 */ public static String stripToEmpty(String str) { - return (str == null ? EMPTY : strip(str, null)); + return str == null ? EMPTY : strip(str, null); } - + /** *

      Strips any of a set of characters from the start and end of a String. * This is similar to {@link String#trim()} but allows the characters * to be stripped to be controlled.

      * *

      A null input String returns null. * An empty string ("") input returns the empty string.

      - * + * *

      If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}. * Alternatively use {@link #strip(String)}.

      - * + * *
            * StringUtils.strip(null, *)          = null
            * StringUtils.strip("", *)            = ""
      @@ -507,13 +471,13 @@
            * StringUtils.strip(" abc ", null)    = "abc"
            * StringUtils.strip("  abcyx", "xyz") = "  abc"
            * 
      - * + * * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ public static String strip(String str, String stripChars) { - if (str == null || str.length() == 0) { + if (isEmpty(str)) { return str; } str = stripStart(str, stripChars); @@ -525,10 +489,10 @@ * *

      A null input String returns null. * An empty string ("") input returns the empty string.

      - * + * *

      If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

      - * + * *
            * StringUtils.stripStart(null, *)          = null
            * StringUtils.stripStart("", *)            = ""
      @@ -539,7 +503,7 @@
            * StringUtils.stripStart(" abc ", null)    = "abc "
            * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
            * 
      - * + * * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, null if null String input @@ -557,7 +521,7 @@ } else if (stripChars.length() == 0) { return str; } else { - while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != -1)) { + while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) { start++; } } @@ -569,10 +533,10 @@ * *

      A null input String returns null. * An empty string ("") input returns the empty string.

      - * + * *

      If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

      - * + * *
            * StringUtils.stripEnd(null, *)          = null
            * StringUtils.stripEnd("", *)            = ""
      @@ -582,26 +546,27 @@
            * StringUtils.stripEnd("abc  ", null)    = "abc"
            * StringUtils.stripEnd(" abc ", null)    = " abc"
            * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
      +     * StringUtils.stripEnd("120.00", ".0")   = "12"
            * 
      - * + * * @param str the String to remove characters from, may be null - * @param stripChars the characters to remove, null treated as whitespace + * @param stripChars the set of characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ public static String stripEnd(String str, String stripChars) { int end; if (str == null || (end = str.length()) == 0) { return str; } - + if (stripChars == null) { while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) { end--; } } else if (stripChars.length() == 0) { return str; } else { - while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) { + while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) { end--; } } @@ -618,21 +583,21 @@ * A null array will return null. * An empty array will return itself. * A null array entry will be ignored.

      - * + * *
            * StringUtils.stripAll(null)             = null
            * StringUtils.stripAll([])               = []
            * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
            * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
            * 
      - * + * * @param strs the array to remove whitespace from, may be null * @return the stripped Strings, null if null array input */ public static String[] stripAll(String[] strs) { return stripAll(strs, null); } - + /** *

      Strips any of a set of characters from the start and end of every * String in an array.

      @@ -644,7 +609,7 @@ * A null array entry will be ignored. * A null stripChars will strip whitespace as defined by * {@link Character#isWhitespace(char)}.

      - * + * *
            * StringUtils.stripAll(null, *)                = null
            * StringUtils.stripAll([], *)                  = []
      @@ -653,7 +618,7 @@
            * StringUtils.stripAll(["abc  ", null], "yz")  = ["abc  ", null]
            * StringUtils.stripAll(["yabcz", null], "yz")  = ["abc", null]
            * 
      - * + * * @param strs the array to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped Strings, null if null array input @@ -668,7 +633,7 @@ newArr[i] = strip(strs[i], stripChars); } return newArr; - } + } // Equals //----------------------------------------------------------------------- @@ -685,15 +650,15 @@ * StringUtils.equals("abc", "abc") = true * StringUtils.equals("abc", "ABC") = false *
      - * + * * @see java.lang.String#equals(Object) * @param str1 the first String, may be null * @param str2 the second String, may be null * @return true if the Strings are equal, case sensitive, or * both null */ public static boolean equals(String str1, String str2) { - return (str1 == null ? str2 == null : str1.equals(str2)); + return str1 == null ? str2 == null : str1.equals(str2); } /** @@ -710,15 +675,15 @@ * StringUtils.equalsIgnoreCase("abc", "abc") = true * StringUtils.equalsIgnoreCase("abc", "ABC") = true *
      - * + * * @see java.lang.String#equalsIgnoreCase(String) * @param str1 the first String, may be null * @param str2 the second String, may be null * @return true if the Strings are equal, case insensitive, or * both null */ public static boolean equalsIgnoreCase(String str1, String str2) { - return (str1 == null ? str2 == null : str1.equalsIgnoreCase(str2)); + return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2); } // IndexOf @@ -727,37 +692,37 @@ *

      Finds the first index within a String, handling null. * This method uses {@link String#indexOf(int)}.

      * - *

      A null or empty ("") String will return -1.

      - * + *

      A null or empty ("") String will return INDEX_NOT_FOUND (-1).

      + * *
            * StringUtils.indexOf(null, *)         = -1
            * StringUtils.indexOf("", *)           = -1
            * StringUtils.indexOf("aabaabaa", 'a') = 0
            * StringUtils.indexOf("aabaabaa", 'b') = 2
            * 
      - * + * * @param str the String to check, may be null * @param searchChar the character to find - * @return the first index of the search character, + * @return the first index of the search character, * -1 if no match or null string input * @since 2.0 */ public static int indexOf(String str, char searchChar) { - if (str == null || str.length() == 0) { - return -1; + if (isEmpty(str)) { + return INDEX_NOT_FOUND; } return str.indexOf(searchChar); } - + /** *

      Finds the first index within a String from a start position, * handling null. * This method uses {@link String#indexOf(int, int)}.

      * - *

      A null or empty ("") String will return -1. + *

      A null or empty ("") String will return (INDEX_NOT_FOUND) -1. * A negative start position is treated as zero. * A start position greater than the string length returns -1.

      - * + * *
            * StringUtils.indexOf(null, *, *)          = -1
            * StringUtils.indexOf("", *, *)            = -1
      @@ -766,37 +731,38 @@
            * StringUtils.indexOf("aabaabaa", 'b', 9)  = -1
            * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
            * 
      - * + * * @param str the String to check, may be null * @param searchChar the character to find * @param startPos the start position, negative treated as zero - * @return the first index of the search character, + * @return the first index of the search character, * -1 if no match or null string input * @since 2.0 */ public static int indexOf(String str, char searchChar, int startPos) { - if (str == null || str.length() == 0) { - return -1; + if (isEmpty(str)) { + return INDEX_NOT_FOUND; } return str.indexOf(searchChar, startPos); } - + /** *

      Finds the first index within a String, handling null. * This method uses {@link String#indexOf(String)}.

      * *

      A null String will return -1.

      - * + * *
            * StringUtils.indexOf(null, *)          = -1
            * StringUtils.indexOf(*, null)          = -1
            * StringUtils.indexOf("", "")           = 0
      +     * StringUtils.indexOf("", *)            = -1 (except when * = "")
            * StringUtils.indexOf("aabaabaa", "a")  = 0
            * StringUtils.indexOf("aabaabaa", "b")  = 2
            * StringUtils.indexOf("aabaabaa", "ab") = 1
            * StringUtils.indexOf("aabaabaa", "")   = 0
            * 
      - * + * * @param str the String to check, may be null * @param searchStr the String to find, may be null * @return the first index of the search String, @@ -805,12 +771,86 @@ */ public static int indexOf(String str, String searchStr) { if (str == null || searchStr == null) { - return -1; + return INDEX_NOT_FOUND; } return str.indexOf(searchStr); } - + /** + *

      Finds the n-th index within a String, handling null. + * This method uses {@link String#indexOf(String)}.

      + * + *

      A null String will return -1.

      + * + *
      +     * StringUtils.ordinalIndexOf(null, *, *)          = -1
      +     * StringUtils.ordinalIndexOf(*, null, *)          = -1
      +     * StringUtils.ordinalIndexOf("", "", *)           = 0
      +     * StringUtils.ordinalIndexOf("aabaabaa", "a", 1)  = 0
      +     * StringUtils.ordinalIndexOf("aabaabaa", "a", 2)  = 1
      +     * StringUtils.ordinalIndexOf("aabaabaa", "b", 1)  = 2
      +     * StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  = 5
      +     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
      +     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
      +     * StringUtils.ordinalIndexOf("aabaabaa", "", 1)   = 0
      +     * StringUtils.ordinalIndexOf("aabaabaa", "", 2)   = 0
      +     * 
      + * + *

      Note that 'head(String str, int n)' may be implemented as:

      + * + *
      +     *   str.substring(0, lastOrdinalIndexOf(str, "\n", n))
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @param ordinal the n-th searchStr to find + * @return the n-th index of the search String, + * -1 (INDEX_NOT_FOUND) if no match or null string input + * @since 2.1 + */ + public static int ordinalIndexOf(String str, String searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, false); + } + + /** + *

      Finds the n-th index within a String, handling null. + * This method uses {@link String#indexOf(String)}.

      + * + *

      A null String will return -1.

      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @param ordinal the n-th searchStr to find + * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf() + * @return the n-th index of the search String, + * -1 (INDEX_NOT_FOUND) if no match or null string input + */ + // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int) + private static int ordinalIndexOf(String str, String searchStr, int ordinal, boolean lastIndex) { + if (str == null || searchStr == null || ordinal <= 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return lastIndex ? str.length() : 0; + } + int found = 0; + int index = lastIndex ? str.length() : INDEX_NOT_FOUND; + do { + if(lastIndex) { + index = str.lastIndexOf(searchStr, index - 1); + } else { + index = str.indexOf(searchStr, index + 1); + } + if (index < 0) { + return index; + } + found++; + } while (found < ordinal); + return index; + } + + /** *

      Finds the first index within a String, handling null. * This method uses {@link String#indexOf(String, int)}.

      * @@ -819,11 +859,12 @@ * An empty ("") search String always matches. * A start position greater than the string length only matches * an empty search String.

      - * + * *
            * StringUtils.indexOf(null, *, *)          = -1
            * StringUtils.indexOf(*, null, *)          = -1
            * StringUtils.indexOf("", "", 0)           = 0
      +     * StringUtils.indexOf("", *, 0)            = -1 (except when * = "")
            * StringUtils.indexOf("aabaabaa", "a", 0)  = 0
            * StringUtils.indexOf("aabaabaa", "b", 0)  = 2
            * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
      @@ -833,7 +874,7 @@
            * StringUtils.indexOf("aabaabaa", "", 2)   = 2
            * StringUtils.indexOf("abc", "", 9)        = 3
            * 
      - * + * * @param str the String to check, may be null * @param searchStr the String to find, may be null * @param startPos the start position, negative treated as zero @@ -843,43 +884,124 @@ */ public static int indexOf(String str, String searchStr, int startPos) { if (str == null || searchStr == null) { - return -1; + return INDEX_NOT_FOUND; } // JDK1.2/JDK1.3 have a bug, when startPos > str.length for "", hence if (searchStr.length() == 0 && startPos >= str.length()) { return str.length(); } return str.indexOf(searchStr, startPos); } - + + /** + *

      Case in-sensitive find of the first index within a String.

      + * + *

      A null String will return -1. + * A negative start position is treated as zero. + * An empty ("") search String always matches. + * A start position greater than the string length only matches + * an empty search String.

      + * + *
      +     * StringUtils.indexOfIgnoreCase(null, *)          = -1
      +     * StringUtils.indexOfIgnoreCase(*, null)          = -1
      +     * StringUtils.indexOfIgnoreCase("", "")           = 0
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "a")  = 0
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "b")  = 2
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return the first index of the search String, + * -1 if no match or null string input + * @since 2.5 + */ + public static int indexOfIgnoreCase(String str, String searchStr) { + return indexOfIgnoreCase(str, searchStr, 0); + } + + /** + *

      Case in-sensitive find of the first index within a String + * from the specified position.

      + * + *

      A null String will return -1. + * A negative start position is treated as zero. + * An empty ("") search String always matches. + * A start position greater than the string length only matches + * an empty search String.

      + * + *
      +     * StringUtils.indexOfIgnoreCase(null, *, *)          = -1
      +     * StringUtils.indexOfIgnoreCase(*, null, *)          = -1
      +     * StringUtils.indexOfIgnoreCase("", "", 0)           = 0
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
      +     * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
      +     * StringUtils.indexOfIgnoreCase("abc", "", 9)        = 3
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search String, + * -1 if no match or null string input + * @since 2.5 + */ + public static int indexOfIgnoreCase(String str, String searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos < 0) { + startPos = 0; + } + int endLimit = (str.length() - searchStr.length()) + 1; + if (startPos > endLimit) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + for (int i = startPos; i < endLimit; i++) { + if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + // LastIndexOf //----------------------------------------------------------------------- /** *

      Finds the last index within a String, handling null. * This method uses {@link String#lastIndexOf(int)}.

      * *

      A null or empty ("") String will return -1.

      - * + * *
            * StringUtils.lastIndexOf(null, *)         = -1
            * StringUtils.lastIndexOf("", *)           = -1
            * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
            * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
            * 
      - * + * * @param str the String to check, may be null * @param searchChar the character to find - * @return the last index of the search character, + * @return the last index of the search character, * -1 if no match or null string input * @since 2.0 */ public static int lastIndexOf(String str, char searchChar) { - if (str == null || str.length() == 0) { - return -1; + if (isEmpty(str)) { + return INDEX_NOT_FOUND; } return str.lastIndexOf(searchChar); } - + /** *

      Finds the last index within a String from a start position, * handling null. @@ -888,7 +1010,7 @@ *

      A null or empty ("") String will return -1. * A negative start position returns -1. * A start position greater than the string length searches the whole string.

      - * + * *
            * StringUtils.lastIndexOf(null, *, *)          = -1
            * StringUtils.lastIndexOf("", *,  *)           = -1
      @@ -899,37 +1021,37 @@
            * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
            * StringUtils.lastIndexOf("aabaabaa", 'a', 0)  = 0
            * 
      - * + * * @param str the String to check, may be null * @param searchChar the character to find * @param startPos the start position - * @return the last index of the search character, + * @return the last index of the search character, * -1 if no match or null string input * @since 2.0 */ public static int lastIndexOf(String str, char searchChar, int startPos) { - if (str == null || str.length() == 0) { - return -1; + if (isEmpty(str)) { + return INDEX_NOT_FOUND; } return str.lastIndexOf(searchChar, startPos); } - + /** *

      Finds the last index within a String, handling null. * This method uses {@link String#lastIndexOf(String)}.

      * *

      A null String will return -1.

      - * + * *
            * StringUtils.lastIndexOf(null, *)          = -1
            * StringUtils.lastIndexOf(*, null)          = -1
            * StringUtils.lastIndexOf("", "")           = 0
      -     * StringUtils.lastIndexOf("aabaabaa", "a")  = 0
      -     * StringUtils.lastIndexOf("aabaabaa", "b")  = 2
      -     * StringUtils.lastIndexOf("aabaabaa", "ab") = 1
      +     * StringUtils.lastIndexOf("aabaabaa", "a")  = 7
      +     * StringUtils.lastIndexOf("aabaabaa", "b")  = 5
      +     * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
            * StringUtils.lastIndexOf("aabaabaa", "")   = 8
            * 
      - * + * * @param str the String to check, may be null * @param searchStr the String to find, may be null * @return the last index of the search String, @@ -938,20 +1060,57 @@ */ public static int lastIndexOf(String str, String searchStr) { if (str == null || searchStr == null) { - return -1; + return INDEX_NOT_FOUND; } return str.lastIndexOf(searchStr); } - + /** + *

      Finds the n-th last index within a String, handling null. + * This method uses {@link String#lastIndexOf(String)}.

      + * + *

      A null String will return -1.

      + * + *
      +     * StringUtils.lastOrdinalIndexOf(null, *, *)          = -1
      +     * StringUtils.lastOrdinalIndexOf(*, null, *)          = -1
      +     * StringUtils.lastOrdinalIndexOf("", "", *)           = 0
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
      +     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
      +     * 
      + * + *

      Note that 'tail(String str, int n)' may be implemented as:

      + * + *
      +     *   str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @param ordinal the n-th last searchStr to find + * @return the n-th last index of the search String, + * -1 (INDEX_NOT_FOUND) if no match or null string input + * @since 2.5 + */ + public static int lastOrdinalIndexOf(String str, String searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, true); + } + + /** *

      Finds the first index within a String, handling null. * This method uses {@link String#lastIndexOf(String, int)}.

      * *

      A null String will return -1. * A negative start position returns -1. * An empty ("") search String always matches unless the start position is negative. * A start position greater than the string length searches the whole string.

      - * + * *
            * StringUtils.lastIndexOf(null, *, *)          = -1
            * StringUtils.lastIndexOf(*, null, *)          = -1
      @@ -963,7 +1122,7 @@
            * StringUtils.lastIndexOf("aabaabaa", "a", 0)  = 0
            * StringUtils.lastIndexOf("aabaabaa", "b", 0)  = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchStr the String to find, may be null * @param startPos the start position, negative treated as zero @@ -973,45 +1132,124 @@ */ public static int lastIndexOf(String str, String searchStr, int startPos) { if (str == null || searchStr == null) { - return -1; + return INDEX_NOT_FOUND; } return str.lastIndexOf(searchStr, startPos); } - + + /** + *

      Case in-sensitive find of the last index within a String.

      + * + *

      A null String will return -1. + * A negative start position returns -1. + * An empty ("") search String always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.

      + * + *
      +     * StringUtils.lastIndexOfIgnoreCase(null, *)          = -1
      +     * StringUtils.lastIndexOfIgnoreCase(*, null)          = -1
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return the first index of the search String, + * -1 if no match or null string input + * @since 2.5 + */ + public static int lastIndexOfIgnoreCase(String str, String searchStr) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + return lastIndexOfIgnoreCase(str, searchStr, str.length()); + } + + /** + *

      Case in-sensitive find of the last index within a String + * from the specified position.

      + * + *

      A null String will return -1. + * A negative start position returns -1. + * An empty ("") search String always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.

      + * + *
      +     * StringUtils.lastIndexOfIgnoreCase(null, *, *)          = -1
      +     * StringUtils.lastIndexOfIgnoreCase(*, null, *)          = -1
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
      +     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @param startPos the start position + * @return the first index of the search String, + * -1 if no match or null string input + * @since 2.5 + */ + public static int lastIndexOfIgnoreCase(String str, String searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos > (str.length() - searchStr.length())) { + startPos = str.length() - searchStr.length(); + } + if (startPos < 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + + for (int i = startPos; i >= 0; i--) { + if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + // Contains //----------------------------------------------------------------------- /** *

      Checks if String contains a search character, handling null. * This method uses {@link String#indexOf(int)}.

      * *

      A null or empty ("") String will return false.

      - * + * *
            * StringUtils.contains(null, *)    = false
            * StringUtils.contains("", *)      = false
            * StringUtils.contains("abc", 'a') = true
            * StringUtils.contains("abc", 'z') = false
            * 
      - * + * * @param str the String to check, may be null * @param searchChar the character to find - * @return true if the String contains the search character, + * @return true if the String contains the search character, * false if not or null string input * @since 2.0 */ public static boolean contains(String str, char searchChar) { - if (str == null || str.length() == 0) { + if (isEmpty(str)) { return false; } - return (str.indexOf(searchChar) >= 0); + return str.indexOf(searchChar) >= 0; } - + /** - *

      Find the first index within a String, handling null. - * This method uses {@link String#indexOf(int)}.

      + *

      Checks if String contains a search String, handling null. + * This method uses {@link String#indexOf(String)}.

      * *

      A null String will return false.

      - * + * *
            * StringUtils.contains(null, *)     = false
            * StringUtils.contains(*, null)     = false
      @@ -1020,20 +1258,57 @@
            * StringUtils.contains("abc", "a")  = true
            * StringUtils.contains("abc", "z")  = false
            * 
      - * + * * @param str the String to check, may be null * @param searchStr the String to find, may be null - * @return true if the String contains the search character, + * @return true if the String contains the search String, * false if not or null string input * @since 2.0 */ public static boolean contains(String str, String searchStr) { if (str == null || searchStr == null) { return false; } - return (str.indexOf(searchStr) >= 0); + return str.indexOf(searchStr) >= 0; } - + + /** + *

      Checks if String contains a search String irrespective of case, + * handling null. Case-insensitivity is defined as by + * {@link String#equalsIgnoreCase(String)}. + * + *

      A null String will return false.

      + * + *
      +     * StringUtils.contains(null, *) = false
      +     * StringUtils.contains(*, null) = false
      +     * StringUtils.contains("", "") = true
      +     * StringUtils.contains("abc", "") = true
      +     * StringUtils.contains("abc", "a") = true
      +     * StringUtils.contains("abc", "z") = false
      +     * StringUtils.contains("abc", "A") = true
      +     * StringUtils.contains("abc", "Z") = false
      +     * 
      + * + * @param str the String to check, may be null + * @param searchStr the String to find, may be null + * @return true if the String contains the search String irrespective of + * case or false if not or null string input + */ + public static boolean containsIgnoreCase(String str, String searchStr) { + if (str == null || searchStr == null) { + return false; + } + int len = searchStr.length(); + int max = str.length() - len; + for (int i = 0; i <= max; i++) { + if (str.regionMatches(true, i, searchStr, 0, len)) { + return true; + } + } + return false; + } + // IndexOfAny chars //----------------------------------------------------------------------- /** @@ -1042,7 +1317,7 @@ * *

      A null String will return -1. * A null or zero length search array will return -1.

      - * + * *
            * StringUtils.indexOfAny(null, *)                = -1
            * StringUtils.indexOfAny("", *)                  = -1
      @@ -1052,34 +1327,45 @@
            * StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3
            * StringUtils.indexOfAny("aba", ['z'])           = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchChars the chars to search for, may be null * @return the index of any of the chars, -1 if no match or null input * @since 2.0 */ - public static int indexOfAny(String str, char[] searchChars) { - if (str == null || str.length() == 0 || searchChars == null || searchChars.length == 0) { - return -1; - } - for (int i = 0; i < str.length(); i ++) { - char ch = str.charAt(i); - for (int j = 0; j < searchChars.length; j++) { - if (searchChars[j] == ch) { - return i; - } - } - } - return -1; - } + public static int indexOfAny(String str, char[] searchChars) { + if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = str.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { + char ch = str.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) { + // ch is a supplementary character + if (searchChars[j + 1] == str.charAt(i + 1)) { + return i; + } + } else { + return i; + } + } + } + } + return INDEX_NOT_FOUND; + } /** *

      Search a String to find the first index of any * character in the given set of characters.

      * *

      A null String will return -1. * A null search string will return -1.

      - * + * *
            * StringUtils.indexOfAny(null, *)            = -1
            * StringUtils.indexOfAny("", *)              = -1
      @@ -1089,19 +1375,108 @@
            * StringUtils.indexOfAny("zzabyycdxx", "by") = 3
            * StringUtils.indexOfAny("aba","z")          = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchChars the chars to search for, may be null * @return the index of any of the chars, -1 if no match or null input * @since 2.0 */ public static int indexOfAny(String str, String searchChars) { - if (str == null || str.length() == 0 || searchChars == null || searchChars.length() == 0) { - return -1; + if (isEmpty(str) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; } return indexOfAny(str, searchChars.toCharArray()); } + // ContainsAny + //----------------------------------------------------------------------- + /** + *

      Checks if the String contains any character in the given + * set of characters.

      + * + *

      A null String will return false. + * A null or zero length search array will return false.

      + * + *
      +     * StringUtils.containsAny(null, *)                = false
      +     * StringUtils.containsAny("", *)                  = false
      +     * StringUtils.containsAny(*, null)                = false
      +     * StringUtils.containsAny(*, [])                  = false
      +     * StringUtils.containsAny("zzabyycdxx",['z','a']) = true
      +     * StringUtils.containsAny("zzabyycdxx",['b','y']) = true
      +     * StringUtils.containsAny("aba", ['z'])           = false
      +     * 
      + * + * @param str the String to check, may be null + * @param searchChars the chars to search for, may be null + * @return the true if any of the chars are found, + * false if no match or null input + * @since 2.4 + */ + public static boolean containsAny(String str, char[] searchChars) { + if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { + return false; + } + int csLength = str.length(); + int searchLength = searchChars.length; + int csLast = csLength - 1; + int searchLast = searchLength - 1; + for (int i = 0; i < csLength; i++) { + char ch = str.charAt(i); + for (int j = 0; j < searchLength; j++) { + if (searchChars[j] == ch) { + if (CharUtils.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return true; + } + if (i < csLast && searchChars[j + 1] == str.charAt(i + 1)) { + return true; + } + } else { + // ch is in the Basic Multilingual Plane + return true; + } + } + } + } + return false; + } + + /** + *

      + * Checks if the String contains any character in the given set of characters. + *

      + * + *

      + * A null String will return false. A null search string will return + * false. + *

      + * + *
      +     * StringUtils.containsAny(null, *)            = false
      +     * StringUtils.containsAny("", *)              = false
      +     * StringUtils.containsAny(*, null)            = false
      +     * StringUtils.containsAny(*, "")              = false
      +     * StringUtils.containsAny("zzabyycdxx", "za") = true
      +     * StringUtils.containsAny("zzabyycdxx", "by") = true
      +     * StringUtils.containsAny("aba","z")          = false
      +     * 
      + * + * @param str + * the String to check, may be null + * @param searchChars + * the chars to search for, may be null + * @return the true if any of the chars are found, false if no match or null input + * @since 2.4 + */ + public static boolean containsAny(String str, String searchChars) { + if (searchChars == null) { + return false; + } + return containsAny(str, searchChars.toCharArray()); + } + // IndexOfAnyBut chars //----------------------------------------------------------------------- /** @@ -1110,70 +1485,91 @@ * *

      A null String will return -1. * A null or zero length search array will return -1.

      - * + * *
      -     * StringUtils.indexOfAnyBut(null, *)           = -1
      -     * StringUtils.indexOfAnyBut("", *)             = -1
      -     * StringUtils.indexOfAnyBut(*, null)           = -1
      -     * StringUtils.indexOfAnyBut(*, [])             = -1
      -     * StringUtils.indexOfAnyBut("zzabyycdxx",'za') = 3
      -     * StringUtils.indexOfAnyBut("zzabyycdxx", '')  = 0
      -     * StringUtils.indexOfAnyBut("aba", 'ab')       = -1
      +     * StringUtils.indexOfAnyBut(null, *)                              = -1
      +     * StringUtils.indexOfAnyBut("", *)                                = -1
      +     * StringUtils.indexOfAnyBut(*, null)                              = -1
      +     * StringUtils.indexOfAnyBut(*, [])                                = -1
      +     * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
      +     * StringUtils.indexOfAnyBut("aba", new char[] {'z'} )             = 0
      +     * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} )        = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchChars the chars to search for, may be null * @return the index of any of the chars, -1 if no match or null input * @since 2.0 */ - public static int indexOfAnyBut(String str, char[] searchChars) { - if (str == null || str.length() == 0 || searchChars == null || searchChars.length == 0) { - return -1; - } - outer: for (int i = 0; i < str.length(); i ++) { - char ch = str.charAt(i); - for (int j = 0; j < searchChars.length; j++) { - if (searchChars[j] == ch) { - continue outer; - } - } - return i; - } - return -1; - } + public static int indexOfAnyBut(String str, char[] searchChars) { + if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = str.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + outer: + for (int i = 0; i < csLen; i++) { + char ch = str.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && CharUtils.isHighSurrogate(ch)) { + if (searchChars[j + 1] == str.charAt(i + 1)) { + continue outer; + } + } else { + continue outer; + } + } + } + return i; + } + return INDEX_NOT_FOUND; + } /** *

      Search a String to find the first index of any * character not in the given set of characters.

      * *

      A null String will return -1. - * A null search string will return -1.

      - * + * A null or empty search string will return -1.

      + * *
            * StringUtils.indexOfAnyBut(null, *)            = -1
            * StringUtils.indexOfAnyBut("", *)              = -1
            * StringUtils.indexOfAnyBut(*, null)            = -1
            * StringUtils.indexOfAnyBut(*, "")              = -1
            * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3
      -     * StringUtils.indexOfAnyBut("zzabyycdxx", "")   = 0
      +     * StringUtils.indexOfAnyBut("zzabyycdxx", "")   = -1
            * StringUtils.indexOfAnyBut("aba","ab")         = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchChars the chars to search for, may be null * @return the index of any of the chars, -1 if no match or null input * @since 2.0 */ public static int indexOfAnyBut(String str, String searchChars) { - if (str == null || str.length() == 0 || searchChars == null || searchChars.length() == 0) { - return -1; + if (isEmpty(str) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; } - for (int i = 0; i < str.length(); i++) { - if (searchChars.indexOf(str.charAt(i)) < 0) { - return i; + int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + char ch = str.charAt(i); + boolean chFound = searchChars.indexOf(ch) >= 0; + if (i + 1 < strLen && CharUtils.isHighSurrogate(ch)) { + char ch2 = str.charAt(i + 1); + if (chFound && searchChars.indexOf(ch2) < 0) { + return i; + } + } else { + if (!chFound) { + return i; + } } } - return -1; + return INDEX_NOT_FOUND; } // ContainsOnly @@ -1183,7 +1579,7 @@ * *

      A null String will return false. * A null valid character array will return false. - * An empty String ("") always returns true.

      + * An empty String (length()=0) always returns true.

      * *
            * StringUtils.containsOnly(null, *)       = false
      @@ -1194,14 +1590,14 @@
            * StringUtils.containsOnly("ab1", 'abc')  = false
            * StringUtils.containsOnly("abz", 'abc')  = false
            * 
      - * + * * @param str the String to check, may be null * @param valid an array of valid chars, may be null * @return true if it only contains valid chars and is non-null */ public static boolean containsOnly(String str, char[] valid) { // All these pre-checks are to maintain API with an older version - if ( (valid == null) || (str == null) ) { + if ((valid == null) || (str == null)) { return false; } if (str.length() == 0) { @@ -1210,15 +1606,15 @@ if (valid.length == 0) { return false; } - return indexOfAnyBut(str, valid) == -1; + return indexOfAnyBut(str, valid) == INDEX_NOT_FOUND; } /** *

      Checks if the String contains only certain characters.

      * *

      A null String will return false. * A null valid character String will return false. - * An empty String ("") always returns true.

      + * An empty String (length()=0) always returns true.

      * *
            * StringUtils.containsOnly(null, *)       = false
      @@ -1229,7 +1625,7 @@
            * StringUtils.containsOnly("ab1", "abc")  = false
            * StringUtils.containsOnly("abz", "abc")  = false
            * 
      - * + * * @param str the String to check, may be null * @param validChars a String of valid chars, may be null * @return true if it only contains valid chars and is non-null @@ -1241,15 +1637,15 @@ } return containsOnly(str, validChars.toCharArray()); } - + // ContainsNone //----------------------------------------------------------------------- /** *

      Checks that the String does not contain certain characters.

      * *

      A null String will return true. * A null invalid character array will return true. - * An empty String ("") always returns true.

      + * An empty String (length()=0) always returns true.

      * *
            * StringUtils.containsNone(null, *)       = true
      @@ -1260,23 +1656,36 @@
            * StringUtils.containsNone("ab1", 'xyz')  = true
            * StringUtils.containsNone("abz", 'xyz')  = false
            * 
      - * + * * @param str the String to check, may be null - * @param invalidChars an array of invalid chars, may be null + * @param searchChars an array of invalid chars, may be null * @return true if it contains none of the invalid chars, or is null * @since 2.0 */ - public static boolean containsNone(String str, char[] invalidChars) { - if (str == null || invalidChars == null) { + public static boolean containsNone(String str, char[] searchChars) { + if (str == null || searchChars == null) { return true; } - int strSize = str.length(); - int validSize = invalidChars.length; - for (int i = 0; i < strSize; i++) { + int csLen = str.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { char ch = str.charAt(i); - for (int j = 0; j < validSize; j++) { - if (invalidChars[j] == ch) { - return false; + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (CharUtils.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return false; + } + if (i < csLast && searchChars[j + 1] == str.charAt(i + 1)) { + return false; + } + } else { + // ch is in the Basic Multilingual Plane + return false; + } } } } @@ -1299,7 +1708,7 @@ * StringUtils.containsNone("ab1", "xyz") = true * StringUtils.containsNone("abz", "xyz") = false *
      - * + * * @param str the String to check, may be null * @param invalidChars a String of invalid chars, may be null * @return true if it contains none of the invalid chars, or is null @@ -1311,7 +1720,7 @@ } return containsNone(str, invalidChars.toCharArray()); } - + // IndexOfAny strings //----------------------------------------------------------------------- /** @@ -1322,7 +1731,7 @@ * A null search array entry will be ignored, but a search * array containing "" will return 0 if str is not * null. This method uses {@link String#indexOf(String)}.

      - * + * *
            * StringUtils.indexOfAny(null, *)                     = -1
            * StringUtils.indexOfAny(*, null)                     = -1
      @@ -1335,14 +1744,14 @@
            * StringUtils.indexOfAny("", [""])                    = 0
            * StringUtils.indexOfAny("", ["a"])                   = -1
            * 
      - * + * * @param str the String to check, may be null * @param searchStrs the Strings to search for, may be null * @return the first index of any of the searchStrs in str, -1 if no match */ public static int indexOfAny(String str, String[] searchStrs) { if ((str == null) || (searchStrs == null)) { - return -1; + return INDEX_NOT_FOUND; } int sz = searchStrs.length; @@ -1356,7 +1765,7 @@ continue; } tmp = str.indexOf(search); - if (tmp == -1) { + if (tmp == INDEX_NOT_FOUND) { continue; } @@ -1365,7 +1774,7 @@ } } - return (ret == Integer.MAX_VALUE) ? -1 : ret; + return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret; } /** @@ -1374,9 +1783,9 @@ *

      A null String will return -1. * A null search array will return -1. * A null or zero length search array entry will be ignored, - * but a search array containing "" will return the length of str + * but a search array containing "" will return the length of str * if str is not null. This method uses {@link String#indexOf(String)}

      - * + * *
            * StringUtils.lastIndexOfAny(null, *)                   = -1
            * StringUtils.lastIndexOfAny(*, null)                   = -1
      @@ -1388,17 +1797,17 @@
            * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
            * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""])   = 10
            * 
      - * + * * @param str the String to check, may be null * @param searchStrs the Strings to search for, may be null * @return the last index of any of the Strings, -1 if no match */ public static int lastIndexOfAny(String str, String[] searchStrs) { if ((str == null) || (searchStrs == null)) { - return -1; + return INDEX_NOT_FOUND; } int sz = searchStrs.length; - int ret = -1; + int ret = INDEX_NOT_FOUND; int tmp = 0; for (int i = 0; i < sz; i++) { String search = searchStrs[i]; @@ -1423,7 +1832,7 @@ * *

      A null String will return null. * An empty ("") String will return "".

      - * + * *
            * StringUtils.substring(null, *)   = null
            * StringUtils.substring("", *)     = ""
      @@ -1433,7 +1842,7 @@
            * StringUtils.substring("abc", -2) = "bc"
            * StringUtils.substring("abc", -4) = "abc"
            * 
      - * + * * @param str the String to get the substring from, may be null * @param start the position to start from, negative means * count back from the end of the String by this many characters @@ -1458,22 +1867,22 @@ return str.substring(start); } - + /** *

      Gets a substring from the specified String avoiding exceptions.

      * *

      A negative start position can be used to start/end n * characters from the end of the String.

      * *

      The returned substring starts with the character in the start - * position and ends before the end position. All postion counting is - * zero-based -- i.e., to start at the beginning of the string use - * start = 0. Negative start and end positions can be used to + * position and ends before the end position. All position counting is + * zero-based -- i.e., to start at the beginning of the string use + * start = 0. Negative start and end positions can be used to * specify offsets relative to the end of the String.

      * *

      If start is not strictly to the left of end, "" * is returned.

      - * + * *
            * StringUtils.substring(null, *, *)    = null
            * StringUtils.substring("", * ,  *)    = "";
      @@ -1485,7 +1894,7 @@
            * StringUtils.substring("abc", -2, -1) = "b"
            * StringUtils.substring("abc", -4, 2)  = "ab"
            * 
      - * + * * @param str the String to get the substring from, may be null * @param start the position to start from, negative means * count back from the end of the String by this many characters @@ -1534,7 +1943,7 @@ * *

      If len characters are not available, or the * String is null, the String will be returned without - * an exception. An exception is thrown if len is negative.

      + * an exception. An empty String is returned if len is negative.

      * *
            * StringUtils.left(null, *)    = null
      @@ -1544,9 +1953,9 @@
            * StringUtils.left("abc", 2)   = "ab"
            * StringUtils.left("abc", 4)   = "abc"
            * 
      - * + * * @param str the String to get the leftmost characters from, may be null - * @param len the length of the required String, must be zero or positive + * @param len the length of the required String * @return the leftmost characters, null if null String input */ public static String left(String str, int len) { @@ -1558,17 +1967,16 @@ } if (str.length() <= len) { return str; - } else { - return str.substring(0, len); } + return str.substring(0, len); } /** *

      Gets the rightmost len characters of a String.

      * *

      If len characters are not available, or the String * is null, the String will be returned without an - * an exception. An exception is thrown if len is negative.

      + * an exception. An empty String is returned if len is negative.

      * *
            * StringUtils.right(null, *)    = null
      @@ -1578,9 +1986,9 @@
            * StringUtils.right("abc", 2)   = "bc"
            * StringUtils.right("abc", 4)   = "abc"
            * 
      - * + * * @param str the String to get the rightmost characters from, may be null - * @param len the length of the required String, must be zero or positive + * @param len the length of the required String * @return the rightmost characters, null if null String input */ public static String right(String str, int len) { @@ -1592,9 +2000,8 @@ } if (str.length() <= len) { return str; - } else { - return str.substring(str.length() - len); } + return str.substring(str.length() - len); } /** @@ -1603,7 +2010,8 @@ *

      If len characters are not available, the remainder * of the String will be returned without an exception. If the * String is null, null will be returned. - * An exception is thrown if len is negative.

      + * An empty String is returned if len is negative or exceeds the + * length of str.

      * *
            * StringUtils.mid(null, *, *)    = null
      @@ -1615,10 +2023,10 @@
            * StringUtils.mid("abc", 4, 2)   = ""
            * StringUtils.mid("abc", -2, 2)  = "ab"
            * 
      - * + * * @param str the String to get the characters from, may be null * @param pos the position to start from, negative treated as zero - * @param len the length of the required String, must be zero or positive + * @param len the length of the required String * @return the middle characters, null if null String input */ public static String mid(String str, int pos, int len) { @@ -1633,21 +2041,22 @@ } if (str.length() <= (pos + len)) { return str.substring(pos); - } else { - return str.substring(pos, pos + len); } + return str.substring(pos, pos + len); } // SubStringAfter/SubStringBefore //----------------------------------------------------------------------- /** - *

      Gets the substring before the first occurance of a separator. + *

      Gets the substring before the first occurrence of a separator. * The separator is not returned.

      * *

      A null string input will return null. * An empty ("") string input will return the empty string. * A null separator will return the input string.

      * + *

      If nothing is found, the string input is returned.

      + * *
            * StringUtils.substringBefore(null, *)      = null
            * StringUtils.substringBefore("", *)        = ""
      @@ -1661,33 +2070,35 @@
            *
            * @param str  the String to get a substring from, may be null
            * @param separator  the String to search for, may be null
      -     * @return the substring before the first occurance of the separator,
      +     * @return the substring before the first occurrence of the separator,
            *  null if null String input
            * @since 2.0
            */
           public static String substringBefore(String str, String separator) {
      -        if (str == null || separator == null || str.length() == 0) {
      +        if (isEmpty(str) || separator == null) {
                   return str;
               }
               if (separator.length() == 0) {
                   return EMPTY;
               }
               int pos = str.indexOf(separator);
      -        if (pos == -1) {
      +        if (pos == INDEX_NOT_FOUND) {
                   return str;
               }
               return str.substring(0, pos);
           }
       
           /**
      -     * 

      Gets the substring after the first occurance of a separator. + *

      Gets the substring after the first occurrence of a separator. * The separator is not returned.

      * *

      A null string input will return null. * An empty ("") string input will return the empty string. * A null separator will return the empty string if the * input string is not null.

      - * + * + *

      If nothing is found, the empty string is returned.

      + * *
            * StringUtils.substringAfter(null, *)      = null
            * StringUtils.substringAfter("", *)        = ""
      @@ -1701,32 +2112,34 @@
            *
            * @param str  the String to get a substring from, may be null
            * @param separator  the String to search for, may be null
      -     * @return the substring after the first occurance of the separator,
      +     * @return the substring after the first occurrence of the separator,
            *  null if null String input
            * @since 2.0
            */
           public static String substringAfter(String str, String separator) {
      -        if (str == null || str.length() == 0) {
      +        if (isEmpty(str)) {
                   return str;
               }
               if (separator == null) {
                   return EMPTY;
               }
               int pos = str.indexOf(separator);
      -        if (pos == -1) {
      +        if (pos == INDEX_NOT_FOUND) {
                   return EMPTY;
               }
               return str.substring(pos + separator.length());
           }
       
           /**
      -     * 

      Gets the substring before the last occurance of a separator. + *

      Gets the substring before the last occurrence of a separator. * The separator is not returned.

      * *

      A null string input will return null. * An empty ("") string input will return the empty string. * An empty or null separator will return the input string.

      - * + * + *

      If nothing is found, the string input is returned.

      + * *
            * StringUtils.substringBeforeLast(null, *)      = null
            * StringUtils.substringBeforeLast("", *)        = ""
      @@ -1740,30 +2153,32 @@
            *
            * @param str  the String to get a substring from, may be null
            * @param separator  the String to search for, may be null
      -     * @return the substring before the last occurance of the separator,
      +     * @return the substring before the last occurrence of the separator,
            *  null if null String input
            * @since 2.0
            */
           public static String substringBeforeLast(String str, String separator) {
      -        if (str == null || separator == null || str.length() == 0 || separator.length() == 0) {
      +        if (isEmpty(str) || isEmpty(separator)) {
                   return str;
               }
               int pos = str.lastIndexOf(separator);
      -        if (pos == -1) {
      +        if (pos == INDEX_NOT_FOUND) {
                   return str;
               }
               return str.substring(0, pos);
           }
       
           /**
      -     * 

      Gets the substring after the last occurance of a separator. + *

      Gets the substring after the last occurrence of a separator. * The separator is not returned.

      * *

      A null string input will return null. * An empty ("") string input will return the empty string. * An empty or null separator will return the empty string if * the input string is not null.

      * + *

      If nothing is found, the empty string is returned.

      + * *
            * StringUtils.substringAfterLast(null, *)      = null
            * StringUtils.substringAfterLast("", *)        = ""
      @@ -1778,19 +2193,19 @@
            *
            * @param str  the String to get a substring from, may be null
            * @param separator  the String to search for, may be null
      -     * @return the substring after the last occurance of the separator,
      +     * @return the substring after the last occurrence of the separator,
            *  null if null String input
            * @since 2.0
            */
           public static String substringAfterLast(String str, String separator) {
      -        if (str == null || str.length() == 0) {
      +        if (isEmpty(str)) {
                   return str;
               }
      -        if (separator == null || separator.length() == 0) {
      +        if (isEmpty(separator)) {
                   return EMPTY;
               }
               int pos = str.lastIndexOf(separator);
      -        if (pos == -1 || pos == (str.length() - separator.length())) {
      +        if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) {
                   return EMPTY;
               }
               return str.substring(pos + separator.length());
      @@ -1804,7 +2219,7 @@
            *
            * 

      A null input String returns null. * A null tag returns null.

      - * + * *
            * StringUtils.substringBetween(null, *)            = null
            * StringUtils.substringBetween("", "")             = ""
      @@ -1822,21 +2237,23 @@
           public static String substringBetween(String str, String tag) {
               return substringBetween(str, tag, tag);
           }
      -    
      +
           /**
            * 

      Gets the String that is nested in between two Strings. * Only the first match is returned.

      - * + * *

      A null input String returns null. * A null open/close returns null (no match). - * An empty ("") open/close returns an empty string.

      + * An empty ("") open and close returns an empty string.

      * *
      +     * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
            * StringUtils.substringBetween(null, *, *)          = null
      +     * StringUtils.substringBetween(*, null, *)          = null
      +     * StringUtils.substringBetween(*, *, null)          = null
            * StringUtils.substringBetween("", "", "")          = ""
      -     * StringUtils.substringBetween("", "", "tag")       = null
      -     * StringUtils.substringBetween("", "tag", "tag")    = null
      -     * StringUtils.substringBetween("yabcz", null, null) = null
      +     * StringUtils.substringBetween("", "", "]")         = null
      +     * StringUtils.substringBetween("", "[", "]")        = null
            * StringUtils.substringBetween("yabcz", "", "")     = ""
            * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
            * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
      @@ -1853,15 +2270,68 @@
                   return null;
               }
               int start = str.indexOf(open);
      -        if (start != -1) {
      +        if (start != INDEX_NOT_FOUND) {
                   int end = str.indexOf(close, start + open.length());
      -            if (end != -1) {
      +            if (end != INDEX_NOT_FOUND) {
                       return str.substring(start + open.length(), end);
                   }
               }
               return null;
           }
       
      +    /**
      +     * 

      Searches a String for substrings delimited by a start and end tag, + * returning all matching substrings in an array.

      + * + *

      A null input String returns null. + * A null open/close returns null (no match). + * An empty ("") open/close returns null (no match).

      + * + *
      +     * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
      +     * StringUtils.substringsBetween(null, *, *)            = null
      +     * StringUtils.substringsBetween(*, null, *)            = null
      +     * StringUtils.substringsBetween(*, *, null)            = null
      +     * StringUtils.substringsBetween("", "[", "]")          = []
      +     * 
      + * + * @param str the String containing the substrings, null returns null, empty returns empty + * @param open the String identifying the start of the substring, empty returns null + * @param close the String identifying the end of the substring, empty returns null + * @return a String Array of substrings, or null if no match + * @since 2.3 + */ + public static String[] substringsBetween(String str, String open, String close) { + if (str == null || isEmpty(open) || isEmpty(close)) { + return null; + } + int strLen = str.length(); + if (strLen == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + int closeLen = close.length(); + int openLen = open.length(); + List list = new ArrayList(); + int pos = 0; + while (pos < (strLen - closeLen)) { + int start = str.indexOf(open, pos); + if (start < 0) { + break; + } + start += openLen; + int end = str.indexOf(close, start); + if (end < 0) { + break; + } + list.add(str.substring(start, end)); + pos = end + closeLen; + } + if (list.isEmpty()) { + return null; + } + return (String[]) list.toArray(new String [list.size()]); + } + // Nested extraction //----------------------------------------------------------------------- /** @@ -1870,7 +2340,7 @@ * *

      A null input String returns null. * A null tag returns null.

      - * + * *
            * StringUtils.getNestedString(null, *)            = null
            * StringUtils.getNestedString("", "")             = ""
      @@ -1889,11 +2359,11 @@
           public static String getNestedString(String str, String tag) {
               return substringBetween(str, tag, tag);
           }
      -    
      +
           /**
            * 

      Gets the String that is nested in between two Strings. * Only the first match is returned.

      - * + * *

      A null input String returns null. * A null open/close returns null (no match). * An empty ("") open/close returns an empty string.

      @@ -1928,8 +2398,9 @@ * Whitespace is defined by {@link Character#isWhitespace(char)}.

      * *

      The separator is not included in the returned String array. - * Adjacent separators are treated as one separator.

      - * + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

      + * *

      A null input String returns null.

      * *
      @@ -1939,7 +2410,7 @@
            * StringUtils.split("abc  def") = ["abc", "def"]
            * StringUtils.split(" abc ")    = ["abc"]
            * 
      - * + * * @param str the String to parse, may be null * @return an array of parsed Strings, null if null String input */ @@ -1952,8 +2423,9 @@ * This is an alternative to using StringTokenizer.

      * *

      The separator is not included in the returned String array. - * Adjacent separators are treated as one separator.

      - * + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

      + * *

      A null input String returns null.

      * *
      @@ -1962,19 +2434,362 @@
            * StringUtils.split("a.b.c", '.')    = ["a", "b", "c"]
            * StringUtils.split("a..b.c", '.')   = ["a", "b", "c"]
            * StringUtils.split("a:b:c", '.')    = ["a:b:c"]
      -     * StringUtils.split("a\tb\nc", null) = ["a", "b", "c"]
            * StringUtils.split("a b c", ' ')    = ["a", "b", "c"]
            * 
      - * + * * @param str the String to parse, may be null - * @param separatorChar the character used as the delimiter, - * null splits on whitespace + * @param separatorChar the character used as the delimiter * @return an array of parsed Strings, null if null String input * @since 2.0 */ public static String[] split(String str, char separatorChar) { + return splitWorker(str, separatorChar, false); + } + + /** + *

      Splits the provided text into an array, separators specified. + * This is an alternative to using StringTokenizer.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

      + * + *

      A null input String returns null. + * A null separatorChars splits on whitespace.

      + * + *
      +     * StringUtils.split(null, *)         = null
      +     * StringUtils.split("", *)           = []
      +     * StringUtils.split("abc def", null) = ["abc", "def"]
      +     * StringUtils.split("abc def", " ")  = ["abc", "def"]
      +     * StringUtils.split("abc  def", " ") = ["abc", "def"]
      +     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * null splits on whitespace + * @return an array of parsed Strings, null if null String input + */ + public static String[] split(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, false); + } + + /** + *

      Splits the provided text into an array with a maximum length, + * separators specified.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as one separator.

      + * + *

      A null input String returns null. + * A null separatorChars splits on whitespace.

      + * + *

      If more than max delimited substrings are found, the last + * returned string includes all characters after the first max - 1 + * returned strings (including separator characters).

      + * + *
      +     * StringUtils.split(null, *, *)            = null
      +     * StringUtils.split("", *, *)              = []
      +     * StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
      +     * StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
      +     * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
      +     * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * null splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, null if null String input + */ + public static String[] split(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, false); + } + + /** + *

      Splits the provided text into an array, separator string specified.

      + * + *

      The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.

      + * + *

      A null input String returns null. + * A null separator splits on whitespace.

      + * + *
      +     * StringUtils.splitByWholeSeparator(null, *)               = null
      +     * StringUtils.splitByWholeSeparator("", *)                 = []
      +     * StringUtils.splitByWholeSeparator("ab de fg", null)      = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparator("ab   de fg", null)    = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparator("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
      +     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * null splits on whitespace + * @return an array of parsed Strings, null if null String was input + */ + public static String[] splitByWholeSeparator(String str, String separator) { + return splitByWholeSeparatorWorker( str, separator, -1, false ) ; + } + + /** + *

      Splits the provided text into an array, separator string specified. + * Returns a maximum of max substrings.

      + * + *

      The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.

      + * + *

      A null input String returns null. + * A null separator splits on whitespace.

      + * + *
      +     * StringUtils.splitByWholeSeparator(null, *, *)               = null
      +     * StringUtils.splitByWholeSeparator("", *, *)                 = []
      +     * StringUtils.splitByWholeSeparator("ab de fg", null, 0)      = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparator("ab   de fg", null, 0)    = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
      +     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
      +     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * null splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, null if null String was input + */ + public static String[] splitByWholeSeparator( String str, String separator, int max ) { + return splitByWholeSeparatorWorker(str, separator, max, false); + } + + /** + *

      Splits the provided text into an array, separator string specified.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

      + * + *

      A null input String returns null. + * A null separator splits on whitespace.

      + * + *
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *)               = null
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *)                 = []
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null)      = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null)    = ["ab", "", "", "de", "fg"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * null splits on whitespace + * @return an array of parsed Strings, null if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator) { + return splitByWholeSeparatorWorker(str, separator, -1, true); + } + + /** + *

      Splits the provided text into an array, separator string specified. + * Returns a maximum of max substrings.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

      + * + *

      A null input String returns null. + * A null separator splits on whitespace.

      + * + *
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *)               = null
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *)                 = []
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0)      = ["ab", "de", "fg"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 0)    = ["ab", "", "", "de", "fg"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
      +     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * null splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, null if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator, int max) { + return splitByWholeSeparatorWorker(str, separator, max, true); + } + + /** + * Performs the logic for the splitByWholeSeparatorPreserveAllTokens methods. + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * null splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if true, adjacent separators are + * treated as empty token separators; if false, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, null if null String input + * @since 2.4 + */ + private static String[] splitByWholeSeparatorWorker(String str, String separator, int max, + boolean preserveAllTokens) + { + if (str == null) { + return null; + } + + int len = str.length(); + + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + + if ((separator == null) || (EMPTY.equals(separator))) { + // Split on whitespace. + return splitWorker(str, null, max, preserveAllTokens); + } + + int separatorLength = separator.length(); + + ArrayList substrings = new ArrayList(); + int numberOfSubstrings = 0; + int beg = 0; + int end = 0; + while (end < len) { + end = str.indexOf(separator, beg); + + if (end > -1) { + if (end > beg) { + numberOfSubstrings += 1; + + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + // The following is OK, because String.substring( beg, end ) excludes + // the character at the position 'end'. + substrings.add(str.substring(beg, end)); + + // Set the starting point for the next search. + // The following is equivalent to beg = end + (separatorLength - 1) + 1, + // which is the right calculation: + beg = end + separatorLength; + } + } else { + // We found a consecutive occurrence of the separator, so skip it. + if (preserveAllTokens) { + numberOfSubstrings += 1; + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + substrings.add(EMPTY); + } + } + beg = end + separatorLength; + } + } else { + // String.substring( beg ) goes from 'beg' to the end of the String. + substrings.add(str.substring(beg)); + end = len; + } + } + + return (String[]) substrings.toArray(new String[substrings.size()]); + } + + // ----------------------------------------------------------------------- + /** + *

      Splits the provided text into an array, using whitespace as the + * separator, preserving all tokens, including empty tokens created by + * adjacent separators. This is an alternative to using StringTokenizer. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

      + * + *

      A null input String returns null.

      + * + *
      +     * StringUtils.splitPreserveAllTokens(null)       = null
      +     * StringUtils.splitPreserveAllTokens("")         = []
      +     * StringUtils.splitPreserveAllTokens("abc def")  = ["abc", "def"]
      +     * StringUtils.splitPreserveAllTokens("abc  def") = ["abc", "", "def"]
      +     * StringUtils.splitPreserveAllTokens(" abc ")    = ["", "abc", ""]
      +     * 
      + * + * @param str the String to parse, may be null + * @return an array of parsed Strings, null if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str) { + return splitWorker(str, null, -1, true); + } + + /** + *

      Splits the provided text into an array, separator specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.

      + * + *

      The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

      + * + *

      A null input String returns null.

      + * + *
      +     * StringUtils.splitPreserveAllTokens(null, *)         = null
      +     * StringUtils.splitPreserveAllTokens("", *)           = []
      +     * StringUtils.splitPreserveAllTokens("a.b.c", '.')    = ["a", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens("a..b.c", '.')   = ["a", "", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens("a:b:c", '.')    = ["a:b:c"]
      +     * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens("a b c", ' ')    = ["a", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens("a b c ", ' ')   = ["a", "b", "c", ""]
      +     * StringUtils.splitPreserveAllTokens("a b c  ", ' ')   = ["a", "b", "c", "", ""]
      +     * StringUtils.splitPreserveAllTokens(" a b c", ' ')   = ["", a", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens("  a b c", ' ')  = ["", "", a", "b", "c"]
      +     * StringUtils.splitPreserveAllTokens(" a b c ", ' ')  = ["", a", "b", "c", ""]
      +     * 
      + * + * @param str the String to parse, may be null + * @param separatorChar the character used as the delimiter, + * null splits on whitespace + * @return an array of parsed Strings, null if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, char separatorChar) { + return splitWorker(str, separatorChar, true); + } + + /** + * Performs the logic for the split and + * splitPreserveAllTokens methods that do not return a + * maximum array length. + * + * @param str the String to parse, may be null + * @param separatorChar the separate character + * @param preserveAllTokens if true, adjacent separators are + * treated as empty token separators; if false, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, null if null String input + */ + private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) { // Performance tuned for 2.0 (JDK1.4) - + if (str == null) { return null; } @@ -1983,85 +2798,125 @@ return ArrayUtils.EMPTY_STRING_ARRAY; } List list = new ArrayList(); - int i =0, start = 0; + int i = 0, start = 0; boolean match = false; + boolean lastMatch = false; while (i < len) { if (str.charAt(i) == separatorChar) { - if (match) { + if (match || preserveAllTokens) { list.add(str.substring(start, i)); match = false; + lastMatch = true; } start = ++i; continue; } + lastMatch = false; match = true; i++; } - if (match) { + if (match || (preserveAllTokens && lastMatch)) { list.add(str.substring(start, i)); } return (String[]) list.toArray(new String[list.size()]); } /** - *

      Splits the provided text into an array, separators specified. - * This is an alternative to using StringTokenizer.

      + *

      Splits the provided text into an array, separators specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.

      * *

      The separator is not included in the returned String array. - * Adjacent separators are treated as one separator.

      - * + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

      + * *

      A null input String returns null. * A null separatorChars splits on whitespace.

      * *
      -     * StringUtils.split(null, *)         = null
      -     * StringUtils.split("", *)           = []
      -     * StringUtils.split("abc def", null) = ["abc", "def"]
      -     * StringUtils.split("abc def", " ")  = ["abc", "def"]
      -     * StringUtils.split("abc  def", " ") = ["abc", "def"]
      -     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens(null, *)           = null
      +     * StringUtils.splitPreserveAllTokens("", *)             = []
      +     * StringUtils.splitPreserveAllTokens("abc def", null)   = ["abc", "def"]
      +     * StringUtils.splitPreserveAllTokens("abc def", " ")    = ["abc", "def"]
      +     * StringUtils.splitPreserveAllTokens("abc  def", " ")   = ["abc", "", def"]
      +     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":")   = ["ab", "cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":")  = ["ab", "cd", "ef", ""]
      +     * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
      +     * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":")  = ["ab", "", cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens(":cd:ef", ":")     = ["", cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens("::cd:ef", ":")    = ["", "", cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens(":cd:ef:", ":")    = ["", cd", "ef", ""]
            * 
      - * - * @param str the String to parse, may be null + * + * @param str the String to parse, may be null * @param separatorChars the characters used as the delimiters, * null splits on whitespace * @return an array of parsed Strings, null if null String input + * @since 2.1 */ - public static String[] split(String str, String separatorChars) { - return split(str, separatorChars, -1); + public static String[] splitPreserveAllTokens(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, true); } /** - *

      Splits the provided text into an array, separators specified. - * This is an alternative to using StringTokenizer.

      + *

      Splits the provided text into an array with a maximum length, + * separators specified, preserving all tokens, including empty tokens + * created by adjacent separators.

      * *

      The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. * Adjacent separators are treated as one separator.

      * *

      A null input String returns null. * A null separatorChars splits on whitespace.

      - * + * + *

      If more than max delimited substrings are found, the last + * returned string includes all characters after the first max - 1 + * returned strings (including separator characters).

      + * *
      -     * StringUtils.split(null, *, *)            = null
      -     * StringUtils.split("", *, *)              = []
      -     * StringUtils.split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
      -     * StringUtils.split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
      -     * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
      -     * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cdef"]
      +     * StringUtils.splitPreserveAllTokens(null, *, *)            = null
      +     * StringUtils.splitPreserveAllTokens("", *, *)              = []
      +     * StringUtils.splitPreserveAllTokens("ab de fg", null, 0)   = ["ab", "cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 0) = ["ab", "cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
      +     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
      +     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 2) = ["ab", "  de fg"]
      +     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 3) = ["ab", "", " de fg"]
      +     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 4) = ["ab", "", "", "de fg"]
            * 
      - * - * @param str the String to parse, may be null + * + * @param str the String to parse, may be null * @param separatorChars the characters used as the delimiters, * null splits on whitespace * @param max the maximum number of elements to include in the * array. A zero or negative value implies no limit * @return an array of parsed Strings, null if null String input + * @since 2.1 */ - public static String[] split(String str, String separatorChars, int max) { + public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, true); + } + + /** + * Performs the logic for the split and + * splitPreserveAllTokens methods that return a maximum array + * length. + * + * @param str the String to parse, may be null + * @param separatorChars the separate character + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if true, adjacent separators are + * treated as empty token separators; if false, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, null if null String input + */ + private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { // Performance tuned for 2.0 (JDK1.4) // Direct code is quicker than StringTokenizer. // Also, StringTokenizer uses isSpace() not isWhitespace() - + if (str == null) { return null; } @@ -2071,22 +2926,26 @@ } List list = new ArrayList(); int sizePlus1 = 1; - int i =0, start = 0; + int i = 0, start = 0; boolean match = false; + boolean lastMatch = false; if (separatorChars == null) { // Null separator means use whitespace while (i < len) { if (Character.isWhitespace(str.charAt(i))) { - if (match) { + if (match || preserveAllTokens) { + lastMatch = true; if (sizePlus1++ == max) { i = len; + lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } + lastMatch = false; match = true; i++; } @@ -2095,48 +2954,153 @@ char sep = separatorChars.charAt(0); while (i < len) { if (str.charAt(i) == sep) { - if (match) { + if (match || preserveAllTokens) { + lastMatch = true; if (sizePlus1++ == max) { i = len; + lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } + lastMatch = false; match = true; i++; } } else { // standard case while (i < len) { if (separatorChars.indexOf(str.charAt(i)) >= 0) { - if (match) { + if (match || preserveAllTokens) { + lastMatch = true; if (sizePlus1++ == max) { i = len; + lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } + lastMatch = false; match = true; i++; } } - if (match) { + if (match || (preserveAllTokens && lastMatch)) { list.add(str.substring(start, i)); } return (String[]) list.toArray(new String[list.size()]); } + /** + *

      Splits a String by Character type as returned by + * java.lang.Character.getType(char). Groups of contiguous + * characters of the same type are returned as complete tokens. + *

      +     * StringUtils.splitByCharacterType(null)         = null
      +     * StringUtils.splitByCharacterType("")           = []
      +     * StringUtils.splitByCharacterType("ab de fg")   = ["ab", " ", "de", " ", "fg"]
      +     * StringUtils.splitByCharacterType("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
      +     * StringUtils.splitByCharacterType("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
      +     * StringUtils.splitByCharacterType("number5")    = ["number", "5"]
      +     * StringUtils.splitByCharacterType("fooBar")     = ["foo", "B", "ar"]
      +     * StringUtils.splitByCharacterType("foo200Bar")  = ["foo", "200", "B", "ar"]
      +     * StringUtils.splitByCharacterType("ASFRules")   = ["ASFR", "ules"]
      +     * 
      + * @param str the String to split, may be null + * @return an array of parsed Strings, null if null String input + * @since 2.4 + */ + public static String[] splitByCharacterType(String str) { + return splitByCharacterType(str, false); + } + + /** + *

      Splits a String by Character type as returned by + * java.lang.Character.getType(char). Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: the character of type + * Character.UPPERCASE_LETTER, if any, immediately + * preceding a token of type Character.LOWERCASE_LETTER + * will belong to the following token rather than to the preceding, if any, + * Character.UPPERCASE_LETTER token. + *

      +     * StringUtils.splitByCharacterTypeCamelCase(null)         = null
      +     * StringUtils.splitByCharacterTypeCamelCase("")           = []
      +     * StringUtils.splitByCharacterTypeCamelCase("ab de fg")   = ["ab", " ", "de", " ", "fg"]
      +     * StringUtils.splitByCharacterTypeCamelCase("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
      +     * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
      +     * StringUtils.splitByCharacterTypeCamelCase("number5")    = ["number", "5"]
      +     * StringUtils.splitByCharacterTypeCamelCase("fooBar")     = ["foo", "Bar"]
      +     * StringUtils.splitByCharacterTypeCamelCase("foo200Bar")  = ["foo", "200", "Bar"]
      +     * StringUtils.splitByCharacterTypeCamelCase("ASFRules")   = ["ASF", "Rules"]
      +     * 
      + * @param str the String to split, may be null + * @return an array of parsed Strings, null if null String input + * @since 2.4 + */ + public static String[] splitByCharacterTypeCamelCase(String str) { + return splitByCharacterType(str, true); + } + + /** + *

      Splits a String by Character type as returned by + * java.lang.Character.getType(char). Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: if camelCase is true, + * the character of type Character.UPPERCASE_LETTER, if any, + * immediately preceding a token of type Character.LOWERCASE_LETTER + * will belong to the following token rather than to the preceding, if any, + * Character.UPPERCASE_LETTER token. + * @param str the String to split, may be null + * @param camelCase whether to use so-called "camel-case" for letter types + * @return an array of parsed Strings, null if null String input + * @since 2.4 + */ + private static String[] splitByCharacterType(String str, boolean camelCase) { + if (str == null) { + return null; + } + if (str.length() == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + char[] c = str.toCharArray(); + List list = new ArrayList(); + int tokenStart = 0; + int currentType = Character.getType(c[tokenStart]); + for (int pos = tokenStart + 1; pos < c.length; pos++) { + int type = Character.getType(c[pos]); + if (type == currentType) { + continue; + } + if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) { + int newTokenStart = pos - 1; + if (newTokenStart != tokenStart) { + list.add(new String(c, tokenStart, newTokenStart - tokenStart)); + tokenStart = newTokenStart; + } + } else { + list.add(new String(c, tokenStart, pos - tokenStart)); + tokenStart = pos; + } + currentType = type; + } + list.add(new String(c, tokenStart, c.length - tokenStart)); + return (String[]) list.toArray(new String[list.size()]); + } + // Joining //----------------------------------------------------------------------- /** - *

      Concatenates elements of an array into a single String. - * Null objects or empty strings within the array are represented by + *

      Joins the provided elements into a single String.

      + * + *

      No separator is added to the joined String. + * Null objects or empty string elements are represented by * empty strings.

      * *
      @@ -2146,7 +3110,7 @@
            * StringUtils.concatenate(["a", "b", "c"]) = "abc"
            * StringUtils.concatenate([null, "", "a"]) = "a"
            * 
      - * + * * @param array the array of values to concatenate, may be null * @return the concatenated String, null if null array input * @deprecated Use the better named {@link #join(Object[])} instead. @@ -2155,37 +3119,37 @@ public static String concatenate(Object[] array) { return join(array, null); } - + /** *

      Joins the elements of the provided array into a single String * containing the provided list of elements.

      * *

      No separator is added to the joined String. - * Null objects or empty strings within the array are represented by + * Null objects or empty strings within the array are represented by * empty strings.

      - * + * *
            * StringUtils.join(null)            = null
            * StringUtils.join([])              = ""
            * StringUtils.join([null])          = ""
            * StringUtils.join(["a", "b", "c"]) = "abc"
            * StringUtils.join([null, "", "a"]) = "a"
            * 
      - * + * * @param array the array of values to join together, may be null * @return the joined String, null if null array input * @since 2.0 */ public static String join(Object[] array) { return join(array, null); } - + /** *

      Joins the elements of the provided array into a single String * containing the provided list of elements.

      * *

      No delimiter is added before or after the list. - * Null objects or empty strings within the array are represented by + * Null objects or empty strings within the array are represented by * empty strings.

      * *
      @@ -2206,12 +3170,50 @@
               if (array == null) {
                   return null;
               }
      -        int arraySize = array.length;
      -        int bufSize = (arraySize == 0 ? 0 : ((array[0] == null ? 16 : array[0].toString().length()) + 1) * arraySize);
      -        StringBuffer buf = new StringBuffer(bufSize);
       
      -        for (int i = 0; i < arraySize; i++) {
      -            if (i > 0) {
      +        return join(array, separator, 0, array.length);
      +    }
      +
      +    /**
      +     * 

      Joins the elements of the provided array into a single String + * containing the provided list of elements.

      + * + *

      No delimiter is added before or after the list. + * Null objects or empty strings within the array are represented by + * empty strings.

      + * + *
      +     * StringUtils.join(null, *)               = null
      +     * StringUtils.join([], *)                 = ""
      +     * StringUtils.join([null], *)             = ""
      +     * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
      +     * StringUtils.join(["a", "b", "c"], null) = "abc"
      +     * StringUtils.join([null, "", "a"], ';')  = ";;a"
      +     * 
      + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, null if null array input + * @since 2.0 + */ + public static String join(Object[] array, char separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + int bufSize = (endIndex - startIndex); + if (bufSize <= 0) { + return EMPTY; + } + + bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + 1); + StrBuilder buf = new StrBuilder(bufSize); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { buf.append(separator); } if (array[i] != null) { @@ -2221,13 +3223,14 @@ return buf.toString(); } + /** *

      Joins the elements of the provided array into a single String * containing the provided list of elements.

      * *

      No delimiter is added before or after the list. - * A null separator is the same as an empty String (""). - * Null objects or empty strings within the array are represented by + * A null separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by * empty strings.

      * *
      @@ -2248,23 +3251,58 @@
               if (array == null) {
                   return null;
               }
      +        return join(array, separator, 0, array.length);
      +    }
      +
      +    /**
      +     * 

      Joins the elements of the provided array into a single String + * containing the provided list of elements.

      + * + *

      No delimiter is added before or after the list. + * A null separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.

      + * + *
      +     * StringUtils.join(null, *)                = null
      +     * StringUtils.join([], *)                  = ""
      +     * StringUtils.join([null], *)              = ""
      +     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
      +     * StringUtils.join(["a", "b", "c"], null)  = "abc"
      +     * StringUtils.join(["a", "b", "c"], "")    = "abc"
      +     * StringUtils.join([null, "", "a"], ',')   = ",,a"
      +     * 
      + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, null if null array input + */ + public static String join(Object[] array, String separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } if (separator == null) { separator = EMPTY; } - int arraySize = array.length; - // ArraySize == 0: Len = 0 - // ArraySize > 0: Len = NofStrings *(len(firstString) + len(separator)) + // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) // (Assuming that all Strings are roughly equally long) - int bufSize - = ((arraySize == 0) ? 0 - : arraySize * ((array[0] == null ? 16 : array[0].toString().length()) - + ((separator != null) ? separator.length(): 0))); + int bufSize = (endIndex - startIndex); + if (bufSize <= 0) { + return EMPTY; + } - StringBuffer buf = new StringBuffer(bufSize); + bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + + separator.length()); - for (int i = 0; i < arraySize; i++) { - if ((separator != null) && (i > 0)) { + StrBuilder buf = new StrBuilder(bufSize); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { buf.append(separator); } if (array[i] != null) { @@ -2278,7 +3316,7 @@ *

      Joins the elements of the provided Iterator into * a single String containing the provided elements.

      * - *

      No delimiter is added before or after the list. Null objects or empty + *

      No delimiter is added before or after the list. Null objects or empty * strings within the iteration are represented by empty strings.

      * *

      See the examples here: {@link #join(Object[],char)}.

      @@ -2289,19 +3327,33 @@ * @since 2.0 */ public static String join(Iterator iterator, char separator) { + + // handle null, zero and one elements before building a buffer if (iterator == null) { return null; } - StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StrBuilder buf = new StrBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + while (iterator.hasNext()) { + buf.append(separator); Object obj = iterator.next(); if (obj != null) { buf.append(obj); } - if (iterator.hasNext()) { - buf.append(separator); - } } + return buf.toString(); } @@ -2319,29 +3371,86 @@ * @return the joined String, null if null iterator input */ public static String join(Iterator iterator, String separator) { + + // handle null, zero and one elements before building a buffer if (iterator == null) { return null; } - StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StrBuilder buf = new StrBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + while (iterator.hasNext()) { + if (separator != null) { + buf.append(separator); + } Object obj = iterator.next(); if (obj != null) { buf.append(obj); } - if ((separator != null) && iterator.hasNext()) { - buf.append(separator); - } - } + } return buf.toString(); } + /** + *

      Joins the elements of the provided Collection into + * a single String containing the provided elements.

      + * + *

      No delimiter is added before or after the list. Null objects or empty + * strings within the iteration are represented by empty strings.

      + * + *

      See the examples here: {@link #join(Object[],char)}.

      + * + * @param collection the Collection of values to join together, may be null + * @param separator the separator character to use + * @return the joined String, null if null iterator input + * @since 2.3 + */ + public static String join(Collection collection, char separator) { + if (collection == null) { + return null; + } + return join(collection.iterator(), separator); + } + + /** + *

      Joins the elements of the provided Collection into + * a single String containing the provided elements.

      + * + *

      No delimiter is added before or after the list. + * A null separator is the same as an empty String ("").

      + * + *

      See the examples here: {@link #join(Object[],String)}.

      + * + * @param collection the Collection of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, null if null iterator input + * @since 2.3 + */ + public static String join(Collection collection, String separator) { + if (collection == null) { + return null; + } + return join(collection.iterator(), separator); + } + // Delete //----------------------------------------------------------------------- /** *

      Deletes all 'space' characters from a String as defined by * {@link Character#isSpace(char)}.

      - * - *

      This is the only StringUtils method that uses the + * + *

      This is the only StringUtils method that uses the * isSpace definition. You are advised to use * {@link #deleteWhitespace(String)} instead as whitespace is much * better localized.

      @@ -2354,7 +3463,7 @@ * StringUtils.deleteSpaces("ab c") = "abc" * StringUtils.deleteSpaces("a\nb\tc ") = "abc" *
      - * + * *

      Spaces are defined as {' ', '\t', '\r', '\n', '\b'} * in line with the deprecated isSpace method.

      * @@ -2380,76 +3489,286 @@ * StringUtils.deleteWhitespace("abc") = "abc" * StringUtils.deleteWhitespace(" ab c ") = "abc" *
      - * + * * @param str the String to delete whitespace from, may be null * @return the String without whitespaces, null if null String input */ public static String deleteWhitespace(String str) { - if (str == null) { - return null; + if (isEmpty(str)) { + return str; } int sz = str.length(); - StringBuffer buffer = new StringBuffer(sz); + char[] chs = new char[sz]; + int count = 0; for (int i = 0; i < sz; i++) { if (!Character.isWhitespace(str.charAt(i))) { - buffer.append(str.charAt(i)); + chs[count++] = str.charAt(i); } } - return buffer.toString(); + if (count == sz) { + return str; + } + return new String(chs, 0, count); } + // Remove + //----------------------------------------------------------------------- + /** + *

      Removes a substring only if it is at the begining of a source string, + * otherwise returns the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string. + * A null search string will return the source string.

      + * + *
      +     * StringUtils.removeStart(null, *)      = null
      +     * StringUtils.removeStart("", *)        = ""
      +     * StringUtils.removeStart(*, null)      = *
      +     * StringUtils.removeStart("www.domain.com", "www.")   = "domain.com"
      +     * StringUtils.removeStart("domain.com", "www.")       = "domain.com"
      +     * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com"
      +     * StringUtils.removeStart("abc", "")    = "abc"
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * null if null String input + * @since 2.1 + */ + public static String removeStart(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.startsWith(remove)){ + return str.substring(remove.length()); + } + return str; + } + + /** + *

      Case insensitive removal of a substring if it is at the begining of a source string, + * otherwise returns the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string. + * A null search string will return the source string.

      + * + *
      +     * StringUtils.removeStartIgnoreCase(null, *)      = null
      +     * StringUtils.removeStartIgnoreCase("", *)        = ""
      +     * StringUtils.removeStartIgnoreCase(*, null)      = *
      +     * StringUtils.removeStartIgnoreCase("www.domain.com", "www.")   = "domain.com"
      +     * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.")   = "domain.com"
      +     * StringUtils.removeStartIgnoreCase("domain.com", "www.")       = "domain.com"
      +     * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
      +     * StringUtils.removeStartIgnoreCase("abc", "")    = "abc"
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * null if null String input + * @since 2.4 + */ + public static String removeStartIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (startsWithIgnoreCase(str, remove)) { + return str.substring(remove.length()); + } + return str; + } + + /** + *

      Removes a substring only if it is at the end of a source string, + * otherwise returns the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string. + * A null search string will return the source string.

      + * + *
      +     * StringUtils.removeEnd(null, *)      = null
      +     * StringUtils.removeEnd("", *)        = ""
      +     * StringUtils.removeEnd(*, null)      = *
      +     * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
      +     * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
      +     * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
      +     * StringUtils.removeEnd("abc", "")    = "abc"
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * null if null String input + * @since 2.1 + */ + public static String removeEnd(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.endsWith(remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + *

      Case insensitive removal of a substring if it is at the end of a source string, + * otherwise returns the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string. + * A null search string will return the source string.

      + * + *
      +     * StringUtils.removeEndIgnoreCase(null, *)      = null
      +     * StringUtils.removeEndIgnoreCase("", *)        = ""
      +     * StringUtils.removeEndIgnoreCase(*, null)      = *
      +     * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.")  = "www.domain.com"
      +     * StringUtils.removeEndIgnoreCase("www.domain.com", ".com")   = "www.domain"
      +     * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
      +     * StringUtils.removeEndIgnoreCase("abc", "")    = "abc"
      +     * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
      +     * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * null if null String input + * @since 2.4 + */ + public static String removeEndIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (endsWithIgnoreCase(str, remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + *

      Removes all occurrences of a substring from within the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string. + * A null remove string will return the source string. + * An empty ("") remove string will return the source string.

      + * + *
      +     * StringUtils.remove(null, *)        = null
      +     * StringUtils.remove("", *)          = ""
      +     * StringUtils.remove(*, null)        = *
      +     * StringUtils.remove(*, "")          = *
      +     * StringUtils.remove("queued", "ue") = "qd"
      +     * StringUtils.remove("queued", "zz") = "queued"
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * null if null String input + * @since 2.1 + */ + public static String remove(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + return replace(str, remove, EMPTY, -1); + } + + /** + *

      Removes all occurrences of a character from within the source string.

      + * + *

      A null source string will return null. + * An empty ("") source string will return the empty string.

      + * + *
      +     * StringUtils.remove(null, *)       = null
      +     * StringUtils.remove("", *)         = ""
      +     * StringUtils.remove("queued", 'u') = "qeed"
      +     * StringUtils.remove("queued", 'z') = "queued"
      +     * 
      + * + * @param str the source String to search, may be null + * @param remove the char to search for and remove, may be null + * @return the substring with the char removed if found, + * null if null String input + * @since 2.1 + */ + public static String remove(String str, char remove) { + if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) { + return str; + } + char[] chars = str.toCharArray(); + int pos = 0; + for (int i = 0; i < chars.length; i++) { + if (chars[i] != remove) { + chars[pos++] = chars[i]; + } + } + return new String(chars, 0, pos); + } + // Replacing //----------------------------------------------------------------------- /** *

      Replaces a String with another String inside a larger String, once.

      - * + * *

      A null reference passed to this method is a no-op.

      - * + * *
            * StringUtils.replaceOnce(null, *, *)        = null
            * StringUtils.replaceOnce("", *, *)          = ""
      -     * StringUtils.replaceOnce("aba", null, null) = "aba"
      -     * StringUtils.replaceOnce("aba", null, null) = "aba"
      +     * StringUtils.replaceOnce("any", null, *)    = "any"
      +     * StringUtils.replaceOnce("any", *, null)    = "any"
      +     * StringUtils.replaceOnce("any", "", *)      = "any"
            * StringUtils.replaceOnce("aba", "a", null)  = "aba"
      -     * StringUtils.replaceOnce("aba", "a", "")    = "aba"
      +     * StringUtils.replaceOnce("aba", "a", "")    = "ba"
            * StringUtils.replaceOnce("aba", "a", "z")   = "zba"
            * 
      - * - * @see #replace(String text, String repl, String with, int max) + * + * @see #replace(String text, String searchString, String replacement, int max) * @param text text to search and replace in, may be null - * @param repl the String to search for, may be null - * @param with the String to replace with, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace with, may be null * @return the text with any replacements processed, * null if null String input */ - public static String replaceOnce(String text, String repl, String with) { - return replace(text, repl, with, 1); + public static String replaceOnce(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, 1); } /** - *

      Replaces all occurances of a String within another String.

      + *

      Replaces all occurrences of a String within another String.

      * *

      A null reference passed to this method is a no-op.

      - * + * *
            * StringUtils.replace(null, *, *)        = null
            * StringUtils.replace("", *, *)          = ""
      -     * StringUtils.replace("aba", null, null) = "aba"
      -     * StringUtils.replace("aba", null, null) = "aba"
      +     * StringUtils.replace("any", null, *)    = "any"
      +     * StringUtils.replace("any", *, null)    = "any"
      +     * StringUtils.replace("any", "", *)      = "any"
            * StringUtils.replace("aba", "a", null)  = "aba"
      -     * StringUtils.replace("aba", "a", "")    = "aba"
      +     * StringUtils.replace("aba", "a", "")    = "b"
            * StringUtils.replace("aba", "a", "z")   = "zbz"
            * 
      - * - * @see #replace(String text, String repl, String with, int max) + * + * @see #replace(String text, String searchString, String replacement, int max) * @param text text to search and replace in, may be null - * @param repl the String to search for, may be null - * @param with the String to replace with, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null * @return the text with any replacements processed, * null if null String input */ - public static String replace(String text, String repl, String with) { - return replace(text, repl, with, -1); + public static String replace(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, -1); } /** @@ -2461,58 +3780,341 @@ *
            * StringUtils.replace(null, *, *, *)         = null
            * StringUtils.replace("", *, *, *)           = ""
      -     * StringUtils.replace("abaa", null, null, 1) = "abaa"
      -     * StringUtils.replace("abaa", null, null, 1) = "abaa"
      -     * StringUtils.replace("abaa", "a", null, 1)  = "abaa"
      -     * StringUtils.replace("abaa", "a", "", 1)    = "abaa"
      +     * StringUtils.replace("any", null, *, *)     = "any"
      +     * StringUtils.replace("any", *, null, *)     = "any"
      +     * StringUtils.replace("any", "", *, *)       = "any"
      +     * StringUtils.replace("any", *, *, 0)        = "any"
      +     * StringUtils.replace("abaa", "a", null, -1) = "abaa"
      +     * StringUtils.replace("abaa", "a", "", -1)   = "b"
            * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
            * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
            * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
            * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
            * 
      - * + * * @param text text to search and replace in, may be null - * @param repl the String to search for, may be null - * @param with the String to replace with, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null * @param max maximum number of values to replace, or -1 if no maximum * @return the text with any replacements processed, * null if null String input */ - public static String replace(String text, String repl, String with, int max) { - if (text == null || repl == null || with == null || repl.length() == 0 || max == 0) { + public static String replace(String text, String searchString, String replacement, int max) { + if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) { return text; } - - StringBuffer buf = new StringBuffer(text.length()); - int start = 0, end = 0; - while ((end = text.indexOf(repl, start)) != -1) { - buf.append(text.substring(start, end)).append(with); - start = end + repl.length(); - + int start = 0; + int end = text.indexOf(searchString, start); + if (end == INDEX_NOT_FOUND) { + return text; + } + int replLength = searchString.length(); + int increase = replacement.length() - replLength; + increase = (increase < 0 ? 0 : increase); + increase *= (max < 0 ? 16 : (max > 64 ? 64 : max)); + StrBuilder buf = new StrBuilder(text.length() + increase); + while (end != INDEX_NOT_FOUND) { + buf.append(text.substring(start, end)).append(replacement); + start = end + replLength; if (--max == 0) { break; } + end = text.indexOf(searchString, start); } buf.append(text.substring(start)); return buf.toString(); } - + + /** + *

      + * Replaces all occurrences of Strings within another String. + *

      + * + *

      + * A null reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. This will not repeat. For repeating replaces, call the + * overloaded method. + *

      + * + *
      +     *  StringUtils.replaceEach(null, *, *)        = null
      +     *  StringUtils.replaceEach("", *, *)          = ""
      +     *  StringUtils.replaceEach("aba", null, null) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
      +     *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
      +     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
      +     *  (example of how it does not repeat)
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
      +     * 
      + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, null if + * null String input + * @throws IndexOutOfBoundsException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEach(String text, String[] searchList, String[] replacementList) { + return replaceEach(text, searchList, replacementList, false, 0); + } + + /** + *

      + * Replaces all occurrences of Strings within another String. + *

      + * + *

      + * A null reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. This will not repeat. For repeating replaces, call the + * overloaded method. + *

      + * + *
      +     *  StringUtils.replaceEach(null, *, *, *) = null
      +     *  StringUtils.replaceEach("", *, *, *) = ""
      +     *  StringUtils.replaceEach("aba", null, null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
      +     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
      +     *  (example of how it repeats)
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalArgumentException
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
      +     * 
      + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, null if + * null String input + * @throws IllegalArgumentException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IndexOutOfBoundsException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) { + // timeToLive should be 0 if not used or nothing to replace, else it's + // the length of the replace array + int timeToLive = searchList == null ? 0 : searchList.length; + return replaceEach(text, searchList, replacementList, true, timeToLive); + } + + /** + *

      + * Replaces all occurrences of Strings within another String. + *

      + * + *

      + * A null reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. + *

      + * + *
      +     *  StringUtils.replaceEach(null, *, *, *) = null
      +     *  StringUtils.replaceEach("", *, *, *) = ""
      +     *  StringUtils.replaceEach("aba", null, null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
      +     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
      +     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
      +     *  (example of how it repeats)
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
      +     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalArgumentException
      +     * 
      + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @param repeat if true, then replace repeatedly + * until there are no more possible replacements or timeToLive < 0 + * @param timeToLive + * if less than 0 then there is a circular reference and endless + * loop + * @return the text with any replacements processed, null if + * null String input + * @throws IllegalArgumentException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IndexOutOfBoundsException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + private static String replaceEach(String text, String[] searchList, String[] replacementList, + boolean repeat, int timeToLive) + { + + // mchyzer Performance note: This creates very few new objects (one major goal) + // let me know if there are performance requests, we can create a harness to measure + + if (text == null || text.length() == 0 || searchList == null || + searchList.length == 0 || replacementList == null || replacementList.length == 0) + { + return text; + } + + // if recursing, this shouldnt be less than 0 + if (timeToLive < 0) { + throw new IllegalStateException("TimeToLive of " + timeToLive + " is less than 0: " + text); + } + + int searchLength = searchList.length; + int replacementLength = replacementList.length; + + // make sure lengths are ok, these need to be equal + if (searchLength != replacementLength) { + throw new IllegalArgumentException("Search and Replace array lengths don't match: " + + searchLength + + " vs " + + replacementLength); + } + + // keep track of which still have matches + boolean[] noMoreMatchesForReplIndex = new boolean[searchLength]; + + // index on index that the match was found + int textIndex = -1; + int replaceIndex = -1; + int tempIndex = -1; + + // index of replace array that will replace the search string found + // NOTE: logic duplicated below START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) + { + continue; + } + tempIndex = text.indexOf(searchList[i]); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic mostly below END + + // no search strings found, we are done + if (textIndex == -1) { + return text; + } + + int start = 0; + + // get a good guess on the size of the result buffer so it doesnt have to double if it goes over a bit + int increase = 0; + + // count the replacement text elements that are larger than their corresponding text being replaced + for (int i = 0; i < searchList.length; i++) { + if (searchList[i] == null || replacementList[i] == null) { + continue; + } + int greater = replacementList[i].length() - searchList[i].length(); + if (greater > 0) { + increase += 3 * greater; // assume 3 matches + } + } + // have upper-bound at 20% increase, then let Java take over + increase = Math.min(increase, text.length() / 5); + + StrBuilder buf = new StrBuilder(text.length() + increase); + + while (textIndex != -1) { + + for (int i = start; i < textIndex; i++) { + buf.append(text.charAt(i)); + } + buf.append(replacementList[replaceIndex]); + + start = textIndex + searchList[replaceIndex].length(); + + textIndex = -1; + replaceIndex = -1; + tempIndex = -1; + // find the next earliest match + // NOTE: logic mostly duplicated above START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) + { + continue; + } + tempIndex = text.indexOf(searchList[i], start); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic duplicated above END + + } + int textLength = text.length(); + for (int i = start; i < textLength; i++) { + buf.append(text.charAt(i)); + } + String result = buf.toString(); + if (!repeat) { + return result; + } + + return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1); + } + // Replace, character based //----------------------------------------------------------------------- /** - *

      Replaces all occurrances of a character in a String with another. + *

      Replaces all occurrences of a character in a String with another. * This is a null-safe version of {@link String#replace(char, char)}.

      * *

      A null string input returns null. * An empty ("") string input returns an empty string.

      - * + * *
            * StringUtils.replaceChars(null, *, *)        = null
            * StringUtils.replaceChars("", *, *)          = ""
            * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya"
            * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba"
            * 
      - * + * * @param str String to replace characters in, may be null * @param searchChar the character to search for, may be null * @param replaceChar the character to replace, may be null @@ -2525,25 +4127,25 @@ } return str.replace(searchChar, replaceChar); } - + /** *

      Replaces multiple characters in a String in one go. * This method can also be used to delete characters.

      * *

      For example:
      * replaceChars("hello", "ho", "jy") = jelly.

      - * + * *

      A null string input returns null. * An empty ("") string input returns an empty string. * A null or empty set of search characters returns the input string.

      - * + * *

      The length of the search characters should normally equal the length * of the replace characters. * If the search characters is longer, then the extra search characters * are deleted. * If the search characters is shorter, then the extra replace characters * are ignored.

      - * + * *
            * StringUtils.replaceChars(null, *, *)           = null
            * StringUtils.replaceChars("", *, *)             = ""
      @@ -2555,47 +4157,40 @@
            * StringUtils.replaceChars("abcba", "bc", "y")   = "ayya"
            * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya"
            * 
      - * + * * @param str String to replace characters in, may be null * @param searchChars a set of characters to search for, may be null * @param replaceChars a set of characters to replace, may be null * @return modified String, null if null string input * @since 2.0 */ public static String replaceChars(String str, String searchChars, String replaceChars) { - if (str == null || str.length() == 0 || searchChars == null || searchChars.length()== 0) { + if (isEmpty(str) || isEmpty(searchChars)) { return str; } - char[] chars = str.toCharArray(); - int len = chars.length; + if (replaceChars == null) { + replaceChars = EMPTY; + } boolean modified = false; - for (int i = 0, isize = searchChars.length(); i < isize; i++) { - char searchChar = searchChars.charAt(i); - if (replaceChars == null || i >= replaceChars.length()) { - // delete - int pos = 0; - for (int j = 0; j < len; j++) { - if (chars[j] != searchChar) { - chars[pos++] = chars[j]; - } else { - modified = true; - } + int replaceCharsLength = replaceChars.length(); + int strLength = str.length(); + StrBuilder buf = new StrBuilder(strLength); + for (int i = 0; i < strLength; i++) { + char ch = str.charAt(i); + int index = searchChars.indexOf(ch); + if (index >= 0) { + modified = true; + if (index < replaceCharsLength) { + buf.append(replaceChars.charAt(index)); } - len = pos; } else { - // replace - for (int j = 0; j < len; j++) { - if (chars[j] == searchChar) { - chars[j] = replaceChars.charAt(i); - modified = true; - } - } + buf.append(ch); } } - if (modified == false) { - return str; + if (modified) { + return buf.toString(); } - return new String(chars, 0, len); + return str; } // Overlay @@ -2614,7 +4209,7 @@ * StringUtils.overlayString("abcdef", "zzzz", -1, 4) = IndexOutOfBoundsException * StringUtils.overlayString("abcdef", "zzzz", 2, 8) = IndexOutOfBoundsException *
      - * + * * @param text the String to do overlaying in, may be null * @param overlay the String to overlay, may be null * @param start the position to start overlaying at, must be valid @@ -2626,7 +4221,7 @@ * Method will be removed in Commons Lang 3.0. */ public static String overlayString(String text, String overlay, int start, int end) { - return new StringBuffer(start + overlay.length() + text.length() - end + 1) + return new StrBuilder(start + overlay.length() + text.length() - end + 1) .append(text.substring(0, start)) .append(overlay) .append(text.substring(end)) @@ -2635,7 +4230,7 @@ /** *

      Overlays part of a String with another String.

      - * + * *

      A null string input returns null. * A negative index is treated as zero. * An index greater than the string length is treated as the string length. @@ -2654,7 +4249,7 @@ * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef" * StringUtils.overlay("abcdef", "zzzz", 8, 10) = "abcdefzzzz" *

      - * + * * @param str the String to do overlaying in, may be null * @param overlay the String to overlay, may be null * @param start the position to start overlaying at @@ -2687,7 +4282,7 @@ start = end; end = temp; } - return new StringBuffer(len + start - end + overlay.length() + 1) + return new StrBuilder(len + start - end + overlay.length() + 1) .append(str.substring(0, start)) .append(overlay) .append(str.substring(end)) @@ -2722,29 +4317,26 @@ * @return String without newline, null if null String input */ public static String chomp(String str) { - if (str == null || str.length() == 0) { + if (isEmpty(str)) { return str; } if (str.length() == 1) { char ch = str.charAt(0); - if (ch == '\r' || ch == '\n') { + if (ch == CharUtils.CR || ch == CharUtils.LF) { return EMPTY; - } else { - return str; } + return str; } int lastIdx = str.length() - 1; char last = str.charAt(lastIdx); - if (last == '\n') { - if (str.charAt(lastIdx - 1) == '\r') { + if (last == CharUtils.LF) { + if (str.charAt(lastIdx - 1) == CharUtils.CR) { lastIdx--; } - } else if (last == '\r') { - - } else { + } else if (last != CharUtils.CR) { lastIdx++; } return str.substring(0, lastIdx); @@ -2765,7 +4357,7 @@ * StringUtils.chomp("foobar", "bar") = "foo" * StringUtils.chomp("foobar", "baz") = "foobar" * StringUtils.chomp("foo", "foo") = "" - * StringUtils.chomp("foo ", "foo") = "foo" + * StringUtils.chomp("foo ", "foo") = "foo " * StringUtils.chomp(" foo", "foo") = " " * StringUtils.chomp("foo", "foooo") = "foo" * StringUtils.chomp("foo", "") = "foo" @@ -2777,7 +4369,7 @@ * @return String without trailing separator, null if null String input */ public static String chomp(String str, String separator) { - if (str == null || str.length() == 0 || separator == null) { + if (isEmpty(str) || separator == null) { return str; } if (str.endsWith(separator)) { @@ -2789,7 +4381,7 @@ /** *

      Remove any "\n" if and only if it is at the end * of the supplied String.

      - * + * * @param str the String to chomp from, must not be null * @return String without chomped ending * @throws NullPointerException if str is null @@ -2799,10 +4391,10 @@ public static String chompLast(String str) { return chompLast(str, "\n"); } - + /** *

      Remove a value if and only if the String ends with that value.

      - * + * * @param str the String to chomp from, must not be null * @param sep the String to chomp, must not be null * @return String without chomped ending @@ -2817,12 +4409,11 @@ String sub = str.substring(str.length() - sep.length()); if (sep.equals(sub)) { return str.substring(0, str.length() - sep.length()); - } else { - return str; } + return str; } - /** + /** *

      Remove everything and return the last value of a supplied String, and * everything after it from a String.

      * @@ -2845,7 +4436,7 @@ } } - /** + /** *

      Remove the first value of a supplied String, and everything before it * from a String.

      * @@ -2858,14 +4449,13 @@ */ public static String prechomp(String str, String sep) { int idx = str.indexOf(sep); - if (idx != -1) { - return str.substring(idx + sep.length()); - } else { + if (idx == -1) { return str; - } + } + return str.substring(idx + sep.length()); } - /** + /** *

      Remove and return everything before the first value of a * supplied String from another String.

      * @@ -2879,11 +4469,10 @@ */ public static String getPrechomp(String str, String sep) { int idx = str.indexOf(sep); - if (idx != -1) { - return str.substring(0, idx + sep.length()); - } else { + if (idx == -1) { return EMPTY; - } + } + return str.substring(0, idx + sep.length()); } // Chopping @@ -2922,8 +4511,8 @@ int lastIdx = strLen - 1; String ret = str.substring(0, lastIdx); char last = str.charAt(lastIdx); - if (last == '\n') { - if (ret.charAt(lastIdx - 1) == '\r') { + if (last == CharUtils.LF) { + if (ret.charAt(lastIdx - 1) == CharUtils.CR) { return ret.substring(0, lastIdx - 1); } } @@ -2946,8 +4535,8 @@ return EMPTY; } char last = str.charAt(lastIdx); - if (last == '\n') { - if (str.charAt(lastIdx - 1) == '\r') { + if (last == CharUtils.LF) { + if (str.charAt(lastIdx - 1) == CharUtils.CR) { lastIdx--; } } else { @@ -2956,7 +4545,6 @@ return str.substring(0, lastIdx); } - // Conversion //----------------------------------------------------------------------- /** @@ -3001,7 +4589,7 @@ */ public static String repeat(String str, int repeat) { // Performance tuned for 2.0 (JDK1.4) - + if (str == null) { return null; } @@ -3013,37 +4601,67 @@ return str; } if (inputLength == 1 && repeat <= PAD_LIMIT) { - return padding(repeat, str.charAt(0)); + return padding(repeat, str.charAt(0)); } int outputLength = inputLength * repeat; switch (inputLength) { - case 1: + case 1 : char ch = str.charAt(0); char[] output1 = new char[outputLength]; for (int i = repeat - 1; i >= 0; i--) { output1[i] = ch; } return new String(output1); - case 2: + case 2 : char ch0 = str.charAt(0); char ch1 = str.charAt(1); char[] output2 = new char[outputLength]; - for (int i = repeat * 2 - 2; i >= 0; i--,i--) { + for (int i = repeat * 2 - 2; i >= 0; i--, i--) { output2[i] = ch0; output2[i + 1] = ch1; } return new String(output2); - default: - StringBuffer buf = new StringBuffer(outputLength); + default : + StrBuilder buf = new StrBuilder(outputLength); for (int i = 0; i < repeat; i++) { buf.append(str); - } + } return buf.toString(); } } /** + *

      Repeat a String repeat times to form a + * new String, with a String separator injected each time.

      + * + *
      +     * StringUtils.repeat(null, null, 2) = null
      +     * StringUtils.repeat(null, "x", 2)  = null
      +     * StringUtils.repeat("", null, 0)   = ""
      +     * StringUtils.repeat("", "", 2)     = ""
      +     * StringUtils.repeat("", "x", 3)    = "xxx"
      +     * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
      +     * 
      + * + * @param str the String to repeat, may be null + * @param separator the String to inject, may be null + * @param repeat number of times to repeat str, negative treated as zero + * @return a new String consisting of the original String repeated, + * null if null String input + * @since 2.5 + */ + public static String repeat(String str, String separator, int repeat) { + if(str == null || separator == null) { + return repeat(str, repeat); + } else { + // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it + String result = repeat(str + separator, repeat); + return removeEnd(result, separator); + } + } + + /** *

      Returns padding using the specified delimiter repeated * to a given length.

      * @@ -3053,30 +4671,35 @@ * StringUtils.padding(-2, 'e') = IndexOutOfBoundsException *
      * + *

      Note: this method doesn't not support padding with + * Unicode Supplementary Characters + * as they require a pair of chars to be represented. + * If you are needing to support full I18N of your applications + * consider using {@link #repeat(String, int)} instead. + *

      + * * @param repeat number of times to repeat delim * @param padChar character to repeat * @return String with repeated character * @throws IndexOutOfBoundsException if repeat < 0 + * @see #repeat(String, int) */ - private static String padding(int repeat, char padChar) { - // be careful of synchronization in this method - // we are assuming that get and set from an array index is atomic - String pad = PADDING[padChar]; - if (pad == null) { - pad = String.valueOf(padChar); + private static String padding(int repeat, char padChar) throws IndexOutOfBoundsException { + if (repeat < 0) { + throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat); } - while (pad.length() < repeat) { - pad = pad.concat(pad); + final char[] buf = new char[repeat]; + for (int i = 0; i < buf.length; i++) { + buf[i] = padChar; } - PADDING[padChar] = pad; - return pad.substring(0, repeat); + return new String(buf); } /** *

      Right pad a String with spaces (' ').

      * *

      The String is padded to the size of size.

      - * + * *
            * StringUtils.rightPad(null, *)   = null
            * StringUtils.rightPad("", 3)     = "   "
      @@ -3157,7 +4780,7 @@
               if (str == null) {
                   return null;
               }
      -        if (padStr == null || padStr.length() == 0) {
      +        if (isEmpty(padStr)) {
                   padStr = " ";
               }
               int padLen = padStr.length();
      @@ -3169,7 +4792,7 @@
               if (padLen == 1 && pads <= PAD_LIMIT) {
                   return rightPad(str, size, padStr.charAt(0));
               }
      -        
      +
               if (pads == padLen) {
                   return str.concat(padStr);
               } else if (pads < padLen) {
      @@ -3187,7 +4810,7 @@
           /**
            * 

      Left pad a String with spaces (' ').

      * - *

      The String is padded to the size of size.

      + *

      The String is padded to the size of size.

      * *
            * StringUtils.leftPad(null, *)   = null
      @@ -3204,7 +4827,7 @@
            *  null if null String input
            */
           public static String leftPad(String str, int size) {
      -        return leftPad(str, size, ' ');        
      +        return leftPad(str, size, ' ');
           }
       
           /**
      @@ -3269,7 +4892,7 @@
               if (str == null) {
                   return null;
               }
      -        if (padStr == null || padStr.length() == 0) {
      +        if (isEmpty(padStr)) {
                   padStr = " ";
               }
               int padLen = padStr.length();
      @@ -3281,7 +4904,7 @@
               if (padLen == 1 && pads <= PAD_LIMIT) {
                   return leftPad(str, size, padStr.charAt(0));
               }
      -        
      +
               if (pads == padLen) {
                   return padStr.concat(str);
               } else if (pads < padLen) {
      @@ -3296,12 +4919,24 @@
               }
           }
       
      +    /**
      +     * Gets a String's length or 0 if the String is null.
      +     * 
      +     * @param str
      +     *            a String or null
      +     * @return String length or 0 if the String is null.
      +     * @since 2.4
      +     */
      +    public static int length(String str) {
      +        return str == null ? 0 : str.length();
      +    }
      +    
           // Centering
           //-----------------------------------------------------------------------
           /**
            * 

      Centers a String in a larger String of size size * using the space character (' ').

      - * + * *

      If the size is less than the String length, the String is returned. * A null String returns null. * A negative size is treated as zero.

      @@ -3316,7 +4951,7 @@ * StringUtils.center("abcd", 2) = "abcd" * StringUtils.center("a", 4) = " a " *
      - * + * * @param str the String to center, may be null * @param size the int size of new String, negative treated as zero * @return centered String, null if null String input @@ -3342,7 +4977,7 @@ * StringUtils.center("a", 4, ' ') = " a " * StringUtils.center("a", 4, 'y') = "yayy" *
      - * + * * @param str the String to center, may be null * @param size the int size of new String, negative treated as zero * @param padChar the character to pad the new String with @@ -3382,7 +5017,7 @@ * StringUtils.center("abc", 7, null) = " abc " * StringUtils.center("abc", 7, "") = " abc " *
      - * + * * @param str the String to center, may be null * @param size the int size of new String, negative treated as zero * @param padStr the String to pad the new String with, must not be null or empty @@ -3393,7 +5028,7 @@ if (str == null || size <= 0) { return str; } - if (padStr == null || padStr.length() == 0) { + if (isEmpty(padStr)) { padStr = " "; } int strLen = str.length(); @@ -3410,15 +5045,20 @@ //----------------------------------------------------------------------- /** *

      Converts a String to upper case as per {@link String#toUpperCase()}.

      - * + * *

      A null input String returns null.

      - * + * *
            * StringUtils.upperCase(null)  = null
            * StringUtils.upperCase("")    = ""
            * StringUtils.upperCase("aBc") = "ABC"
            * 
      - * + * + *

      Note: As described in the documentation for {@link String#toUpperCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).

      + * * @param str the String to upper case, may be null * @return the upper cased String, null if null String input */ @@ -3430,16 +5070,44 @@ } /** + *

      Converts a String to upper case as per {@link String#toUpperCase(Locale)}.

      + * + *

      A null input String returns null.

      + * + *
      +     * StringUtils.upperCase(null, Locale.ENGLISH)  = null
      +     * StringUtils.upperCase("", Locale.ENGLISH)    = ""
      +     * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC"
      +     * 
      + * + * @param str the String to upper case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the upper cased String, null if null String input + * @since 2.5 + */ + public static String upperCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toUpperCase(locale); + } + + /** *

      Converts a String to lower case as per {@link String#toLowerCase()}.

      - * + * *

      A null input String returns null.

      - * + * *
            * StringUtils.lowerCase(null)  = null
            * StringUtils.lowerCase("")    = ""
            * StringUtils.lowerCase("aBc") = "abc"
            * 
      - * + * + *

      Note: As described in the documentation for {@link String#toLowerCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).

      + * * @param str the String to lower case, may be null * @return the lower cased String, null if null String input */ @@ -3451,19 +5119,42 @@ } /** + *

      Converts a String to lower case as per {@link String#toLowerCase(Locale)}.

      + * + *

      A null input String returns null.

      + * + *
      +     * StringUtils.lowerCase(null, Locale.ENGLISH)  = null
      +     * StringUtils.lowerCase("", Locale.ENGLISH)    = ""
      +     * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc"
      +     * 
      + * + * @param str the String to lower case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the lower cased String, null if null String input + * @since 2.5 + */ + public static String lowerCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toLowerCase(locale); + } + + /** *

      Capitalizes a String changing the first letter to title case as * per {@link Character#toTitleCase(char)}. No other letters are changed.

      - * - *

      For a word based alorithm, see {@link WordUtils#capitalize(String)}. + * + *

      For a word based algorithm, see {@link WordUtils#capitalize(String)}. * A null input String returns null.

      - * + * *
            * StringUtils.capitalize(null)  = null
            * StringUtils.capitalize("")    = ""
            * StringUtils.capitalize("cat") = "Cat"
            * StringUtils.capitalize("cAt") = "CAt"
            * 
      - * + * * @param str the String to capitalize, may be null * @return the capitalized String, null if null String input * @see WordUtils#capitalize(String) @@ -3475,7 +5166,7 @@ if (str == null || (strLen = str.length()) == 0) { return str; } - return new StringBuffer(strLen) + return new StrBuilder(strLen) .append(Character.toTitleCase(str.charAt(0))) .append(str.substring(1)) .toString(); @@ -3484,7 +5175,7 @@ /** *

      Capitalizes a String changing the first letter to title case as * per {@link Character#toTitleCase(char)}. No other letters are changed.

      - * + * * @param str the String to capitalize, may be null * @return the capitalized String, null if null String input * @deprecated Use the standardly named {@link #capitalize(String)}. @@ -3497,17 +5188,17 @@ /** *

      Uncapitalizes a String changing the first letter to title case as * per {@link Character#toLowerCase(char)}. No other letters are changed.

      - * - *

      For a word based alorithm, see {@link WordUtils#uncapitalize(String)}. + * + *

      For a word based algorithm, see {@link WordUtils#uncapitalize(String)}. * A null input String returns null.

      - * + * *
            * StringUtils.uncapitalize(null)  = null
            * StringUtils.uncapitalize("")    = ""
            * StringUtils.uncapitalize("Cat") = "cat"
            * StringUtils.uncapitalize("CAT") = "cAT"
            * 
      - * + * * @param str the String to uncapitalize, may be null * @return the uncapitalized String, null if null String input * @see WordUtils#uncapitalize(String) @@ -3519,7 +5210,7 @@ if (str == null || (strLen = str.length()) == 0) { return str; } - return new StringBuffer(strLen) + return new StrBuilder(strLen) .append(Character.toLowerCase(str.charAt(0))) .append(str.substring(1)) .toString(); @@ -3528,7 +5219,7 @@ /** *

      Uncapitalizes a String changing the first letter to title case as * per {@link Character#toLowerCase(char)}. No other letters are changed.

      - * + * * @param str the String to uncapitalize, may be null * @return the uncapitalized String, null if null String input * @deprecated Use the standardly named {@link #uncapitalize(String)}. @@ -3541,27 +5232,27 @@ /** *

      Swaps the case of a String changing upper and title case to * lower case, and lower case to upper case.

      - * + * *
        *
      • Upper case character converts to Lower case
      • *
      • Title case character converts to Lower case
      • *
      • Lower case character converts to Upper case
      • *
      - * - *

      For a word based alorithm, see {@link WordUtils#swapCase(String)}. + * + *

      For a word based algorithm, see {@link WordUtils#swapCase(String)}. * A null input String returns null.

      - * + * *
            * StringUtils.swapCase(null)                 = null
            * StringUtils.swapCase("")                   = ""
            * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
            * 
      - * + * *

      NOTE: This method changed in Lang version 2.0. - * It no longer performs a word based alorithm. + * It no longer performs a word based algorithm. * If you only use ASCII, you will notice no change. * That functionality is available in WordUtils.

      - * + * * @param str the String to swap case, may be null * @return the changed String, null if null String input */ @@ -3570,7 +5261,7 @@ if (str == null || (strLen = str.length()) == 0) { return str; } - StringBuffer buffer = new StringBuffer(strLen); + StrBuilder buffer = new StrBuilder(strLen); char ch = 0; for (int i = 0; i < strLen; i++) { @@ -3609,7 +5300,7 @@ *

      Counts how many times the substring appears in the larger String.

      * *

      A null or empty ("") String input returns 0.

      - * + * *
            * StringUtils.countMatches(null, *)       = 0
            * StringUtils.countMatches("", *)         = 0
      @@ -3622,15 +5313,15 @@
            *
            * @param str  the String to check, may be null
            * @param sub  the substring to count, may be null
      -     * @return the number of occurances, 0 if either String is null
      +     * @return the number of occurrences, 0 if either String is null
            */
           public static int countMatches(String str, String sub) {
      -        if (str == null || str.length() == 0 || sub == null || sub.length() == 0) {
      +        if (isEmpty(str) || isEmpty(sub)) {
                   return 0;
               }
               int count = 0;
               int idx = 0;
      -        while ((idx = str.indexOf(sub, idx)) != -1) {
      +        while ((idx = str.indexOf(sub, idx)) != INDEX_NOT_FOUND) {
                   count++;
                   idx += sub.length();
               }
      @@ -3643,8 +5334,8 @@
            * 

      Checks if the String contains only unicode letters.

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isAlpha(null)   = false
            * StringUtils.isAlpha("")     = true
      @@ -3675,8 +5366,8 @@
            * space (' ').

      * *

      null will return false - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isAlphaSpace(null)   = false
            * StringUtils.isAlphaSpace("")     = true
      @@ -3697,8 +5388,7 @@
               }
               int sz = str.length();
               for (int i = 0; i < sz; i++) {
      -            if ((Character.isLetter(str.charAt(i)) == false) &&
      -                (str.charAt(i) != ' ')) {
      +            if ((Character.isLetter(str.charAt(i)) == false) && (str.charAt(i) != ' ')) {
                       return false;
                   }
               }
      @@ -3709,8 +5399,8 @@
            * 

      Checks if the String contains only unicode letters or digits.

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isAlphanumeric(null)   = false
            * StringUtils.isAlphanumeric("")     = true
      @@ -3743,8 +5433,8 @@
            * or space (' ').

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isAlphanumeric(null)   = false
            * StringUtils.isAlphanumeric("")     = true
      @@ -3765,21 +5455,58 @@
               }
               int sz = str.length();
               for (int i = 0; i < sz; i++) {
      -            if ((Character.isLetterOrDigit(str.charAt(i)) == false) &&
      -                (str.charAt(i) != ' ')) {
      +            if ((Character.isLetterOrDigit(str.charAt(i)) == false) && (str.charAt(i) != ' ')) {
                       return false;
                   }
               }
               return true;
           }
       
           /**
      +     * 

      Checks if the string contains only ASCII printable characters.

      + * + *

      null will return false. + * An empty String (length()=0) will return true.

      + * + *
      +     * StringUtils.isAsciiPrintable(null)     = false
      +     * StringUtils.isAsciiPrintable("")       = true
      +     * StringUtils.isAsciiPrintable(" ")      = true
      +     * StringUtils.isAsciiPrintable("Ceki")   = true
      +     * StringUtils.isAsciiPrintable("ab2c")   = true
      +     * StringUtils.isAsciiPrintable("!ab-c~") = true
      +     * StringUtils.isAsciiPrintable("\u0020") = true
      +     * StringUtils.isAsciiPrintable("\u0021") = true
      +     * StringUtils.isAsciiPrintable("\u007e") = true
      +     * StringUtils.isAsciiPrintable("\u007f") = false
      +     * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
      +     * 
      + * + * @param str the string to check, may be null + * @return true if every character is in the range + * 32 thru 126 + * @since 2.1 + */ + public static boolean isAsciiPrintable(String str) { + if (str == null) { + return false; + } + int sz = str.length(); + for (int i = 0; i < sz; i++) { + if (CharUtils.isAsciiPrintable(str.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** *

      Checks if the String contains only unicode digits. * A decimal point is not a unicode digit and returns false.

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isNumeric(null)   = false
            * StringUtils.isNumeric("")     = true
      @@ -3813,8 +5540,8 @@
            * A decimal point is not a unicode digit and returns false.

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isNumeric(null)   = false
            * StringUtils.isNumeric("")     = true
      @@ -3836,8 +5563,7 @@
               }
               int sz = str.length();
               for (int i = 0; i < sz; i++) {
      -            if ((Character.isDigit(str.charAt(i)) == false) &&
      -                (str.charAt(i) != ' ')) {
      +            if ((Character.isDigit(str.charAt(i)) == false) && (str.charAt(i) != ' ')) {
                       return false;
                   }
               }
      @@ -3848,8 +5574,8 @@
            * 

      Checks if the String contains only whitespace.

      * *

      null will return false. - * An empty String ("") will return true.

      - * + * An empty String (length()=0) will return true.

      + * *
            * StringUtils.isWhitespace(null)   = false
            * StringUtils.isWhitespace("")     = true
      @@ -3869,77 +5595,182 @@
               }
               int sz = str.length();
               for (int i = 0; i < sz; i++) {
      -            if ((Character.isWhitespace(str.charAt(i)) == false) ) {
      +            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                       return false;
                   }
               }
               return true;
           }
       
      +    /**
      +     * 

      Checks if the String contains only lowercase characters.

      + * + *

      null will return false. + * An empty String (length()=0) will return false.

      + * + *
      +     * StringUtils.isAllLowerCase(null)   = false
      +     * StringUtils.isAllLowerCase("")     = false
      +     * StringUtils.isAllLowerCase("  ")   = false
      +     * StringUtils.isAllLowerCase("abc")  = true
      +     * StringUtils.isAllLowerCase("abC") = false
      +     * 
      + * + * @param str the String to check, may be null + * @return true if only contains lowercase characters, and is non-null + * @since 2.5 + */ + public static boolean isAllLowerCase(String str) { + if (str == null || isEmpty(str)) { + return false; + } + int sz = str.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLowerCase(str.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

      Checks if the String contains only uppercase characters.

      + * + *

      null will return false. + * An empty String (length()=0) will return false.

      + * + *
      +     * StringUtils.isAllUpperCase(null)   = false
      +     * StringUtils.isAllUpperCase("")     = false
      +     * StringUtils.isAllUpperCase("  ")   = false
      +     * StringUtils.isAllUpperCase("ABC")  = true
      +     * StringUtils.isAllUpperCase("aBC") = false
      +     * 
      + * + * @param str the String to check, may be null + * @return true if only contains uppercase characters, and is non-null + * @since 2.5 + */ + public static boolean isAllUpperCase(String str) { + if (str == null || isEmpty(str)) { + return false; + } + int sz = str.length(); + for (int i = 0; i < sz; i++) { + if (Character.isUpperCase(str.charAt(i)) == false) { + return false; + } + } + return true; + } + // Defaults //----------------------------------------------------------------------- /** - *

      Returns either the passed in String, + *

      Returns either the passed in String, * or if the String is null, an empty String ("").

      - * + * *
            * StringUtils.defaultString(null)  = ""
            * StringUtils.defaultString("")    = ""
            * StringUtils.defaultString("bat") = "bat"
            * 
      - * + * * @see ObjectUtils#toString(Object) * @see String#valueOf(Object) * @param str the String to check, may be null * @return the passed in String, or the empty String if it * was null */ public static String defaultString(String str) { - return (str == null ? EMPTY : str); + return str == null ? EMPTY : str; } /** - *

      Returns either the passed in String, - * or if the String is null, an empty String ("").

      - * + *

      Returns either the passed in String, or if the String is + * null, the value of defaultStr.

      + * *
      -     * StringUtils.defaultString(null, "null")  = "null"
      -     * StringUtils.defaultString("", "null")    = ""
      -     * StringUtils.defaultString("bat", "null") = "bat"
      +     * StringUtils.defaultString(null, "NULL")  = "NULL"
      +     * StringUtils.defaultString("", "NULL")    = ""
      +     * StringUtils.defaultString("bat", "NULL") = "bat"
            * 
      - * + * * @see ObjectUtils#toString(Object,String) * @see String#valueOf(Object) * @param str the String to check, may be null - * @param defaultStr the default String to return + * @param defaultStr the default String to return * if the input is null, may be null * @return the passed in String, or the default if it was null */ public static String defaultString(String str, String defaultStr) { - return (str == null ? defaultStr : str); + return str == null ? defaultStr : str; } + /** + *

      Returns either the passed in String, or if the String is + * whitespace, empty ("") or null, the value of defaultStr.

      + * + *
      +     * StringUtils.defaultIfBlank(null, "NULL")  = "NULL"
      +     * StringUtils.defaultIfBlank("", "NULL")    = "NULL"
      +     * StringUtils.defaultIfBlank(" ", "NULL")   = "NULL"
      +     * StringUtils.defaultIfBlank("bat", "NULL") = "bat"
      +     * StringUtils.defaultIfBlank("", null)      = null
      +     * 
      + * @param str the String to check, may be null + * @param defaultStr the default String to return + * if the input is whitespace, empty ("") or null, may be null + * @return the passed in String, or the default + * @see StringUtils#defaultString(String, String) + * @since 2.6 + */ + public static String defaultIfBlank(String str, String defaultStr) { + return StringUtils.isBlank(str) ? defaultStr : str; + } + + /** + *

      Returns either the passed in String, or if the String is + * empty or null, the value of defaultStr.

      + * + *
      +     * StringUtils.defaultIfEmpty(null, "NULL")  = "NULL"
      +     * StringUtils.defaultIfEmpty("", "NULL")    = "NULL"
      +     * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
      +     * StringUtils.defaultIfEmpty("", null)      = null
      +     * 
      + * + * @param str the String to check, may be null + * @param defaultStr the default String to return + * if the input is empty ("") or null, may be null + * @return the passed in String, or the default + * @see StringUtils#defaultString(String, String) + */ + public static String defaultIfEmpty(String str, String defaultStr) { + return StringUtils.isEmpty(str) ? defaultStr : str; + } + // Reversing //----------------------------------------------------------------------- /** - *

      Reverses a String as per {@link StringBuffer#reverse()}.

      + *

      Reverses a String as per {@link StrBuilder#reverse()}.

      * - *

      null String returns null.

      - * + *

      A null String returns null.

      + * *
            * StringUtils.reverse(null)  = null
            * StringUtils.reverse("")    = ""
            * StringUtils.reverse("bat") = "tab"
            * 
      - * + * * @param str the String to reverse, may be null * @return the reversed String, null if null String input */ public static String reverse(String str) { if (str == null) { return null; } - return new StringBuffer(str).reverse().toString(); + return new StrBuilder(str).reverse().toString(); } /** @@ -3948,14 +5779,14 @@ *

      The Strings between the delimiters are not reversed. * Thus java.lang.String becomes String.lang.java (if the delimiter * is '.').

      - * + * *
            * StringUtils.reverseDelimited(null, *)      = null
            * StringUtils.reverseDelimited("", *)        = ""
            * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c"
            * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a"
            * 
      - * + * * @param str the String to reverse, may be null * @param separatorChar the separator character to use * @return the reversed String, null if null String input @@ -3965,7 +5796,7 @@ if (str == null) { return null; } - // could implement manually, but simple way is to reuse other, + // could implement manually, but simple way is to reuse other, // probably slower, methods. String[] strs = split(str, separatorChar); ArrayUtils.reverse(strs); @@ -3978,27 +5809,27 @@ *

      The Strings between the delimiters are not reversed. * Thus java.lang.String becomes String.lang.java (if the delimiter * is ".").

      - * + * *
            * StringUtils.reverseDelimitedString(null, *)       = null
            * StringUtils.reverseDelimitedString("",*)          = ""
            * StringUtils.reverseDelimitedString("a.b.c", null) = "a.b.c"
            * StringUtils.reverseDelimitedString("a.b.c", ".")  = "c.b.a"
            * 
      - * + * * @param str the String to reverse, may be null * @param separatorChars the separator characters to use, null treated as whitespace * @return the reversed String, null if null String input * @deprecated Use {@link #reverseDelimited(String, char)} instead. * This method is broken as the join doesn't know which char to use. * Method will be removed in Commons Lang 3.0. - * + * */ public static String reverseDelimitedString(String str, String separatorChars) { if (str == null) { return null; } - // could implement manually, but simple way is to reuse other, + // could implement manually, but simple way is to reuse other, // probably slower, methods. String[] strs = split(str, separatorChars); ArrayUtils.reverse(strs); @@ -4011,7 +5842,7 @@ // Abbreviating //----------------------------------------------------------------------- /** - *

      Abbreviates a String using ellipses. This will turn + *

      Abbreviates a String using ellipses. This will turn * "Now is the time for all good men" into "Now is the time for..."

      * *

      Specifically: @@ -4047,7 +5878,7 @@ } /** - *

      Abbreviates a String using ellipses. This will turn + *

      Abbreviates a String using ellipses. This will turn * "Now is the time for all good men" into "...is the time for..."

      * *

      Works like abbreviate(String, int), but allows you to specify @@ -4108,7 +5939,57 @@ } return "..." + str.substring(str.length() - (maxWidth - 3)); } + + /** + *

      Abbreviates a String to the length passed, replacing the middle characters with the supplied + * replacement String.

      + * + *

      This abbreviation only occurs if the following criteria is met: + *

        + *
      • Neither the String for abbreviation nor the replacement String are null or empty
      • + *
      • The length to truncate to is less than the length of the supplied String
      • + *
      • The length to truncate to is greater than 0
      • + *
      • The abbreviated String will have enough room for the length supplied replacement String + * and the first and last characters of the supplied String for abbreviation
      • + *
      + * Otherwise, the returned String will be the same as the supplied String for abbreviation. + *

      + * + *
      +     * StringUtils.abbreviateMiddle(null, null, 0)      = null
      +     * StringUtils.abbreviateMiddle("abc", null, 0)      = "abc"
      +     * StringUtils.abbreviateMiddle("abc", ".", 0)      = "abc"
      +     * StringUtils.abbreviateMiddle("abc", ".", 3)      = "abc"
      +     * StringUtils.abbreviateMiddle("abcdef", ".", 4)     = "ab.f"
      +     * 
      + * + * @param str the String to abbreviate, may be null + * @param middle the String to replace the middle characters with, may be null + * @param length the length to abbreviate str to. + * @return the abbreviated String if the above criteria is met, or the original String supplied for abbreviation. + * @since 2.5 + */ + public static String abbreviateMiddle(String str, String middle, int length) { + if (isEmpty(str) || isEmpty(middle)) { + return str; + } + + if (length >= str.length() || length < (middle.length()+2)) { + return str; + } + int targetSting = length-middle.length(); + int startOffset = targetSting/2+targetSting%2; + int endOffset = str.length()-targetSting/2; + + StrBuilder builder = new StrBuilder(length); + builder.append(str.substring(0,startOffset)); + builder.append(middle); + builder.append(str.substring(endOffset)); + + return builder.toString(); + } + // Difference //----------------------------------------------------------------------- /** @@ -4132,7 +6013,7 @@ * * @param str1 the first String, may be null * @param str2 the second String, may be null - * @return the portion of str2 where it differs from str1; returns the + * @return the portion of str2 where it differs from str1; returns the * empty String if they are equal * @since 2.0 */ @@ -4144,7 +6025,7 @@ return str1; } int at = indexOfDifference(str1, str2); - if (at == -1) { + if (at == INDEX_NOT_FOUND) { return EMPTY; } return str2.substring(at); @@ -4153,8 +6034,8 @@ /** *

      Compares two Strings, and returns the index at which the * Strings begin to differ.

      - * - *

      For example, + * + *

      For example, * indexOfDifference("i am a machine", "i am a robot") -> 7

      * *
      @@ -4175,7 +6056,7 @@
            */
           public static int indexOfDifference(String str1, String str2) {
               if (str1 == str2) {
      -            return -1;
      +            return INDEX_NOT_FOUND;
               }
               if (str1 == null || str2 == null) {
                   return 0;
      @@ -4189,22 +6070,168 @@
               if (i < str2.length() || i < str1.length()) {
                   return i;
               }
      -        return -1;
      +        return INDEX_NOT_FOUND;
           }
       
      +    /**
      +     * 

      Compares all Strings in an array and returns the index at which the + * Strings begin to differ.

      + * + *

      For example, + * indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7

      + * + *
      +     * StringUtils.indexOfDifference(null) = -1
      +     * StringUtils.indexOfDifference(new String[] {}) = -1
      +     * StringUtils.indexOfDifference(new String[] {"abc"}) = -1
      +     * StringUtils.indexOfDifference(new String[] {null, null}) = -1
      +     * StringUtils.indexOfDifference(new String[] {"", ""}) = -1
      +     * StringUtils.indexOfDifference(new String[] {"", null}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0
      +     * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1
      +     * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1
      +     * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2
      +     * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
      +     * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0
      +     * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
      +     * 
      + * + * @param strs array of strings, entries may be null + * @return the index where the strings begin to differ; -1 if they are all equal + * @since 2.4 + */ + public static int indexOfDifference(String[] strs) { + if (strs == null || strs.length <= 1) { + return INDEX_NOT_FOUND; + } + boolean anyStringNull = false; + boolean allStringsNull = true; + int arrayLen = strs.length; + int shortestStrLen = Integer.MAX_VALUE; + int longestStrLen = 0; + // find the min and max string lengths; this avoids checking to make + // sure we are not exceeding the length of the string each time through + // the bottom loop. + for (int i = 0; i < arrayLen; i++) { + if (strs[i] == null) { + anyStringNull = true; + shortestStrLen = 0; + } else { + allStringsNull = false; + shortestStrLen = Math.min(strs[i].length(), shortestStrLen); + longestStrLen = Math.max(strs[i].length(), longestStrLen); + } + } + + // handle lists containing all nulls or all empty strings + if (allStringsNull || (longestStrLen == 0 && !anyStringNull)) { + return INDEX_NOT_FOUND; + } + + // handle lists containing some nulls or some empty strings + if (shortestStrLen == 0) { + return 0; + } + + // find the position with the first difference across all strings + int firstDiff = -1; + for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) { + char comparisonChar = strs[0].charAt(stringPos); + for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) { + if (strs[arrayPos].charAt(stringPos) != comparisonChar) { + firstDiff = stringPos; + break; + } + } + if (firstDiff != -1) { + break; + } + } + + if (firstDiff == -1 && shortestStrLen != longestStrLen) { + // we compared all of the characters up to the length of the + // shortest string and didn't find a match, but the string lengths + // vary, so return the length of the shortest string. + return shortestStrLen; + } + return firstDiff; + } + + /** + *

      Compares all Strings in an array and returns the initial sequence of + * characters that is common to all of them.

      + * + *

      For example, + * getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "

      + * + *
      +     * StringUtils.getCommonPrefix(null) = ""
      +     * StringUtils.getCommonPrefix(new String[] {}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
      +     * StringUtils.getCommonPrefix(new String[] {null, null}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"", null}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
      +     * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
      +     * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
      +     * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
      +     * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
      +     * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
      +     * 
      + * + * @param strs array of String objects, entries may be null + * @return the initial sequence of characters that are common to all Strings + * in the array; empty String if the array is null, the elements are all null + * or if there is no common prefix. + * @since 2.4 + */ + public static String getCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return EMPTY; + } + int smallestIndexOfDiff = indexOfDifference(strs); + if (smallestIndexOfDiff == INDEX_NOT_FOUND) { + // all strings were identical + if (strs[0] == null) { + return EMPTY; + } + return strs[0]; + } else if (smallestIndexOfDiff == 0) { + // there were no common initial characters + return EMPTY; + } else { + // we found a common initial character sequence + return strs[0].substring(0, smallestIndexOfDiff); + } + } + // Misc //----------------------------------------------------------------------- /** *

      Find the Levenshtein distance between two Strings.

      * *

      This is the number of changes needed to change one String into - * another, where each change is a single character modification (deletion, + * another, where each change is a single character modification (deletion, * insertion or substitution).

      * - *

      This implementation of the Levenshtein distance algorithm - * is from http://www.merriampark.com/ld.htm

      + *

      The previous implementation of the Levenshtein distance algorithm + * was from http://www.merriampark.com/ld.htm

      * + *

      Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError + * which can occur when my Java implementation is used with very large strings.
      + * This implementation of the Levenshtein distance algorithm + * is from http://www.merriampark.com/ldjava.htm

      + * *
            * StringUtils.getLevenshteinDistance(null, *)             = IllegalArgumentException
            * StringUtils.getLevenshteinDistance(*, null)             = IllegalArgumentException
      @@ -4218,7 +6245,7 @@
            * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8
            * StringUtils.getLevenshteinDistance("hello", "hallo")    = 1
            * 
      - * + * * @param s the first String, must not be null * @param t the second String, must not be null * @return result distance @@ -4228,77 +6255,340 @@ if (s == null || t == null) { throw new IllegalArgumentException("Strings must not be null"); } - int d[][]; // matrix - int n; // length of s - int m; // length of t - int i; // iterates through s - int j; // iterates through t - char s_i; // ith character of s - char t_j; // jth character of t - int cost; // cost - // Step 1 - n = s.length(); - m = t.length(); + /* + The difference between this impl. and the previous is that, rather + than creating and retaining a matrix of size s.length()+1 by t.length()+1, + we maintain two single-dimensional arrays of length s.length()+1. The first, d, + is the 'current working' distance array that maintains the newest distance cost + counts as we iterate through the characters of String s. Each time we increment + the index of String t we are comparing, d is copied to p, the second int[]. Doing so + allows us to retain the previous cost counts as required by the algorithm (taking + the minimum of the cost count to the left, up one, and diagonally up and to the left + of the current cost count being calculated). (Note that the arrays aren't really + copied anymore, just switched...this is clearly much better than cloning an array + or doing a System.arraycopy() each time through the outer loop.) + + Effectively, the difference between the two implementations is this one does not + cause an out of memory condition when calculating the LD over two very large strings. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + if (n == 0) { return m; - } - if (m == 0) { + } else if (m == 0) { return n; } - d = new int[n + 1][m + 1]; - // Step 2 - for (i = 0; i <= n; i++) { - d[i][0] = i; + if (n > m) { + // swap the input strings to consume less memory + String tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); } - for (j = 0; j <= m; j++) { - d[0][j] = j; + int p[] = new int[n+1]; //'previous' cost array, horizontally + int d[] = new int[n+1]; // cost array, horizontally + int _d[]; //placeholder to assist in swapping p and d + + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + + char t_j; // jth character of t + + int cost; // cost + + for (i = 0; i<=n; i++) { + p[i] = i; } - // Step 3 - for (i = 1; i <= n; i++) { - s_i = s.charAt(i - 1); + for (j = 1; j<=m; j++) { + t_j = t.charAt(j-1); + d[0] = j; - // Step 4 - for (j = 1; j <= m; j++) { - t_j = t.charAt(j - 1); + for (i=1; i<=n; i++) { + cost = s.charAt(i-1)==t_j ? 0 : 1; + // minimum of cell to the left+1, to the top+1, diagonally left and up +cost + d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+cost); + } - // Step 5 - if (s_i == t_j) { - cost = 0; - } else { - cost = 1; - } + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } - // Step 6 - d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost); + // our last action in the above loop was to switch d and p, so p now + // actually has the most recent cost counts + return p[n]; + } + + // startsWith + //----------------------------------------------------------------------- + + /** + *

      Check if a String starts with a specified prefix.

      + * + *

      nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

      + * + *
      +     * StringUtils.startsWith(null, null)      = true
      +     * StringUtils.startsWith(null, "abc")     = false
      +     * StringUtils.startsWith("abcdef", null)  = false
      +     * StringUtils.startsWith("abcdef", "abc") = true
      +     * StringUtils.startsWith("ABCDEF", "abc") = false
      +     * 
      + * + * @see java.lang.String#startsWith(String) + * @param str the String to check, may be null + * @param prefix the prefix to find, may be null + * @return true if the String starts with the prefix, case sensitive, or + * both null + * @since 2.4 + */ + public static boolean startsWith(String str, String prefix) { + return startsWith(str, prefix, false); + } + + /** + *

      Case insensitive check if a String starts with a specified prefix.

      + * + *

      nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case insensitive.

      + * + *
      +     * StringUtils.startsWithIgnoreCase(null, null)      = true
      +     * StringUtils.startsWithIgnoreCase(null, "abc")     = false
      +     * StringUtils.startsWithIgnoreCase("abcdef", null)  = false
      +     * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
      +     * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
      +     * 
      + * + * @see java.lang.String#startsWith(String) + * @param str the String to check, may be null + * @param prefix the prefix to find, may be null + * @return true if the String starts with the prefix, case insensitive, or + * both null + * @since 2.4 + */ + public static boolean startsWithIgnoreCase(String str, String prefix) { + return startsWith(str, prefix, true); + } + + /** + *

      Check if a String starts with a specified prefix (optionally case insensitive).

      + * + * @see java.lang.String#startsWith(String) + * @param str the String to check, may be null + * @param prefix the prefix to find, may be null + * @param ignoreCase inidicates whether the compare should ignore case + * (case insensitive) or not. + * @return true if the String starts with the prefix or + * both null + */ + private static boolean startsWith(String str, String prefix, boolean ignoreCase) { + if (str == null || prefix == null) { + return (str == null && prefix == null); + } + if (prefix.length() > str.length()) { + return false; + } + return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); + } + + /** + *

      Check if a String starts with any of an array of specified strings.

      + * + *
      +     * StringUtils.startsWithAny(null, null)      = false
      +     * StringUtils.startsWithAny(null, new String[] {"abc"})  = false
      +     * StringUtils.startsWithAny("abcxyz", null)     = false
      +     * StringUtils.startsWithAny("abcxyz", new String[] {""}) = false
      +     * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true
      +     * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
      +     * 
      + * + * @see #startsWith(String, String) + * @param string the String to check, may be null + * @param searchStrings the Strings to find, may be null or empty + * @return true if the String starts with any of the the prefixes, case insensitive, or + * both null + * @since 2.5 + */ + public static boolean startsWithAny(String string, String[] searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (int i = 0; i < searchStrings.length; i++) { + String searchString = searchStrings[i]; + if (StringUtils.startsWith(string, searchString)) { + return true; } } + return false; + } - // Step 7 - return d[n][m]; + // endsWith + //----------------------------------------------------------------------- + + /** + *

      Check if a String ends with a specified suffix.

      + * + *

      nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case sensitive.

      + * + *
      +     * StringUtils.endsWith(null, null)      = true
      +     * StringUtils.endsWith(null, "def")     = false
      +     * StringUtils.endsWith("abcdef", null)  = false
      +     * StringUtils.endsWith("abcdef", "def") = true
      +     * StringUtils.endsWith("ABCDEF", "def") = false
      +     * StringUtils.endsWith("ABCDEF", "cde") = false
      +     * 
      + * + * @see java.lang.String#endsWith(String) + * @param str the String to check, may be null + * @param suffix the suffix to find, may be null + * @return true if the String ends with the suffix, case sensitive, or + * both null + * @since 2.4 + */ + public static boolean endsWith(String str, String suffix) { + return endsWith(str, suffix, false); } /** - *

      Gets the minimum of three int values.

      + *

      Case insensitive check if a String ends with a specified suffix.

      + * + *

      nulls are handled without exceptions. Two null + * references are considered to be equal. The comparison is case insensitive.

      + * + *
      +     * StringUtils.endsWithIgnoreCase(null, null)      = true
      +     * StringUtils.endsWithIgnoreCase(null, "def")     = false
      +     * StringUtils.endsWithIgnoreCase("abcdef", null)  = false
      +     * StringUtils.endsWithIgnoreCase("abcdef", "def") = true
      +     * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true
      +     * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false
      +     * 
      + * + * @see java.lang.String#endsWith(String) + * @param str the String to check, may be null + * @param suffix the suffix to find, may be null + * @return true if the String ends with the suffix, case insensitive, or + * both null + * @since 2.4 + */ + public static boolean endsWithIgnoreCase(String str, String suffix) { + return endsWith(str, suffix, true); + } + + /** + *

      Check if a String ends with a specified suffix (optionally case insensitive).

      + * + * @see java.lang.String#endsWith(String) + * @param str the String to check, may be null + * @param suffix the suffix to find, may be null + * @param ignoreCase inidicates whether the compare should ignore case + * (case insensitive) or not. + * @return true if the String starts with the prefix or + * both null + */ + private static boolean endsWith(String str, String suffix, boolean ignoreCase) { + if (str == null || suffix == null) { + return (str == null && suffix == null); + } + if (suffix.length() > str.length()) { + return false; + } + int strOffset = str.length() - suffix.length(); + return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length()); + } + + /** + *

      + * Similar to http://www.w3.org/TR/xpath/#function-normalize + * -space + *

      + *

      + * The function returns the argument string with whitespace normalized by using + * {@link #trim(String)} to remove leading and trailing whitespace + * and then replacing sequences of whitespace characters by a single space. + *

      + * In XML Whitespace characters are the same as those allowed by the S production, which is S ::= (#x20 | #x9 | #xD | #xA)+ + *

      + * See Java's {@link Character#isWhitespace(char)} for which characters are considered whitespace. + *

      + * The difference is that Java's whitespace includes vertical tab and form feed, which this functional will also + * normalize. Additonally {@link #trim(String)} removes control characters (char <= 32) from both + * ends of this String. + *

      + * + * @see Character#isWhitespace(char) + * @see #trim(String) + * @see + * http://www.w3.org/TR/xpath/#function-normalize-space + * @param str the source String to normalize whitespaces from, may be null + * @return the modified string with whitespace normalized, null if null String input * - * @param a value 1 - * @param b value 2 - * @param c value 3 - * @return the smallest of the values + * @since 2.6 */ - private static int min(int a, int b, int c) { - // Method copied from NumberUtils to avoid dependency on subpackage - if (b < a) { - a = b; + public static String normalizeSpace(String str) { + str = strip(str); + if(str == null || str.length() <= 2) { + return str; } - if (c < a) { - a = c; + StrBuilder b = new StrBuilder(str.length()); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (Character.isWhitespace(c)) { + if (i > 0 && !Character.isWhitespace(str.charAt(i - 1))) { + b.append(' '); + } + } else { + b.append(c); + } } - return a; + return b.toString(); } -} + /** + *

      Check if a String ends with any of an array of specified strings.

      + * + *
      +     * StringUtils.endsWithAny(null, null)      = false
      +     * StringUtils.endsWithAny(null, new String[] {"abc"})  = false
      +     * StringUtils.endsWithAny("abcxyz", null)     = false
      +     * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true
      +     * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true
      +     * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
      +     * 
      + * + * @param string the String to check, may be null + * @param searchStrings the Strings to find, may be null or empty + * @return true if the String ends with any of the the prefixes, case insensitive, or + * both null + * @since 2.6 + */ + public static boolean endsWithAny(String string, String[] searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (int i = 0; i < searchStrings.length; i++) { + String searchString = searchStrings[i]; + if (StringUtils.endsWith(string, searchString)) { + return true; + } + } + return false; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/SystemUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/SystemUtils.java (.../SystemUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/SystemUtils.java (.../SystemUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,720 +1,1392 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; +import java.io.File; + /** - *

      Helpers for java.lang.System.

      + *

      + * Helpers for java.lang.System. + *

      * - *

      If a system property cannot be read due to security restrictions, + *

      + * If a system property cannot be read due to security restrictions, * the corresponding field in this class will be set to null - * and a message will be written to System.err.

      - * + * and a message will be written to System.err. + *

      + * + *

      + * #ThreadSafe# + *

      + * + * @author Apache Software Foundation * @author Based on code from Avalon Excalibur * @author Based on code from Lucene - * @author Stephen Colebourne * @author Steve Downey * @author Gary Gregory * @author Michael Becke * @author Tetsuya Kaneuchi + * @author Rafal Krupinski + * @author Jason Gritman * @since 1.0 * @version $Id$ */ public class SystemUtils { + private static final int JAVA_VERSION_TRIM_SIZE = 3; + + /** + * The prefix String for all Windows OS. + */ + private static final String OS_NAME_WINDOWS_PREFIX = "Windows"; + // System property constants - //----------------------------------------------------------------------- + // ----------------------------------------------------------------------- // These MUST be declared first. Other constants depend on this. - + /** - *

      The file.encoding System Property.

      - *

      File encoding, such as Cp1252.

      + * The System property key for the user home directory. + */ + private static final String USER_HOME_KEY = "user.home"; + + /** + * The System property key for the user directory. + */ + private static final String USER_DIR_KEY = "user.dir"; + + /** + * The System property key for the Java IO temporary directory. + */ + private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir"; + + /** + * The System property key for the Java home directory. + */ + private static final String JAVA_HOME_KEY = "java.home"; + + /** + *

      + * The awt.toolkit System Property. + *

      + *

      + * Holds a class name, on Windows XP this is sun.awt.windows.WToolkit. + *

      + *

      + * On platforms without a GUI, this value is null. + *

      * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will + * be out of sync with that System property. + *

      + * + * @since 2.1 + */ + public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit"); + + /** + *

      + * The file.encoding System Property. + *

      + *

      + * File encoding, such as Cp1252. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 - * @since Java 1.2. + * @since Java 1.2 */ public static final String FILE_ENCODING = getSystemProperty("file.encoding"); /** - *

      The file.separator System Property. - * File separator ("/" on UNIX).

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The file.separator System Property. File separator ("/" on UNIX). + *

      * - * @since Java 1.1. + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since Java 1.1 */ public static final String FILE_SEPARATOR = getSystemProperty("file.separator"); /** - *

      The java.class.path System Property. Java class path.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.awt.fonts System Property. + *

      * - * @since Java 1.1. + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since 2.1 */ + public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts"); + + /** + *

      + * The java.awt.graphicsenv System Property. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since 2.1 + */ + public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv"); + + /** + *

      + * The java.awt.headless System Property. + * The value of this property is the String "true" or "false". + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @see #isJavaAwtHeadless() + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless"); + + /** + *

      + * The java.awt.printerjob System Property. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since 2.1 + */ + public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob"); + + /** + *

      + * The java.class.path System Property. Java class path. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since Java 1.1 + */ public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path"); /** - *

      The java.class.version System Property. - * Java class format version number.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.class.version System Property. Java class format version number. + *

      * - * @since Java 1.1. + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since Java 1.1 */ public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version"); /** - *

      The java.compiler System Property. Name of JIT compiler to use. - * First in JDK version 1.2. Not used in Sun JDKs after 1.2.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.compiler System Property. Name of JIT compiler to use. + * First in JDK version 1.2. Not used in Sun JDKs after 1.2. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2. Not used in Sun versions after 1.2. */ public static final String JAVA_COMPILER = getSystemProperty("java.compiler"); /** - *

      The java.ext.dirs System Property. Path of extension directory - * or directories.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.endorsed.dirs System Property. Path of endorsed directory or directories. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since Java 1.4 + */ + public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs"); + + /** + *

      + * The java.ext.dirs System Property. Path of extension directory or directories. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.3 */ public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs"); /** - *

      The java.home System Property. Java installation directory.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.home System Property. Java installation directory. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ - public static final String JAVA_HOME = getSystemProperty("java.home"); + public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY); /** - *

      The java.io.tmpdir System Property. Default temp file path.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.io.tmpdir System Property. Default temp file path. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ - public static final String JAVA_IO_TMPDIR = getSystemProperty("java.io.tmpdir"); + public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY); /** - *

      The java.library.path System Property. List of paths to search - * when loading libraries.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.library.path System Property. List of paths to search when loading libraries. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path"); /** - *

      The java.runtime.name System Property. Java Runtime Environment - * name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.runtime.name System Property. Java Runtime Environment name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 * @since Java 1.3 */ public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name"); /** - *

      The java.runtime.version System Property. Java Runtime Environment - * version.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.runtime.version System Property. Java Runtime Environment version. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 * @since Java 1.3 */ public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version"); /** - *

      The java.specification.name System Property. Java Runtime Environment - * specification name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.specification.name System Property. Java Runtime Environment specification name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name"); /** - *

      The java.specification.vendor System Property. Java Runtime Environment - * specification vendor.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.specification.vendor System Property. Java Runtime Environment specification vendor. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor"); /** - *

      The java.specification.version System Property. Java Runtime Environment - * specification version.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.specification.version System Property. Java Runtime Environment specification version. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.3 */ public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); /** - *

      The java.vendor System Property. Java vendor-specific string.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.util.prefs.PreferencesFactory System Property. A class name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY = + getSystemProperty("java.util.prefs.PreferencesFactory"); + + /** + *

      + * The java.vendor System Property. Java vendor-specific string. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String JAVA_VENDOR = getSystemProperty("java.vendor"); /** - *

      The java.vendor.url System Property. Java vendor URL.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      - * + *

      + * The java.vendor.url System Property. Java vendor URL. + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 - */ + */ public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url"); /** - *

      The java.version System Property. Java version number.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.version System Property. Java version number. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String JAVA_VERSION = getSystemProperty("java.version"); /** - *

      The java.vm.info System Property. Java Virtual Machine implementation - * info.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.info System Property. Java Virtual Machine implementation info. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 * @since Java 1.2 */ public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info"); /** - *

      The java.vm.name System Property. Java Virtual Machine implementation - * name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.name System Property. Java Virtual Machine implementation name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name"); /** - *

      The java.vm.specification.name System Property. Java Virtual Machine - * specification name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.specification.name System Property. Java Virtual Machine specification name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name"); /** - *

      The java.vm.specification.vendor System Property. Java Virtual - * Machine specification vendor.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.specification.vendor System Property. Java Virtual Machine specification vendor. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor"); /** - *

      The java.vm.specification.version System Property. Java Virtual Machine - * specification version.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.specification.version System Property. Java Virtual Machine specification version. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version"); /** - *

      The java.vm.vendor System Property. Java Virtual Machine implementation - * vendor.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.vendor System Property. Java Virtual Machine implementation vendor. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor"); /** - *

      The java.vm.version System Property. Java Virtual Machine - * implementation version.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The java.vm.version System Property. Java Virtual Machine implementation version. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.2 */ public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version"); /** - *

      The line.separator System Property. Line separator - * ("\n<" on UNIX).

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The line.separator System Property. Line separator ("\n" on UNIX). + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String LINE_SEPARATOR = getSystemProperty("line.separator"); /** - *

      The os.arch System Property. Operating system architecture.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The os.arch System Property. Operating system architecture. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String OS_ARCH = getSystemProperty("os.arch"); /** - *

      The os.name System Property. Operating system name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The os.name System Property. Operating system name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String OS_NAME = getSystemProperty("os.name"); /** - *

      The os.version System Property. Operating system version.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The os.version System Property. Operating system version. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String OS_VERSION = getSystemProperty("os.version"); /** - *

      The path.separator System Property. Path separator - * (":" on UNIX).

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The path.separator System Property. Path separator (":" on UNIX). + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String PATH_SEPARATOR = getSystemProperty("path.separator"); /** - *

      The user.country or user.region System Property. - * User's country code, such as GB. First in JDK version 1.2 as - * user.region. Renamed to user.country in 1.4

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The user.country or user.region System Property. + * User's country code, such as GB. First in + * Java version 1.2 as user.region. Renamed to user.country in 1.4 + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 * @since Java 1.2 */ public static final String USER_COUNTRY = - (getSystemProperty("user.country") == null ? - getSystemProperty("user.region") : getSystemProperty("user.country")); + getSystemProperty("user.country") == null ? + getSystemProperty("user.region") : getSystemProperty("user.country"); /** - *

      The user.dir System Property. User's current working - * directory.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The user.dir System Property. User's current working directory. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ - public static final String USER_DIR = getSystemProperty("user.dir"); + public static final String USER_DIR = getSystemProperty(USER_DIR_KEY); /** - *

      The user.home System Property. User's home directory.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The user.home System Property. User's home directory. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ - public static final String USER_HOME = getSystemProperty("user.home"); + public static final String USER_HOME = getSystemProperty(USER_HOME_KEY); /** - *

      The user.language System Property. User's language code, - * such as 'en'.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The user.language System Property. User's language code, such as "en". + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since 2.0 * @since Java 1.2 */ public static final String USER_LANGUAGE = getSystemProperty("user.language"); /** - *

      The user.name System Property. User's account name.

      - * - *

      Defaults to null if the runtime does not have - * security access to read this property or the property does not exist.

      + *

      + * The user.name System Property. User's account name. + *

      * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * * @since Java 1.1 */ public static final String USER_NAME = getSystemProperty("user.name"); + /** + *

      + * The user.timezone System Property. For example: "America/Los_Angeles". + *

      + * + *

      + * Defaults to null if the runtime does not have + * security access to read this property or the property does not exist. + *

      + * + *

      + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value + * will be out of sync with that System property. + *

      + * + * @since 2.1 + */ + public static final String USER_TIMEZONE = getSystemProperty("user.timezone"); + // Java version - //----------------------------------------------------------------------- - // These MUST be declared after those above as they depend on the + // ----------------------------------------------------------------------- + // This MUST be declared after those above as it depends on the // values being set up - + /** - *

      Gets the Java version as a float.

      - * - *

      Example return values:

      + *

      + * Gets the Java version as a String trimming leading letters. + *

      + * + *

      + * The field will return null if {@link #JAVA_VERSION} is null. + *

      + * + * @since 2.1 + */ + public static final String JAVA_VERSION_TRIMMED = getJavaVersionTrimmed(); + + // Java version values + // ----------------------------------------------------------------------- + // These MUST be declared after the trim above as they depend on the + // value being set up + + /** + *

      + * Gets the Java version as a float. + *

      + * + *

      + * Example return values: + *

      *
        - *
      • 1.2f for JDK 1.2 - *
      • 1.31f for JDK 1.3.1 + *
      • 1.2f for Java 1.2 + *
      • 1.31f for Java 1.3.1 *
      - * - *

      The field will return zero if {@link #JAVA_VERSION} is null.

      * + *

      + * The field will return zero if {@link #JAVA_VERSION} is null. + *

      + * * @since 2.0 */ public static final float JAVA_VERSION_FLOAT = getJavaVersionAsFloat(); /** - *

      Gets the Java version as an int.

      - * - *

      Example return values:

      + *

      + * Gets the Java version as an int. + *

      + * + *

      + * Example return values: + *

      *
        - *
      • 120 for JDK 1.2 - *
      • 131 for JDK 1.3.1 + *
      • 120 for Java 1.2 + *
      • 131 for Java 1.3.1 *
      - * - *

      The field will return zero if {@link #JAVA_VERSION} is null.

      * + *

      + * The field will return zero if {@link #JAVA_VERSION} is null. + *

      + * * @since 2.0 */ public static final int JAVA_VERSION_INT = getJavaVersionAsInt(); // Java version checks - //----------------------------------------------------------------------- + // ----------------------------------------------------------------------- // These MUST be declared after those above as they depend on the // values being set up - + /** - *

      Is true if this is Java version 1.1 (also 1.1.x versions).

      - * - *

      The field will return false if {@link #JAVA_VERSION} is - * null.

      + *

      + * Is true if this is Java version 1.1 (also 1.1.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      */ public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1"); /** - *

      Is true if this is Java version 1.2 (also 1.2.x versions).

      - * - *

      The field will return false if {@link #JAVA_VERSION} is - * null.

      + *

      + * Is true if this is Java version 1.2 (also 1.2.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      */ public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2"); /** - *

      Is true if this is Java version 1.3 (also 1.3.x versions).

      - * - *

      The field will return false if {@link #JAVA_VERSION} is - * null.

      + *

      + * Is true if this is Java version 1.3 (also 1.3.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      */ public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3"); /** - *

      Is true if this is Java version 1.4 (also 1.4.x versions).

      - * - *

      The field will false false if {@link #JAVA_VERSION} is - * null.

      + *

      + * Is true if this is Java version 1.4 (also 1.4.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      */ public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4"); /** - *

      Is true if this is Java version 1.5 (also 1.5.x versions).

      - * - *

      The field will return false if {@link #JAVA_VERSION} is - * null.

      + *

      + * Is true if this is Java version 1.5 (also 1.5.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      */ public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5"); + /** + *

      + * Is true if this is Java version 1.6 (also 1.6.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      + */ + public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6"); + + /** + *

      + * Is true if this is Java version 1.7 (also 1.7.x versions). + *

      + * + *

      + * The field will return false if {@link #JAVA_VERSION} is null. + *

      + * + * @since 2.5 + */ + public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7"); + // Operating system checks - //----------------------------------------------------------------------- + // ----------------------------------------------------------------------- // These MUST be declared after those above as they depend on the // values being set up // OS names from http://www.vamphq.com/os.html - // Selected ones included - please advise commons-dev@jakarta.apache.org + // Selected ones included - please advise dev@commons.apache.org // if you want another added or a mistake corrected /** - *

      Is true if this is AIX.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is AIX. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_AIX = getOSMatches("AIX"); + public static final boolean IS_OS_AIX = getOSMatchesName("AIX"); /** - *

      Is true if this is HP-UX.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is HP-UX. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_HP_UX = getOSMatches("HP-UX"); + public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX"); /** - *

      Is true if this is Irix.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Irix. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_IRIX = getOSMatches("Irix"); + public static final boolean IS_OS_IRIX = getOSMatchesName("Irix"); /** - *

      Is true if this is Linux.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Linux. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_LINUX = getOSMatches("Linux") || getOSMatches("LINUX"); + public static final boolean IS_OS_LINUX = getOSMatchesName("Linux") || getOSMatchesName("LINUX"); /** - *

      Is true if this is Mac.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Mac. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_MAC = getOSMatches("Mac"); + public static final boolean IS_OS_MAC = getOSMatchesName("Mac"); /** - *

      Is true if this is Mac.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Mac. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_MAC_OSX = getOSMatches("Mac OS X"); + public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X"); /** - *

      Is true if this is OS/2.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is OS/2. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_OS2 = getOSMatches("OS/2"); + public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2"); /** - *

      Is true if this is Solaris.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Solaris. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_SOLARIS = getOSMatches("Solaris"); + public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris"); /** - *

      Is true if this is SunOS.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is SunOS. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_SUN_OS = getOSMatches("SunOS"); + public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS"); /** - *

      Is true if this is Windows.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is a UNIX like system, + * as in any of AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * + * @since 2.1 + */ + public static final boolean IS_OS_UNIX = + IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || + IS_OS_MAC_OSX || IS_OS_SOLARIS || IS_OS_SUN_OS; + + /** + *

      + * Is true if this is Windows. + *

      + * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS = getOSMatches("Windows"); + public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX); /** - *

      Is true if this is Windows 2000.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows 2000. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_2000 = getOSMatches("Windows", "5.0"); + public static final boolean IS_OS_WINDOWS_2000 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.0"); /** - *

      Is true if this is Windows 95.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows 95. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_95 = getOSMatches("Windows 9", "4.0"); - // JDK 1.2 running on Windows98 returns 'Windows 95', hence the above + public static final boolean IS_OS_WINDOWS_95 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.0"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above /** - *

      Is true if this is Windows 98.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows 98. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_98 = getOSMatches("Windows 9", "4.1"); - // JDK 1.2 running on Windows98 returns 'Windows 95', hence the above + public static final boolean IS_OS_WINDOWS_98 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.1"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above /** - *

      Is true if this is Windows ME.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows ME. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_ME = getOSMatches("Windows", "4.9"); - // JDK 1.2 running on WindowsME may return 'Windows 95', hence the above + public static final boolean IS_OS_WINDOWS_ME = getOSMatches(OS_NAME_WINDOWS_PREFIX, "4.9"); + // Java 1.2 running on WindowsME may return 'Windows 95', hence the above /** - *

      Is true if this is Windows NT.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows NT. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_NT = getOSMatches("Windows NT"); - // Windows 2000 returns 'Windows 2000' but may suffer from same JDK1.2 problem + public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " NT"); + // Windows 2000 returns 'Windows 2000' but may suffer from same Java1.2 problem /** - *

      Is true if this is Windows XP.

      - * - *

      The field will return false if OS_NAME is - * null.

      + *

      + * Is true if this is Windows XP. + *

      * + *

      + * The field will return false if OS_NAME is null. + *

      + * * @since 2.0 */ - public static final boolean IS_OS_WINDOWS_XP = getOSMatches("Windows", "5.1"); - // Windows XP returns 'Windows 2000' just for fun... + public static final boolean IS_OS_WINDOWS_XP = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.1"); - //----------------------------------------------------------------------- + // ----------------------------------------------------------------------- /** - *

      SystemUtils instances should NOT be constructed in standard - * programming. Instead, the class should be used as - * SystemUtils.FILE_SEPARATOR.

      - * - *

      This constructor is public to permit tools that require a JavaBean - * instance to operate.

      + *

      + * Is true if this is Windows Vista. + *

      + * + *

      + * The field will return false if OS_NAME is null. + *

      + * + * @since 2.4 */ - public SystemUtils() { + public static final boolean IS_OS_WINDOWS_VISTA = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.0"); + + /** + *

      + * Is true if this is Windows 7. + *

      + * + *

      + * The field will return false if OS_NAME is null. + *

      + * + * @since 2.5 + */ + public static final boolean IS_OS_WINDOWS_7 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.1"); + + /** + *

      + * Gets the Java home directory as a File. + *

      + * + * @return a directory + * @throws SecurityException if a security manager exists and its + * checkPropertyAccess method doesn't allow access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaHome() { + return new File(System.getProperty(JAVA_HOME_KEY)); } - - //----------------------------------------------------------------------- + /** + *

      + * Gets the Java IO temporary directory as a File. + *

      + * + * @return a directory + * @throws SecurityException if a security manager exists and its + * checkPropertyAccess method doesn't allow access to the specified system + * property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaIoTmpDir() { + return new File(System.getProperty(JAVA_IO_TMPDIR_KEY)); + } + + /** *

      Gets the Java version number as a float.

      * *

      Example return values:

      @@ -732,155 +1404,498 @@ } /** - *

      Gets the Java version number as a float.

      - * - *

      Example return values:

      + *

      + * Gets the Java version number as a float. + *

      + * + *

      + * Example return values: + *

      *
        - *
      • 1.2f for JDK 1.2 - *
      • 1.31f for JDK 1.3.1 + *
      • 1.2f for Java 1.2
      • + *
      • 1.31f for Java 1.3.1
      • + *
      • 1.6f for Java 1.6.0_20
      • *
      * - *

      Patch releases are not reported. - * Zero is returned if {@link #JAVA_VERSION} is null.

      + *

      + * Patch releases are not reported. + *

      * - * @return the version, for example 1.31f for JDK 1.3.1 + * @return the version, for example 1.31f for Java 1.3.1 */ private static float getJavaVersionAsFloat() { - if (JAVA_VERSION == null) { - return 0f; - } - String str = JAVA_VERSION.substring(0, 3); - if (JAVA_VERSION.length() >= 5) { - str = str + JAVA_VERSION.substring(4, 5); - } - return Float.parseFloat(str); + return toVersionFloat(toJavaVersionIntArray(SystemUtils.JAVA_VERSION, JAVA_VERSION_TRIM_SIZE)); } - + /** - *

      Gets the Java version number as an int.

      - * - *

      Example return values:

      + *

      + * Gets the Java version number as an int. + *

      + * + *

      + * Example return values: + *

      *
        - *
      • 120 for JDK 1.2 - *
      • 131 for JDK 1.3.1 + *
      • 120 for Java 1.2
      • + *
      • 131 for Java 1.3.1
      • + *
      • 160 for Java 1.6.0_20
      • *
      * - *

      Patch releases are not reported. - * Zero is returned if {@link #JAVA_VERSION} is null.

      + *

      + * Patch releases are not reported. + *

      * - * @return the version, for example 131 for JDK 1.3.1 + * @return the version, for example 131 for Java 1.3.1 */ private static int getJavaVersionAsInt() { - if (JAVA_VERSION == null) { - return 0; - } - String str = JAVA_VERSION.substring(0, 1); - str = str + JAVA_VERSION.substring(2, 3); - if (JAVA_VERSION.length() >= 5) { - str = str + JAVA_VERSION.substring(4, 5); - } else { - str = str + "0"; - } - return Integer.parseInt(str); + return toVersionInt(toJavaVersionIntArray(SystemUtils.JAVA_VERSION, JAVA_VERSION_TRIM_SIZE)); } /** - *

      Decides if the java version matches.

      + *

      + * Decides if the Java version matches. + *

      * - * @param versionPrefix the prefix for the java version + * @param versionPrefix + * the prefix for the java version * @return true if matches, or false if not or can't determine */ private static boolean getJavaVersionMatches(String versionPrefix) { - if (JAVA_VERSION == null) { - return false; - } - return JAVA_VERSION.startsWith(versionPrefix); - } - + return isJavaVersionMatch(JAVA_VERSION_TRIMMED, versionPrefix); + } + /** - *

      Decides if the operating system matches.

      + * Trims the text of the java version to start with numbers. * - * @param osNamePrefix the prefix for the os name - * @return true if matches, or false if not or can't determine + * @return the trimmed java version */ - private static boolean getOSMatches(String osNamePrefix) { - if (OS_NAME == null) { - return false; + private static String getJavaVersionTrimmed() { + if (JAVA_VERSION != null) { + for (int i = 0; i < JAVA_VERSION.length(); i++) { + char ch = JAVA_VERSION.charAt(i); + if (ch >= '0' && ch <= '9') { + return JAVA_VERSION.substring(i); + } + } } - return OS_NAME.startsWith(osNamePrefix); - } + return null; + } /** - *

      Decides if the operating system matches.

      + * Decides if the operating system matches. * - * @param osNamePrefix the prefix for the os name - * @param osVersionPrefix the prefix for the version + * @param osNamePrefix + * the prefix for the os name + * @param osVersionPrefix + * the prefix for the version * @return true if matches, or false if not or can't determine */ private static boolean getOSMatches(String osNamePrefix, String osVersionPrefix) { - if (OS_NAME == null || OS_VERSION == null) { - return false; - } - return OS_NAME.startsWith(osNamePrefix) && OS_VERSION.startsWith(osVersionPrefix); - } + return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix); + } - //----------------------------------------------------------------------- /** - *

      Gets a System property, defaulting to null if the property - * cannot be read.

      - * - *

      If a SecurityException is caught, the return - * value is null and a message is written to System.err.

      + * Decides if the operating system matches. * - * @param property the system property name + * @param osNamePrefix + * the prefix for the os name + * @return true if matches, or false if not or can't determine + */ + private static boolean getOSMatchesName(String osNamePrefix) { + return isOSNameMatch(OS_NAME, osNamePrefix); + } + + // ----------------------------------------------------------------------- + /** + *

      + * Gets a System property, defaulting to null if the property cannot be read. + *

      + * + *

      + * If a SecurityException is caught, the return value is null and a message is written to + * System.err. + *

      + * + * @param property + * the system property name * @return the system property value or null if a security problem occurs */ private static String getSystemProperty(String property) { try { return System.getProperty(property); } catch (SecurityException ex) { // we are not allowed to look at this property - System.err.println( - "Caught a SecurityException reading the system property '" + property - + "'; the SystemUtils property value will default to null." - ); + System.err.println("Caught a SecurityException reading the system property '" + property + + "'; the SystemUtils property value will default to null."); return null; } - } - + } + /** - *

      Is the Java version at least the requested version.

      - * - *

      Example input:

      + *

      + * Gets the user directory as a File. + *

      + * + * @return a directory + * @throws SecurityException if a security manager exists and its + * checkPropertyAccess method doesn't allow access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserDir() { + return new File(System.getProperty(USER_DIR_KEY)); + } + + /** + *

      + * Gets the user home directory as a File. + *

      + * + * @return a directory + * @throws SecurityException if a security manager exists and its + * checkPropertyAccess method doesn't allow access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserHome() { + return new File(System.getProperty(USER_HOME_KEY)); + } + + /** + * Returns whether the {@link #JAVA_AWT_HEADLESS} value is true. + * + * @return true if JAVA_AWT_HEADLESS is "true", false otherwise. + * + * @see #JAVA_AWT_HEADLESS + * @since 2.1 + * @since Java 1.4 + */ + public static boolean isJavaAwtHeadless() { + return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS.equals(Boolean.TRUE.toString()) : false; + } + + /** + *

      + * Is the Java version at least the requested version. + *

      + * + *

      + * Example input: + *

      *
        - *
      • 1.2f to test for JDK 1.2
      • - *
      • 1.31f to test for JDK 1.3.1
      • + *
      • 1.2f to test for Java 1.2
      • + *
      • 1.31f to test for Java 1.3.1
      • *
      * - * @param requiredVersion the required version, for example 1.31f - * @return true if the actual version is equal or greater - * than the required version + * @param requiredVersion + * the required version, for example 1.31f + * @return true if the actual version is equal or greater than the required version */ public static boolean isJavaVersionAtLeast(float requiredVersion) { - return (JAVA_VERSION_FLOAT >= requiredVersion); + return JAVA_VERSION_FLOAT >= requiredVersion; } - + /** - *

      Is the Java version at least the requested version.

      - * - *

      Example input:

      + *

      + * Is the Java version at least the requested version. + *

      + * + *

      + * Example input: + *

      *
        - *
      • 120 to test for JDK 1.2 or greater
      • - *
      • 131 to test for JDK 1.3.1 or greater
      • + *
      • 120 to test for Java 1.2 or greater
      • + *
      • 131 to test for Java 1.3.1 or greater
      • *
      * - * @param requiredVersion the required version, for example 131 - * @return true if the actual version is equal or greater - * than the required version + * @param requiredVersion + * the required version, for example 131 + * @return true if the actual version is equal or greater than the required version * @since 2.0 */ public static boolean isJavaVersionAtLeast(int requiredVersion) { - return (JAVA_VERSION_INT >= requiredVersion); + return JAVA_VERSION_INT >= requiredVersion; } - + + /** + *

      + * Decides if the Java version matches. + *

      + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param version + * the actual Java version + * @param versionPrefix + * the prefix for the expected Java version + * @return true if matches, or false if not or can't determine + */ + static boolean isJavaVersionMatch(String version, String versionPrefix) { + if (version == null) { + return false; + } + return version.startsWith(versionPrefix); + } + + /** + * Decides if the operating system matches. + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param osName + * the actual OS name + * @param osVersion + * the actual OS version + * @param osNamePrefix + * the prefix for the expected OS name + * @param osVersionPrefix + * the prefix for the expected OS version + * @return true if matches, or false if not or can't determine + */ + static boolean isOSMatch(String osName, String osVersion, String osNamePrefix, String osVersionPrefix) { + if (osName == null || osVersion == null) { + return false; + } + return osName.startsWith(osNamePrefix) && osVersion.startsWith(osVersionPrefix); + } + + /** + * Decides if the operating system matches. + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param osName + * the actual OS name + * @param osNamePrefix + * the prefix for the expected OS name + * @return true if matches, or false if not or can't determine + */ + static boolean isOSNameMatch(String osName, String osNamePrefix) { + if (osName == null) { + return false; + } + return osName.startsWith(osNamePrefix); + } + + /** + *

      + * Converts the given Java version string to a float. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • 1.2f for Java 1.2
      • + *
      • 1.31f for Java 1.3.1
      • + *
      • 1.6f for Java 1.6.0_20
      • + *
      + * + *

      + * Patch releases are not reported. + *

      + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param version The string version + * @return the version, for example 1.31f for Java 1.3.1 + */ + static float toJavaVersionFloat(String version) { + return toVersionFloat(toJavaVersionIntArray(version, JAVA_VERSION_TRIM_SIZE)); + } + + /** + *

      + * Converts the given Java version string to an int. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • 120 for Java 1.2
      • + *
      • 131 for Java 1.3.1
      • + *
      • 160 for Java 1.6.0_20
      • + *
      + * + *

      + * Patch releases are not reported. + *

      + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param version The string version + * @return the version, for example 131 for Java 1.3.1 + */ + static int toJavaVersionInt(String version) { + return toVersionInt(toJavaVersionIntArray(version, JAVA_VERSION_TRIM_SIZE)); + } + + /** + *

      + * Converts the given Java version string to an int[] of maximum size 3. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • [1, 2, 0] for Java 1.2
      • + *
      • [1, 3, 1] for Java 1.3.1
      • + *
      • [1, 5, 0] for Java 1.5.0_21
      • + *
      + *

      + * This method is package private instead of private to support unit test invocation. + *

      + * + * @param version The string version + * @return the version, for example [1, 5, 0] for Java 1.5.0_21 + */ + static int[] toJavaVersionIntArray(String version) { + return toJavaVersionIntArray(version, Integer.MAX_VALUE); + } + + /** + *

      + * Converts the given Java version string to an int[] of maximum size limit. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • [1, 2, 0] for Java 1.2
      • + *
      • [1, 3, 1] for Java 1.3.1
      • + *
      • [1, 5, 0, 21] for Java 1.5.0_21
      • + *
      + * + * @param version The string version + * @param limit version limit + * @return the version, for example [1, 5, 0, 21] for Java 1.5.0_21 + */ + private static int[] toJavaVersionIntArray(String version, int limit) { + if (version == null) { + return ArrayUtils.EMPTY_INT_ARRAY; + } + String[] strings = StringUtils.split(version, "._- "); + int[] ints = new int[Math.min(limit, strings.length)]; + int j = 0; + for (int i = 0; i < strings.length && j < limit; i++) { + String s = strings[i]; + if (s.length() > 0) { + try { + ints[j] = Integer.parseInt(s); + j++; + } catch (Exception e) { + } + } + } + if (ints.length > j) { + int[] newInts = new int[j]; + System.arraycopy(ints, 0, newInts, 0, j); + ints = newInts; + } + return ints; + } + + /** + *

      + * Converts given the Java version array to a float. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • 1.2f for Java 1.2
      • + *
      • 1.31f for Java 1.3.1
      • + *
      • 1.6f for Java 1.6.0_20
      • + *
      + * + *

      + * Patch releases are not reported. + *

      + * + * @param javaVersions The version numbers + * @return the version, for example 1.31f for Java 1.3.1 + */ + private static float toVersionFloat(int[] javaVersions) { + if (javaVersions == null || javaVersions.length == 0) { + return 0f; + } + if (javaVersions.length == 1) { + return javaVersions[0]; + } + StringBuffer builder = new StringBuffer(); + builder.append(javaVersions[0]); + builder.append('.'); + for (int i = 1; i < javaVersions.length; i++) { + builder.append(javaVersions[i]); + } + try { + return Float.parseFloat(builder.toString()); + } catch (Exception ex) { + return 0f; + } + } + + /** + *

      + * Converts given the Java version array to an int. + *

      + * + *

      + * Example return values: + *

      + *
        + *
      • 120 for Java 1.2
      • + *
      • 131 for Java 1.3.1
      • + *
      • 160 for Java 1.6.0_20
      • + *
      + * + *

      + * Patch releases are not reported. + *

      + * + * @param javaVersions The version numbers + * @return the version, for example 1.31f for Java 1.3.1 + */ + private static int toVersionInt(int[] javaVersions) { + if (javaVersions == null) { + return 0; + } + int intVersion = 0; + int len = javaVersions.length; + if (len >= 1) { + intVersion = javaVersions[0] * 100; + } + if (len >= 2) { + intVersion += javaVersions[1] * 10; + } + if (len >= 3) { + intVersion += javaVersions[2]; + } + return intVersion; + } + + // ----------------------------------------------------------------------- + /** + *

      + * SystemUtils instances should NOT be constructed in standard programming. Instead, the class should be used as + * SystemUtils.FILE_SEPARATOR. + *

      + * + *

      + * This constructor is public to permit tools that require a JavaBean instance to operate. + *

      + */ + public SystemUtils() { + super(); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/UnhandledException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/UnhandledException.java (.../UnhandledException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/UnhandledException.java (.../UnhandledException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,87 +1,73 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; import org.apache.commons.lang.exception.NestableRuntimeException; /** - * Thrown when it is impossible or undesirable to consume - * or throw a checked exception. + *

      Thrown when it is impossible or undesirable to consume or throw a checked exception.

      + * This exception supplements the standard exception classes by providing a more + * semantically rich description of the problem.

      * + *

      UnhandledException represents the case where a method has to deal + * with a checked exception but does not wish to. + * Instead, the checked exception is rethrown in this unchecked wrapper.

      + * + *
      + * public void foo() {
      + *   try {
      + *     // do something that throws IOException
      + *   } catch (IOException ex) {
      + *     // don't want to or can't throw IOException from foo()
      + *     throw new UnhandledException(ex);
      + *   }
      + * }
      + * 
      + * * @author Matthew Hawthorne * @since 2.0 * @version $Id$ */ public class UnhandledException extends NestableRuntimeException { /** - * Constructs the exception using a cause. + * Required for serialization support. * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1832101364842773720L; + + /** + * Constructs the exception using a cause. + * * @param cause the underlying cause */ - public UnhandledException(Throwable cause) { - super(cause); - } + public UnhandledException(Throwable cause) { + super(cause); + } /** * Constructs the exception using a message and cause. - * + * * @param message the message to use * @param cause the underlying cause */ - public UnhandledException(String message, Throwable cause) { - super(message, cause); - } + public UnhandledException(String message, Throwable cause) { + super(message, cause); + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/Validate.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/Validate.java (.../Validate.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/Validate.java (.../Validate.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -58,7 +21,7 @@ import java.util.Map; /** - *

      Assists in validating arguments.

      + *

      This class assists in validating arguments.

      * *

      The class is based along the lines of JUnit. If an argument value is * deemed invalid, an IllegalArgumentException is thrown. For example:

      @@ -68,42 +31,39 @@ * Validate.notNull( surname, "The surname must not be null"); *
      * + * @author Apache Software Foundation * @author Ola Berg - * @author Stephen Colebourne * @author Gary Gregory + * @author Norm Deane * @since 2.0 * @version $Id$ */ public class Validate { + // Validate has no dependencies on other classes in Commons Lang at present /** * Constructor. This class should not normally be instantiated. */ public Validate() { + super(); } - + // isTrue //--------------------------------------------------------------------------------- - /** - *

      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 that the argument condition is true; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating an + * object or using your own custom validation expression.

      * - *
      -     * Validate.isTrue( myObject.isOk(), "The object is not OK: ", myObject);
      -     * 
      + *
      Validate.isTrue( myObject.isOk(), "The object is not OK: ", myObject);
      * - *

      For performance reasons, the object is passed as a separate parameter and - * appended to the message string only in the case of an error.

      + *

      For performance reasons, the object value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

      * - * @param expression a boolean expression - * @param message the exception message you would like to see if the - * expression is false - * @param value the value to append to the message in case of error + * @param expression the boolean expression to check + * @param message the exception message if invalid + * @param value the value to append to the message when invalid * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression, String message, Object value) { @@ -113,23 +73,19 @@ } /** - *

      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 that the argument condition is true; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

      * - *
      -     * Validate.isTrue( i > 0, "The value must be greater than zero: ", i);
      -     * 
      + *
      Validate.isTrue(i > 0.0, "The value must be greater than zero: ", i);
      * - *

      For performance reasons, the object is passed as a separate parameter and - * appended to the message string only in the case of an error.

      + *

      For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

      * - * @param expression a boolean expression - * @param message the exception message you would like to see if the expression is false - * @param value the value to append to the message in case of error + * @param expression the boolean expression to check + * @param message the exception message if invalid + * @param value the value to append to the message when invalid * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression, String message, long value) { @@ -139,24 +95,19 @@ } /** - *

      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 that the argument condition is true; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

      * - *
      -     * Validate.isTrue( d > 0.0, "The value must be greater than zero: ", d);
      -     * 
      + *
      Validate.isTrue(d > 0.0, "The value must be greater than zero: ", d);
      * - *

      For performance reasons, the object is passed as a separate parameter and - * appended to the message string only in the case of an error.

      + *

      For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

      * - * @param expression a boolean expression - * @param message the exception message you would like to see if the expression - * is false - * @param value the value to append to the message in case of error + * @param expression the boolean expression to check + * @param message the exception message if invalid + * @param value the value to append to the message when invalid * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression, String message, double value) { @@ -166,24 +117,18 @@ } /** - *

      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 that the argument condition is true; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

      * *
            * Validate.isTrue( (i > 0), "The value must be greater than zero");
            * Validate.isTrue( myObject.isOk(), "The object is not OK");
            * 
      * - *

      For performance reasons, the message string should not involve a string append, - * instead use the {@link #isTrue(boolean, String, Object)} method.

      - * - * @param expression a boolean expression - * @param message the exception message you would like to see if the expression - * is false + * @param expression the boolean expression to check + * @param message the exception message if invalid * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression, String message) { @@ -193,21 +138,19 @@ } /** - *

      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 that the argument condition is true; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

      * *
      -     * Validate.isTrue( i > 0 );
      -     * Validate.isTrue( myObject.isOk() );
      -     * 
      + * Validate.isTrue(i > 0); + * Validate.isTrue(myObject.isOk());
      * - *

      The message in the exception is 'The validated expression is false'.

      + *

      The message of the exception is "The validated expression is + * false".

      * - * @param expression a boolean expression + * @param expression the boolean expression to check * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression) { @@ -220,56 +163,48 @@ //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument is not null; + * otherwise throwing an exception. * - *

      -     * Validate.notNull(myObject, "The object must not be null");
      -     * 
      + *
      Validate.notNull(myObject);
      + * + *

      The message of the exception is "The validated object is + * null".

      * - * @param object the object to check is not null - * @param message the exception message you would like to see - * if the object is null + * @param object the object to check * @throws IllegalArgumentException if the object is null */ - public static void notNull(Object object, String message) { - if (object == null) { - throw new IllegalArgumentException(message); - } + public static void notNull(Object object) { + notNull(object, "The validated object is null"); } /** - *

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

      + *

      Validate that the specified argument is not null; + * otherwise throwing an exception with the specified message. * - *

      -     * Validate.notNull(myObject);
      -     * 
      - * - *

      The message in the exception is 'The validated object is null'.

      + *
      Validate.notNull(myObject, "The object must not be null");
      * - * @param object the object to check is not null - * @throws IllegalArgumentException if the object is null + * @param object the object to check + * @param message the exception message if invalid */ - public static void notNull(Object object) { + public static void notNull(Object object, String message) { if (object == null) { - throw new IllegalArgumentException("The validated object is null"); + throw new IllegalArgumentException(message); } } // notEmpty array //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument array is neither null + * nor a length of zero (no elements); otherwise throwing an exception + * with the specified message. * - *

      -     * Validate.notEmpty(myArray, "The array must not be empty");
      -     * 
      + *
      Validate.notEmpty(myArray, "The array must not be empty");
      * - * @param array the array to check is not empty - * @param message the exception message you would like to see if the array is empty + * @param array the array to check + * @param message the exception message if invalid * @throws IllegalArgumentException if the array is empty */ public static void notEmpty(Object[] array, String message) { @@ -279,37 +214,33 @@ } /** - *

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

      + *

      Validate that the specified argument array is neither null + * nor a length of zero (no elements); otherwise throwing an exception. * - *

      -     * Validate.notEmpty(myArray);
      -     * 
      - * - *

      The message in the exception is 'The validated array is empty'. + *

      Validate.notEmpty(myArray);
      * - * @param array the array to check is not empty + *

      The message in the exception is "The validated array is + * empty". + * + * @param array the array to check * @throws IllegalArgumentException if the array is empty */ public static void notEmpty(Object[] array) { - if (array == null || array.length == 0) { - throw new IllegalArgumentException("The validated array is empty"); - } + notEmpty(array, "The validated array is empty"); } // notEmpty collection //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument collection is neither null + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. * - *

      -     * Validate.notEmpty(myCollection, "The collection must not be empty");
      -     * 
      + *
      Validate.notEmpty(myCollection, "The collection must not be empty");
      * - * @param collection the collection to check is not empty - * @param message the exception message you would like to see if the collection is empty + * @param collection the collection to check + * @param message the exception message if invalid * @throws IllegalArgumentException if the collection is empty */ public static void notEmpty(Collection collection, String message) { @@ -319,37 +250,33 @@ } /** - *

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

      + *

      Validate that the specified argument collection is neither null + * nor a size of zero (no elements); otherwise throwing an exception. * - *

      -     * Validate.notEmpty(myCollection);
      -     * 
      - * - *

      The message in the exception is 'The validated collection is empty'.

      + *
      Validate.notEmpty(myCollection);
      * - * @param collection the collection to check is not empty + *

      The message in the exception is "The validated collection is + * empty".

      + * + * @param collection the collection to check * @throws IllegalArgumentException if the collection is empty */ public static void notEmpty(Collection collection) { - if (collection == null || collection.size() == 0) { - throw new IllegalArgumentException("The validated collection is empty"); - } + notEmpty(collection, "The validated collection is empty"); } // notEmpty map //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument map is neither null + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. * - *

      -     * Validate.notEmpty(myMap, "The collection must not be empty");
      -     * 
      + *
      Validate.notEmpty(myMap, "The map must not be empty");
      * - * @param map the map to check is not empty - * @param message the exception message you would like to see if the map is empty + * @param map the map to check + * @param message the exception message if invalid * @throws IllegalArgumentException if the map is empty */ public static void notEmpty(Map map, String message) { @@ -359,37 +286,34 @@ } /** - *

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

      + *

      Validate that the specified argument map is neither null + * nor a size of zero (no elements); otherwise throwing an exception. * - *

      -     * Validate.notEmpty(myMap);
      -     * 
      - * - *

      The message in the exception is 'The validated map is empty'.

      + *
      Validate.notEmpty(myMap);
      * - * @param map the map to check is not empty + *

      The message in the exception is "The validated map is + * empty".

      + * + * @param map the map to check * @throws IllegalArgumentException if the map is empty + * @see #notEmpty(Map, String) */ public static void notEmpty(Map map) { - if (map == null || map.size() == 0) { - throw new IllegalArgumentException("The validated map is empty"); - } + notEmpty(map, "The validated map is empty"); } // notEmpty string //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument string is + * neither null nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. * - *

      -     * Validate.notEmpty(myString, "The string must not be empty");
      -     * 
      + *
      Validate.notEmpty(myString, "The string must not be empty");
      * - * @param string the string to check is not empty - * @param message the exception message you would like to see if the string is empty + * @param string the string to check + * @param message the exception message if invalid * @throws IllegalArgumentException if the string is empty */ public static void notEmpty(String string, String message) { @@ -399,41 +323,39 @@ } /** - *

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

      + *

      Validate that the specified argument string is + * neither null nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. * - *

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

      The message in the exception is 'The validated string is empty'.

      + *
      Validate.notEmpty(myString);
      * - * @param string the string to check is not empty + *

      The message in the exception is "The validated + * string is empty".

      + * + * @param string the string to check * @throws IllegalArgumentException if the string is empty */ public static void notEmpty(String string) { - if (string == null || string.length() == 0) { - throw new IllegalArgumentException("The validated string is empty"); - } + notEmpty(string, "The validated string is empty"); } // notNullElements array //--------------------------------------------------------------------------------- /** - *

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

      + *

      Validate that the specified argument array is neither + * null nor contains any elements that are null; + * otherwise throwing an exception with the specified message. * - *

      -     * Validate.notEmpty(myArray, "The array must not contain null elements");
      -     * 
      + *
      Validate.noNullElements(myArray, "The array contain null at position %d");
      * - * @param array the array to check - * @param message the exception message if the array has - * null elements - * @throws IllegalArgumentException if the array has null - * elements or is null + *

      If the array is null, then the message in the exception + * is "The validated object is null".

      + * + * @param array the array to check + * @param message the exception message if the collection has null elements + * @throws IllegalArgumentException if the array is null or + * an element in the array is null */ public static void noNullElements(Object[] array, String message) { Validate.notNull(array); @@ -445,19 +367,22 @@ } /** - *

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

      + *

      Validate that the specified argument array is neither + * null nor contains any elements that are null; + * otherwise throwing an exception. * - *

      -     * Validate.notEmpty(myArray);
      -     * 
      - * - *

      The message in the exception is 'The validated array contains null element at index: '.

      + *
      Validate.noNullElements(myArray);
      * - * @param array the array to check - * @throws IllegalArgumentException if the array has null - * elements or is null + *

      If the array is null, then the message in the exception + * is "The validated object is null".

      + * + *

      If the array has a null element, then the message in the + * exception is "The validated array contains null element at index: + * " followed by the index.

      + * + * @param array the array to check + * @throws IllegalArgumentException if the array is null or + * an element in the array is null */ public static void noNullElements(Object[] array) { Validate.notNull(array); @@ -472,51 +397,108 @@ //--------------------------------------------------------------------------------- /** - *

      Validate an argument, throwing IllegalArgumentException - * if the argument collection has null elements or is - * null.

      + *

      Validate that the specified argument collection is neither + * null nor contains any elements that are null; + * otherwise throwing an exception with the specified message. * - *

      -     * Validate.notEmpty(myCollection, "The collection must not contain null elements");
      -     * 
      + *
      Validate.noNullElements(myCollection, "The collection contains null elements");
      * + *

      If the collection is null, then the message in the exception + * is "The validated object is null".

      + * + * * @param collection the collection to check - * @param message the exception message if the array has - * null elements - * @throws IllegalArgumentException if the collection has - * null elements or is null + * @param message the exception message if the collection has + * @throws IllegalArgumentException if the collection is null or + * an element in the collection is null */ public static void noNullElements(Collection collection, String message) { Validate.notNull(collection); + for (Iterator it = collection.iterator(); it.hasNext();) { + if (it.next() == null) { + throw new IllegalArgumentException(message); + } + } + } + + /** + *

      Validate that the specified argument collection is neither + * null nor contains any elements that are null; + * otherwise throwing an exception. + * + *

      Validate.noNullElements(myCollection);
      + * + *

      If the collection is null, then the message in the exception + * is "The validated object is null".

      + * + *

      If the collection has a null element, then the message in the + * exception is "The validated collection contains null element at index: + * " followed by the index.

      + * + * @param collection the collection to check + * @throws IllegalArgumentException if the collection is null or + * an element in the collection is null + */ + public static void noNullElements(Collection collection) { + Validate.notNull(collection); int i = 0; for (Iterator it = collection.iterator(); it.hasNext(); i++) { if (it.next() == null) { - throw new IllegalArgumentException(message); + throw new IllegalArgumentException("The validated collection contains null element at index: " + i); } } } /** *

      Validate an argument, throwing IllegalArgumentException - * if the argument collection has null elements or is - * null.

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

      * *
      -     * Validate.notEmpty(myCollection);
      +     * Validate.allElementsOfType(collection, String.class, "Collection has invalid elements");
            * 
      * - *

      The message in the exception is 'The validated collection contains null element 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 + * @param message the exception message if the Collection has elements not of type clazz + * @since 2.1 + */ + public static void allElementsOfType(Collection collection, Class clazz, String message) { + Validate.notNull(collection); + Validate.notNull(clazz); + for (Iterator it = collection.iterator(); it.hasNext(); ) { + if (clazz.isInstance(it.next()) == false) { + throw new IllegalArgumentException(message); + } + } + } + + /** + *

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

      * - * @param collection the collection to check - * @throws IllegalArgumentException if the collection has - * null elements or is null + *
      +     * 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 noNullElements(Collection collection) { + public static void allElementsOfType(Collection collection, Class clazz) { Validate.notNull(collection); + Validate.notNull(clazz); int i = 0; for (Iterator it = collection.iterator(); it.hasNext(); i++) { - if (it.next() == null) { - throw new IllegalArgumentException("The validated collection contains null element at index: " + i); + if (clazz.isInstance(it.next()) == false) { + throw new IllegalArgumentException("The validated collection contains an element not of type " + + clazz.getName() + " at index: " + i); } } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/WordUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/WordUtils.java (.../WordUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/WordUtils.java (.../WordUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang; @@ -61,8 +24,7 @@ * Each method documents its behaviour in more detail.

      * * @author Apache Jakarta Velocity - * @author Henri Yandell - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Henning P. Schmiedehausen * @author Gary Gregory * @since 2.0 @@ -71,14 +33,15 @@ public class WordUtils { /** - *

      WordWrapUtils instances should NOT be constructed in + *

      WordUtils instances should NOT be constructed in * standard programming. Instead, the class should be used as - * WordWrapUtils.wrap("foo bar", 20);.

      + * WordUtils.wrap("foo bar", 20);.

      * *

      This constructor is public to permit tools that require a JavaBean * instance to operate.

      */ public WordUtils() { + super(); } // Wrapping @@ -152,7 +115,7 @@ // } // } // -// return (stringBuffer.toString()); +// return stringBuffer.toString(); // } // Wrapping @@ -256,8 +219,9 @@ //----------------------------------------------------------------------- /** *

      Capitalizes all the whitespace separated words in a String. - * Only the first letter of each word is changed. To change all letters to - * the capitalized case, use {@link #capitalizeFully(String)}.

      + * Only the first letter of each word is changed. To convert the + * rest of each word to lowercase at the same time, + * use {@link #capitalizeFully(String)}.

      * *

      Whitespace is defined by {@link Character#isWhitespace(char)}. * A null input String returns null. @@ -276,67 +240,122 @@ * @see #capitalizeFully(String) */ public static String capitalize(String str) { - int strLen; - if (str == null || (strLen = str.length()) == 0) { + return capitalize(str, null); + } + + /** + *

      Capitalizes all the delimiter separated words in a String. + * Only the first letter of each word is changed. To convert the + * rest of each word to lowercase at the same time, + * use {@link #capitalizeFully(String, char[])}.

      + * + *

      The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be capitalized.

      + * + *

      A null input String returns null. + * Capitalization uses the unicode title case, normally equivalent to + * upper case.

      + * + *
      +     * WordUtils.capitalize(null, *)            = null
      +     * WordUtils.capitalize("", *)              = ""
      +     * WordUtils.capitalize(*, new char[0])     = *
      +     * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
      +     * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
      +     * 
      + * + * @param str the String to capitalize, may be null + * @param delimiters set of characters to determine capitalization, null means whitespace + * @return capitalized String, null if null String input + * @see #uncapitalize(String) + * @see #capitalizeFully(String) + * @since 2.1 + */ + public static String capitalize(String str, char[] delimiters) { + int delimLen = (delimiters == null ? -1 : delimiters.length); + if (str == null || str.length() == 0 || delimLen == 0) { return str; } + int strLen = str.length(); StringBuffer buffer = new StringBuffer(strLen); - boolean whitespace = true; + boolean capitalizeNext = true; for (int i = 0; i < strLen; i++) { char ch = str.charAt(i); - if (Character.isWhitespace(ch)) { + + if (isDelimiter(ch, delimiters)) { buffer.append(ch); - whitespace = true; - } else if (whitespace) { + capitalizeNext = true; + } else if (capitalizeNext) { buffer.append(Character.toTitleCase(ch)); - whitespace = false; + capitalizeNext = false; } else { buffer.append(ch); } } return buffer.toString(); } + //----------------------------------------------------------------------- /** - *

      Capitalizes all the whitespace separated words in a String. - * All letters are changed, so the resulting string will be fully changed.

      + *

      Converts all the whitespace separated words in a String into capitalized words, + * that is each word is made up of a titlecase character and then a series of + * lowercase characters.

      * *

      Whitespace is defined by {@link Character#isWhitespace(char)}. * A null input String returns null. * Capitalization uses the unicode title case, normally equivalent to * upper case.

      * *
      -     * WordUtils.capitalize(null)        = null
      -     * WordUtils.capitalize("")          = ""
      -     * WordUtils.capitalize("i am FINE") = "I Am Fine"
      +     * WordUtils.capitalizeFully(null)        = null
      +     * WordUtils.capitalizeFully("")          = ""
      +     * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
            * 
      * * @param str the String to capitalize, may be null * @return capitalized String, null if null String input */ public static String capitalizeFully(String str) { - int strLen; - if (str == null || (strLen = str.length()) == 0) { + return capitalizeFully(str, null); + } + + /** + *

      Converts all the delimiter separated words in a String into capitalized words, + * that is each word is made up of a titlecase character and then a series of + * lowercase characters.

      + * + *

      The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be capitalized.

      + * + *

      A null input String returns null. + * Capitalization uses the unicode title case, normally equivalent to + * upper case.

      + * + *
      +     * WordUtils.capitalizeFully(null, *)            = null
      +     * WordUtils.capitalizeFully("", *)              = ""
      +     * WordUtils.capitalizeFully(*, null)            = *
      +     * WordUtils.capitalizeFully(*, new char[0])     = *
      +     * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
      +     * 
      + * + * @param str the String to capitalize, may be null + * @param delimiters set of characters to determine capitalization, null means whitespace + * @return capitalized String, null if null String input + * @since 2.1 + */ + public static String capitalizeFully(String str, char[] delimiters) { + int delimLen = (delimiters == null ? -1 : delimiters.length); + if (str == null || str.length() == 0 || delimLen == 0) { return str; } - StringBuffer buffer = new StringBuffer(strLen); - boolean whitespace = true; - for (int i = 0; i < strLen; i++) { - char ch = str.charAt(i); - if (Character.isWhitespace(ch)) { - buffer.append(ch); - whitespace = true; - } else if (whitespace) { - buffer.append(Character.toTitleCase(ch)); - whitespace = false; - } else { - buffer.append(Character.toLowerCase(ch)); - } - } - return buffer.toString(); + str = str.toLowerCase(); + return capitalize(str, delimiters); } + //----------------------------------------------------------------------- /** *

      Uncapitalizes all the whitespace separated words in a String. * Only the first letter of each word is changed.

      @@ -355,27 +374,59 @@ * @see #capitalize(String) */ public static String uncapitalize(String str) { - int strLen; - if (str == null || (strLen = str.length()) == 0) { + return uncapitalize(str, null); + } + + /** + *

      Uncapitalizes all the whitespace separated words in a String. + * Only the first letter of each word is changed.

      + * + *

      The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be uncapitalized.

      + * + *

      Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

      + * + *
      +     * WordUtils.uncapitalize(null, *)            = null
      +     * WordUtils.uncapitalize("", *)              = ""
      +     * WordUtils.uncapitalize(*, null)            = *
      +     * WordUtils.uncapitalize(*, new char[0])     = *
      +     * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
      +     * 
      + * + * @param str the String to uncapitalize, may be null + * @param delimiters set of characters to determine uncapitalization, null means whitespace + * @return uncapitalized String, null if null String input + * @see #capitalize(String) + * @since 2.1 + */ + public static String uncapitalize(String str, char[] delimiters) { + int delimLen = (delimiters == null ? -1 : delimiters.length); + if (str == null || str.length() == 0 || delimLen == 0) { return str; } + int strLen = str.length(); StringBuffer buffer = new StringBuffer(strLen); - boolean whitespace = true; + boolean uncapitalizeNext = true; for (int i = 0; i < strLen; i++) { char ch = str.charAt(i); - if (Character.isWhitespace(ch)) { + + if (isDelimiter(ch, delimiters)) { buffer.append(ch); - whitespace = true; - } else if (whitespace) { + uncapitalizeNext = true; + } else if (uncapitalizeNext) { buffer.append(Character.toLowerCase(ch)); - whitespace = false; + uncapitalizeNext = false; } else { buffer.append(ch); } } return buffer.toString(); } + //----------------------------------------------------------------------- /** *

      Swaps the case of a String using a word based algorithm.

      * @@ -429,5 +480,168 @@ } return buffer.toString(); } - + + //----------------------------------------------------------------------- + /** + *

      Extracts the initial letters from each word in the String.

      + * + *

      The first letter of the string and all first letters after + * whitespace are returned as a new string. + * Their case is not changed.

      + * + *

      Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

      + * + *
      +     * WordUtils.initials(null)             = null
      +     * WordUtils.initials("")               = ""
      +     * WordUtils.initials("Ben John Lee")   = "BJL"
      +     * WordUtils.initials("Ben J.Lee")      = "BJ"
      +     * 
      + * + * @param str the String to get initials from, may be null + * @return String of initial letters, null if null String input + * @see #initials(String,char[]) + * @since 2.2 + */ + public static String initials(String str) { + return initials(str, null); + } + + /** + *

      Extracts the initial letters from each word in the String.

      + * + *

      The first letter of the string and all first letters after the + * defined delimiters are returned as a new string. + * Their case is not changed.

      + * + *

      If the delimiters array is null, then Whitespace is used. + * Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null. + * An empty delimiter array returns an empty String.

      + * + *
      +     * WordUtils.initials(null, *)                = null
      +     * WordUtils.initials("", *)                  = ""
      +     * WordUtils.initials("Ben John Lee", null)   = "BJL"
      +     * WordUtils.initials("Ben J.Lee", null)      = "BJ"
      +     * WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
      +     * WordUtils.initials(*, new char[0])         = ""
      +     * 
      + * + * @param str the String to get initials from, may be null + * @param delimiters set of characters to determine words, null means whitespace + * @return String of initial letters, null if null String input + * @see #initials(String) + * @since 2.2 + */ + public static String initials(String str, char[] delimiters) { + if (str == null || str.length() == 0) { + return str; + } + if (delimiters != null && delimiters.length == 0) { + return ""; + } + int strLen = str.length(); + char[] buf = new char[strLen / 2 + 1]; + int count = 0; + boolean lastWasGap = true; + for (int i = 0; i < strLen; i++) { + char ch = str.charAt(i); + + if (isDelimiter(ch, delimiters)) { + lastWasGap = true; + } else if (lastWasGap) { + buf[count++] = ch; + lastWasGap = false; + } else { + // ignore ch + } + } + return new String(buf, 0, count); + } + + //----------------------------------------------------------------------- + /** + * Is the character a delimiter. + * + * @param ch the character to check + * @param delimiters the delimiters + * @return true if it is a delimiter + */ + private static boolean isDelimiter(char ch, char[] delimiters) { + if (delimiters == null) { + return Character.isWhitespace(ch); + } + for (int i = 0, isize = delimiters.length; i < isize; i++) { + if (ch == delimiters[i]) { + return true; + } + } + return false; + } + + //----------------------------------------------------------------------- + /** + * Abbreviates a string nicely. + * + * This method searches for the first space after the lower limit and abbreviates + * the String there. It will also append any String passed as a parameter + * to the end of the String. The upper limit can be specified to forcibly + * abbreviate a String. + * + * @param str the string to be abbreviated. If null is passed, null is returned. + * If the empty String is passed, the empty string is returned. + * @param lower the lower limit. + * @param upper the upper limit; specify -1 if no limit is desired. + * If the upper limit is lower than the lower limit, it will be + * adjusted to be the same as the lower limit. + * @param appendToEnd String to be appended to the end of the abbreviated string. + * This is appended ONLY if the string was indeed abbreviated. + * The append does not count towards the lower or upper limits. + * @return the abbreviated String. + * @since 2.4 + */ + public static String abbreviate(String str, int lower, int upper, String appendToEnd) { + // initial parameter checks + if (str == null) { + return null; + } + if (str.length() == 0) { + return StringUtils.EMPTY; + } + + // if the lower value is greater than the length of the string, + // set to the length of the string + if (lower > str.length()) { + lower = str.length(); + } + // if the upper value is -1 (i.e. no limit) or is greater + // than the length of the string, set to the length of the string + if (upper == -1 || upper > str.length()) { + upper = str.length(); + } + // if upper is less than lower, raise it to lower + if (upper < lower) { + upper = lower; + } + + StringBuffer result = new StringBuffer(); + int index = StringUtils.indexOf(str, " ", lower); + if (index == -1) { + result.append(str.substring(0, upper)); + // only if abbreviation has occured do we append the appendToEnd value + if (upper != str.length()) { + result.append(StringUtils.defaultString(appendToEnd)); + } + } else if (index > upper) { + result.append(str.substring(0, upper)); + result.append(StringUtils.defaultString(appendToEnd)); + } else { + result.append(str.substring(0, index)); + result.append(StringUtils.defaultString(appendToEnd)); + } + return result.toString(); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/CompareToBuilder.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/CompareToBuilder.java (.../CompareToBuilder.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/CompareToBuilder.java (.../CompareToBuilder.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,63 +1,28 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.builder; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.Comparator; +import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.math.NumberUtils; /** @@ -97,7 +62,7 @@ * } *
      * - *

      Alternatively, there is a method {@link #reflectionCompare reflectionCompare} that uses + *

      Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use * reflection to determine the fields to append. Because fields can be private, * reflectionCompare uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to * bypass normal access control checks. This will fail under a security manager, @@ -118,8 +83,8 @@ * @see java.lang.Object#hashCode() * @see EqualsBuilder * @see HashCodeBuilder + * @author Apache Software Foundation * @author Steve Downey - * @author Stephen Colebourne * @author Gary Gregory * @author Pete Gieser * @since 1.0 @@ -172,7 +137,7 @@ * with lhs */ public static int reflectionCompare(Object lhs, Object rhs) { - return reflectionCompare(lhs, rhs, false, null); + return reflectionCompare(lhs, rhs, false, null, null); } /** @@ -204,7 +169,7 @@ * with lhs */ public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) { - return reflectionCompare(lhs, rhs, compareTransients, null); + return reflectionCompare(lhs, rhs, compareTransients, null, null); } /** @@ -216,6 +181,72 @@ * *

        *
      • Static fields will not be compared
      • + *
      • If compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
      • + *
      • Superclass fields will be compared
      • + *
      + * + *

      If both lhs and rhs are null, + * they are considered equal.

      + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields Collection of String fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, Collection /*String*/ excludeFields) { + return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + *

      Compares two Objects via reflection.

      + * + *

      Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

      + * + *
        + *
      • Static fields will not be compared
      • + *
      • If compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
      • + *
      • Superclass fields will be compared
      • + *
      + * + *

      If both lhs and rhs are null, + * they are considered equal.

      + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields array of fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, String[] excludeFields) { + return reflectionCompare(lhs, rhs, false, null, excludeFields); + } + + /** + *

      Compares two Objects via reflection.

      + * + *

      Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

      + * + *
        + *
      • Static fields will not be compared
      • *
      • If the compareTransients is true, * compares transient members. Otherwise ignores them, as they * are likely derived fields.
      • @@ -238,7 +269,51 @@ * with lhs * @since 2.0 */ - public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients, Class reflectUpToClass) { + public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients, + Class reflectUpToClass) + { + return reflectionCompare(lhs, rhs, compareTransients, reflectUpToClass, null); + } + + /** + *

        Compares two Objects via reflection.

        + * + *

        Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

        + * + *
          + *
        • Static fields will not be compared
        • + *
        • If the compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
        • + *
        • Compares superclass fields up to and including reflectUpToClass. + * If reflectUpToClass is null, compares all superclass fields.
        • + *
        + * + *

        If both lhs and rhs are null, + * they are considered equal.

        + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param compareTransients whether to compare transient fields + * @param reflectUpToClass last superclass for which fields are compared + * @param excludeFields fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 + */ + public static int reflectionCompare( + Object lhs, + Object rhs, + boolean compareTransients, + Class reflectUpToClass, + String[] excludeFields) { + if (lhs == rhs) { return 0; } @@ -250,10 +325,10 @@ throw new ClassCastException(); } CompareToBuilder compareToBuilder = new CompareToBuilder(); - reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) { lhsClazz = lhsClazz.getSuperclass(); - reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); } return compareToBuilder.toComparison(); } @@ -267,19 +342,22 @@ * @param clazz Class that defines fields to be compared * @param builder CompareToBuilder to append to * @param useTransients whether to compare transient fields + * @param excludeFields fields to exclude */ private static void reflectionAppend( Object lhs, Object rhs, Class clazz, CompareToBuilder builder, - boolean useTransients) { + boolean useTransients, + String[] excludeFields) { Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length && builder.comparison == 0; i++) { Field f = fields[i]; - if ((f.getName().indexOf('$') == -1) + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) && (useTransients || !Modifier.isTransient(f.getModifiers())) && (!Modifier.isStatic(f.getModifiers()))) { try { @@ -495,7 +573,7 @@ *

        Appends to the builder the comparison of * two doubles.

        * - *

        This handles NaNs, Infinties, and -0.0.

        + *

        This handles NaNs, Infinities, and -0.0.

        * *

        It is compatible with the hash code generated by * HashCodeBuilder.

        @@ -516,7 +594,7 @@ *

        Appends to the builder the comparison of * two floats.

        * - *

        This handles NaNs, Infinties, and -0.0.

        + *

        This handles NaNs, Infinities, and -0.0.

        * *

        It is compatible with the hash code generated by * HashCodeBuilder.

        Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/EqualsBuilder.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/EqualsBuilder.java (.../EqualsBuilder.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/EqualsBuilder.java (.../EqualsBuilder.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,62 +1,28 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.builder; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Collection; +import org.apache.commons.lang.ArrayUtils; + /** *

        Assists in implementing {@link Object#equals(Object)} methods.

        * @@ -78,13 +44,15 @@ * *

        Typical use for the code is as follows:

        *
        - * public boolean equals(Object o) {
        - *   if ( !(o instanceof MyClass) ) {
        - *    return false;
        + * public boolean equals(Object obj) {
        + *   if (obj == null) { return false; }
        + *   if (obj == this) { return true; }
        + *   if (obj.getClass() != getClass()) {
        + *     return false;
          *   }
        - *  MyClass rhs = (MyClass) o;
        - *  return new EqualsBuilder()
        - *                 .appendSuper(super.equals(o))
        + *   MyClass rhs = (MyClass) obj;
        + *   return new EqualsBuilder()
        + *                 .appendSuper(super.equals(obj))
          *                 .append(field1, rhs.field1)
          *                 .append(field2, rhs.field2)
          *                 .append(field3, rhs.field3)
        @@ -101,33 +69,35 @@
          *
          * 

        A typical invocation for this method would look like:

        *
        - * public boolean equals(Object o) {
        - *   return EqualsBuilder.reflectionEquals(this, o);
        + * public boolean equals(Object obj) {
        + *   return EqualsBuilder.reflectionEquals(this, obj);
          * }
          * 
        * + * @author Apache Software Foundation * @author Steve Downey - * @author Stephen Colebourne * @author Gary Gregory * @author Pete Gieser + * @author Arun Mammen Thomas * @since 1.0 * @version $Id$ */ public class EqualsBuilder { + /** * If the fields tested are equals. + * The default value is true. */ - private boolean isEquals; + private boolean isEquals = true; /** *

        Constructor for EqualsBuilder.

        * *

        Starts off assuming that equals is true.

        - * @see java.lang.Object#equals + * @see Object#equals(Object) */ public EqualsBuilder() { - super(); - isEquals = true; + // do nothing for now. } //------------------------------------------------------------------------- @@ -151,7 +121,7 @@ * @return true if the two Objects have tested equals. */ public static boolean reflectionEquals(Object lhs, Object rhs) { - return reflectionEquals(lhs, rhs, false, null); + return reflectionEquals(lhs, rhs, false, null, null); } /** @@ -163,6 +133,52 @@ * a security manager, if the permissions are not set up correctly. It is also * not as efficient as testing explicitly.

        * + *

        Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.

        + * + *

        Static fields will not be tested. Superclass fields will be included.

        + * + * @param lhs this object + * @param rhs the other object + * @param excludeFields Collection of String field names to exclude from testing + * @return true if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, Collection /*String*/ excludeFields) { + return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + *

        This method uses reflection to determine if the two Objects + * are equal.

        + * + *

        It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

        + * + *

        Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.

        + * + *

        Static fields will not be tested. Superclass fields will be included.

        + * + * @param lhs this object + * @param rhs the other object + * @param excludeFields array of field names to exclude from testing + * @return true if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, String[] excludeFields) { + return reflectionEquals(lhs, rhs, false, null, excludeFields); + } + + /** + *

        This method uses reflection to determine if the two Objects + * are equal.

        + * + *

        It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

        + * *

        If the TestTransients parameter is set to true, transient * members will be tested, otherwise they are ignored, as they are likely * derived fields, and not part of the value of the Object.

        @@ -175,7 +191,7 @@ * @return true if the two Objects have tested equals. */ public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) { - return reflectionEquals(lhs, rhs, testTransients, null); + return reflectionEquals(lhs, rhs, testTransients, null, null); } /** @@ -204,6 +220,37 @@ * @since 2.0 */ public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass) { + return reflectionEquals(lhs, rhs, testTransients, reflectUpToClass, null); + } + + /** + *

        This method uses reflection to determine if the two Objects + * are equal.

        + * + *

        It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

        + * + *

        If the testTransients parameter is set to true, transient + * members will be tested, otherwise they are ignored, as they are likely + * derived fields, and not part of the value of the Object.

        + * + *

        Static fields will not be included. Superclass fields will be appended + * up to and including the specified superclass. A null superclass is treated + * as java.lang.Object.

        + * + * @param lhs this object + * @param rhs the other object + * @param testTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), + * may be null + * @param excludeFields array of field names to exclude from testing + * @return true if the two Objects have tested equals. + * @since 2.0 + */ + public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass, + String[] excludeFields) { if (lhs == rhs) { return true; } @@ -235,10 +282,10 @@ } EqualsBuilder equalsBuilder = new EqualsBuilder(); try { - reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients); + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); while (testClass.getSuperclass() != null && testClass != reflectUpToClass) { testClass = testClass.getSuperclass(); - reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients); + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); } } catch (IllegalArgumentException e) { // In this case, we tried to test a subclass vs. a superclass and @@ -260,18 +307,21 @@ * @param clazz the class to append details of * @param builder the builder to append to * @param useTransients whether to test transient fields + * @param excludeFields array of field names to exclude from testing */ private static void reflectionAppend( Object lhs, Object rhs, Class clazz, EqualsBuilder builder, - boolean useTransients) { + boolean useTransients, + String[] excludeFields) { Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); for (int i = 0; i < fields.length && builder.isEquals; i++) { Field f = fields[i]; - if ((f.getName().indexOf('$') == -1) + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) && (useTransients || !Modifier.isTransient(f.getModifiers())) && (!Modifier.isStatic(f.getModifiers()))) { try { @@ -320,45 +370,51 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } Class lhsClass = lhs.getClass(); if (!lhsClass.isArray()) { - //the simple case, not an array, just test the element + // The simple case, not an array, just test the element isEquals = lhs.equals(rhs); + } else if (lhs.getClass() != rhs.getClass()) { + // Here when we compare different dimensions, for example: a boolean[][] to a boolean[] + this.setEquals(false); + } + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays of the same depth + else if (lhs instanceof long[]) { + append((long[]) lhs, (long[]) rhs); + } else if (lhs instanceof int[]) { + append((int[]) lhs, (int[]) rhs); + } else if (lhs instanceof short[]) { + append((short[]) lhs, (short[]) rhs); + } else if (lhs instanceof char[]) { + append((char[]) lhs, (char[]) rhs); + } else if (lhs instanceof byte[]) { + append((byte[]) lhs, (byte[]) rhs); + } else if (lhs instanceof double[]) { + append((double[]) lhs, (double[]) rhs); + } else if (lhs instanceof float[]) { + append((float[]) lhs, (float[]) rhs); + } else if (lhs instanceof boolean[]) { + append((boolean[]) lhs, (boolean[]) rhs); } else { - //'Switch' on type of array, to dispatch to the correct handler - // This handles multi dimensional arrays - if (lhs instanceof long[]) { - append((long[]) lhs, (long[]) rhs); - } else if (lhs instanceof int[]) { - append((int[]) lhs, (int[]) rhs); - } else if (lhs instanceof short[]) { - append((short[]) lhs, (short[]) rhs); - } else if (lhs instanceof char[]) { - append((char[]) lhs, (char[]) rhs); - } else if (lhs instanceof byte[]) { - append((byte[]) lhs, (byte[]) rhs); - } else if (lhs instanceof double[]) { - append((double[]) lhs, (double[]) rhs); - } else if (lhs instanceof float[]) { - append((float[]) lhs, (float[]) rhs); - } else if (lhs instanceof boolean[]) { - append((boolean[]) lhs, (boolean[]) rhs); - } else { - // Not an array of primitives - append((Object[]) lhs, (Object[]) rhs); - } + // Not an array of primitives + append((Object[]) lhs, (Object[]) rhs); } return this; } /** - *

        Test if two longs are equal.

        - * - * @param lhs the left hand long - * @param rhs the right hand long + *

        + * Test if two long s are equal. + *

        + * + * @param lhs + * the left hand long + * @param rhs + * the right hand long * @return EqualsBuilder - used to chain calls. */ public EqualsBuilder append(long lhs, long rhs) { @@ -433,7 +489,7 @@ *

        Test if two doubles are equal by testing that the * pattern of bits returned by doubleToLong are equal.

        * - *

        This handles NaNs, Infinties, and -0.0.

        + *

        This handles NaNs, Infinities, and -0.0.

        * *

        It is compatible with the hash code generated by * HashCodeBuilder.

        @@ -453,7 +509,7 @@ *

        Test if two floats are equal byt testing that the * pattern of bits returned by doubleToLong are equal.

        * - *

        This handles NaNs, Infinties, and -0.0.

        + *

        This handles NaNs, Infinities, and -0.0.

        * *

        It is compatible with the hash code generated by * HashCodeBuilder.

        @@ -502,19 +558,14 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { - Class lhsClass = lhs[i].getClass(); - if (!lhsClass.isInstance(rhs[i])) { - isEquals = false; //If the types don't match, not equal - break; - } append(lhs[i], rhs[i]); } return this; @@ -538,11 +589,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -569,11 +620,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -600,11 +651,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -631,11 +682,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -662,11 +713,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -693,11 +744,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -724,11 +775,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -755,11 +806,11 @@ return this; } if (lhs == null || rhs == null) { - isEquals = false; + this.setEquals(false); return this; } if (lhs.length != rhs.length) { - isEquals = false; + this.setEquals(false); return this; } for (int i = 0; i < lhs.length && isEquals; ++i) { @@ -769,13 +820,30 @@ } /** - *

        Return true if the fields that have been checked + *

        Returns true if the fields that have been checked * are all equal.

        * * @return boolean */ public boolean isEquals() { - return isEquals; + return this.isEquals; } + /** + * Sets the isEquals value. + * + * @param isEquals The value to set. + * @since 2.1 + */ + protected void setEquals(boolean isEquals) { + this.isEquals = isEquals; + } + + /** + * Reset the EqualsBuilder so you can use the same object again + * @since 2.5 + */ + public void reset() { + this.isEquals = true; + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/HashCodeBuilder.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/HashCodeBuilder.java (.../HashCodeBuilder.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/HashCodeBuilder.java (.../HashCodeBuilder.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,82 +1,64 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.apache.commons.lang.builder; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.apache.commons.lang.ArrayUtils; + /** - *

        Assists in implementing {@link Object#hashCode()} methods.

        - * - *

        This class enables a good hashCode method to be built for any class. It - * follows the rules laid out in the book - * Effective Java - * by Joshua Bloch. Writing a good hashCode method is actually quite - * difficult. This class aims to simplify the process.

        - * - *

        All relevant fields from the object should be included in the - * hashCode method. Derived fields may be excluded. In general, any - * field used in the equals method must be used in the hashCode - * method.

        - * - *

        To use this class write code as follows:

        + *

        + * Assists in implementing {@link Object#hashCode()} methods. + *

        + * + *

        + * This class enables a good hashCode method to be built for any class. It follows the rules laid out in + * the book Effective Java by Joshua Bloch. Writing a + * good hashCode method is actually quite difficult. This class aims to simplify the process. + *

        + * + *

        + * The following is the approach taken. When appending a data field, the current total is multiplied by the + * multiplier then a relevant value + * for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then + * appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45. + *

        + * + *

        + * All relevant fields from the object should be included in the hashCode method. Derived fields may be + * excluded. In general, any field used in the equals method must be used in the hashCode + * method. + *

        + * + *

        + * To use this class write code as follows: + *

        + * *
          * public class Person {
          *   String name;
          *   int age;
        - *   boolean isSmoker;
        + *   boolean smoker;
          *   ...
          *
          *   public int hashCode() {
        @@ -90,429 +72,653 @@
          *   }
          * }
          * 
        - * - *

        If required, the superclass hashCode() can be added - * using {@link #appendSuper}.

        - * - *

        Alternatively, there is a method that uses reflection to determine - * the fields to test. Because these fields are usually private, the method, - * reflectionHashCode, uses AccessibleObject.setAccessible to - * change the visibility of the fields. This will fail under a security manager, - * unless the appropriate permissions are set up correctly. It is also slower - * than testing explicitly.

        - * - *

        A typical invocation for this method would look like:

        + * + *

        + * If required, the superclass hashCode() can be added using {@link #appendSuper}. + *

        + * + *

        + * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are + * usually private, the method, reflectionHashCode, uses AccessibleObject.setAccessible + * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions + * are set up correctly. It is also slower than testing explicitly. + *

        + * + *

        + * A typical invocation for this method would look like: + *

        + * *
          * public int hashCode() {
          *   return HashCodeBuilder.reflectionHashCode(this);
          * }
          * 
        - * - * @author Stephen Colebourne + * + * @author Apache Software Foundation * @author Gary Gregory * @author Pete Gieser * @since 1.0 * @version $Id$ */ public class HashCodeBuilder { - /** - * Constant to use in building the hashCode. + *

        + * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops. + *

        + * + * @since 2.3 */ - private final int iConstant; + private static final ThreadLocal REGISTRY = new ThreadLocal(); + + /* + * N.B. we cannot store the actual objects in a HashSet, as that would use the very hashCode() + * we are in the process of calculating. + * + * So we generate a one-to-one mapping from the original object to a new object. + * + * Now HashSet uses equals() to determine if two elements with the same hashcode really + * are equal, so we also need to ensure that the replacement objects are only equal + * if the original objects are identical. + * + * The original implementation (2.4 and before) used the System.indentityHashCode() + * method - however this is not guaranteed to generate unique ids (e.g. LANG-459) + * + * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey) + * to disambiguate the duplicate ids. + */ + /** - * Running total of the hashCode. + *

        + * Returns the registry of objects being traversed by the reflection methods in the current thread. + *

        + * + * @return Set the registry of objects being traversed + * @since 2.3 */ - private int iTotal = 0; + static Set getRegistry() { + return (Set) REGISTRY.get(); + } /** - *

        Constructor.

        - * - *

        This constructor uses two hard coded choices for the constants - * needed to build a hashCode.

        + *

        + * Returns true if the registry contains the given object. Used by the reflection methods to avoid + * infinite loops. + *

        + * + * @param value + * The object to lookup in the registry. + * @return boolean true if the registry contains the given object. + * @since 2.3 */ - public HashCodeBuilder() { - super(); - iConstant = 37; - iTotal = 17; + static boolean isRegistered(Object value) { + Set registry = getRegistry(); + return registry != null && registry.contains(new IDKey(value)); } /** - *

        Constructor.

        - * - *

        Two randomly chosen, non-zero, odd numbers must be passed in. - * Ideally these should be different for each class, however this is - * not vital.

        - * - *

        Prime numbers are preferred, especially for the multiplier.

        - * - * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value - * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier - * @throws IllegalArgumentException if the number is zero or even + *

        + * Appends the fields and values defined by the given object of the given Class. + *

        + * + * @param object + * the object to append details of + * @param clazz + * the class to append details of + * @param builder + * the builder to append to + * @param useTransients + * whether to use transient fields + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code */ - public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { - super(); - if (initialNonZeroOddNumber == 0) { - throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); + private static void reflectionAppend(Object object, Class clazz, HashCodeBuilder builder, boolean useTransients, + String[] excludeFields) { + if (isRegistered(object)) { + return; } - if (initialNonZeroOddNumber % 2 == 0) { - throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); + try { + register(object); + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (!ArrayUtils.contains(excludeFields, field.getName()) + && (field.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(field.getModifiers())) + && (!Modifier.isStatic(field.getModifiers()))) { + try { + Object fieldValue = field.get(object); + builder.append(fieldValue); + } catch (IllegalAccessException e) { + // this can't happen. Would get a Security exception instead + // throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } finally { + unregister(object); } - if (multiplierNonZeroOddNumber == 0) { - throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); - } - if (multiplierNonZeroOddNumber % 2 == 0) { - throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); - } - iConstant = multiplierNonZeroOddNumber; - iTotal = initialNonZeroOddNumber; } - //------------------------------------------------------------------------- - /** - *

        This method uses reflection to build a valid hash code.

        - * - *

        This constructor uses two hard coded choices for the constants - * needed to build a hash code.

        - * - *

        It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run under - * a security manager, if the permissions are not set up correctly. It is - * also not as efficient as testing explicitly.

        - * - *

        Transient members will be not be used, as they are likely derived - * fields, and not part of the value of the Object.

        - * - *

        Static fields will not be tested. Superclass fields will be included.

        - * - * @param object the Object to create a hashCode for + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + *

        + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

        + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for * @return int hash code - * @throws IllegalArgumentException if the object is null + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even */ - public static int reflectionHashCode(Object object) { - return reflectionHashCode(17, 37, object, false, null); + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null, null); } /** - *

        This method uses reflection to build a valid hash code.

        - * - *

        This constructor uses two hard coded choices for the constants needed - * to build a hash code.

        - * - *

        It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run under - * a security manager, if the permissions are not set up correctly. It is - * also not as efficient as testing explicitly.

        - * - *

        If the TestTransients parameter is set to true, transient - * members will be tested, otherwise they are ignored, as they are likely - * derived fields, and not part of the value of the Object.

        - * - *

        Static fields will not be tested. Superclass fields will be included.

        - * - * @param object the Object to create a hashCode for - * @param testTransients whether to include transient fields + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + *

        + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

        + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields * @return int hash code - * @throws IllegalArgumentException if the object is null + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even */ - public static int reflectionHashCode(Object object, boolean testTransients) { - return reflectionHashCode(17, 37, object, testTransients, null); + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object, + boolean testTransients) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null, + null); } /** - *

        This method uses reflection to build a valid hash code.

        - * - *

        It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run under - * a security manager, if the permissions are not set up correctly. It is - * also not as efficient as testing explicitly.

        - * - *

        Transient members will be not be used, as they are likely derived - * fields, and not part of the value of the Object.

        - * - *

        Static fields will not be tested. Superclass fields will be included.

        - * - *

        Two randomly chosen, non-zero, odd numbers must be passed in. Ideally - * these should be different for each class, however this is not vital. - * Prime numbers are preferred, especially for the multiplier.

        - * - * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value - * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier - * @param object the Object to create a hashCode for + * Calls {@link #reflectionHashCode(int, int, Object, boolean, Class, String[])} with excludeFields set to + * null. + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null * @return int hash code - * @throws IllegalArgumentException if the Object is null - * @throws IllegalArgumentException if the number is zero or even */ - public static int reflectionHashCode( - int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) { - return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null); + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object, + boolean testTransients, Class reflectUpToClass) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, + reflectUpToClass, null); } /** - *

        This method uses reflection to build a valid hash code.

        - * - *

        It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run under - * a security manager, if the permissions are not set up correctly. It is also - * not as efficient as testing explicitly.

        - * - *

        If the TestTransients parameter is set to true, transient - * members will be tested, otherwise they are ignored, as they are likely - * derived fields, and not part of the value of the Object.

        - * - *

        Static fields will not be tested. Superclass fields will be included.

        - * - *

        Two randomly chosen, non-zero, odd numbers must be passed in. Ideally - * these should be different for each class, however this is not vital. - * Prime numbers are preferred, especially for the multiplier.

        - * - * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value - * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier - * @param object the Object to create a hashCode for - * @param testTransients whether to include transient fields + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

        + * + *

        + * Static fields will not be included. Superclass fields will be included up to and including the specified + * superclass. A null superclass is treated as java.lang.Object. + *

        + * + *

        + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

        + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @param excludeFields + * array of field names to exclude from use in calculation of hash code * @return int hash code - * @throws IllegalArgumentException if the Object is null - * @throws IllegalArgumentException if the number is zero or even - */ - public static int reflectionHashCode( - int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, - Object object, boolean testTransients) { - return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null); - } - - /** - *

        This method uses reflection to build a valid hash code.

        - * - *

        It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run under - * a security manager, if the permissions are not set up correctly. It is also - * not as efficient as testing explicitly.

        - * - *

        If the TestTransients parameter is set to true, transient - * members will be tested, otherwise they are ignored, as they are likely - * derived fields, and not part of the value of the Object.

        - * - *

        Static fields will not be included. Superclass fields will be included - * up to and including the specified superclass. A null superclass is treated - * as java.lang.Object.

        - * - *

        Two randomly chosen, non-zero, odd numbers must be passed in. Ideally - * these should be different for each class, however this is not vital. - * Prime numbers are preferred, especially for the multiplier.

        - * - * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value - * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier - * @param object the Object to create a hashCode for - * @param testTransients whether to include transient fields - * @param reflectUpToClass the superclass to reflect up to (inclusive), - * may be null - * @return int hash code - * @throws IllegalArgumentException if the Object is null - * @throws IllegalArgumentException if the number is zero or even + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even * @since 2.0 */ - public static int reflectionHashCode( - int initialNonZeroOddNumber, - int multiplierNonZeroOddNumber, - Object object, - boolean testTransients, - Class reflectUpToClass) { + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object, + boolean testTransients, Class reflectUpToClass, String[] excludeFields) { if (object == null) { throw new IllegalArgumentException("The object to build a hash code for must not be null"); } HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber); Class clazz = object.getClass(); - reflectionAppend(object, clazz, builder, testTransients); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); while (clazz.getSuperclass() != null && clazz != reflectUpToClass) { clazz = clazz.getSuperclass(); - reflectionAppend(object, clazz, builder, testTransients); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); } return builder.toHashCode(); } /** - *

        Appends the fields and values defined by the given object of the - * given Class.

        + *

        + * This method uses reflection to build a valid hash code. + *

        * - * @param object the object to append details of - * @param clazz the class to append details of - * @param builder the builder to append to - * @param useTransients whether to use transient fields + *

        + * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + * @param object + * the Object to create a hashCode for + * @return int hash code + * @throws IllegalArgumentException + * if the object is null */ - private static void reflectionAppend(Object object, Class clazz, HashCodeBuilder builder, boolean useTransients) { - Field[] fields = clazz.getDeclaredFields(); - AccessibleObject.setAccessible(fields, true); - for (int i = 0; i < fields.length; i++) { - Field f = fields[i]; - if ((f.getName().indexOf('$') == -1) - && (useTransients || !Modifier.isTransient(f.getModifiers())) - && (!Modifier.isStatic(f.getModifiers()))) { - try { - builder.append(f.get(object)); - } catch (IllegalAccessException e) { - //this can't happen. Would get a Security exception instead - //throw a runtime exception in case the impossible happens. - throw new InternalError("Unexpected IllegalAccessException"); - } - } - } + public static int reflectionHashCode(Object object) { + return reflectionHashCode(17, 37, object, false, null, null); } - //------------------------------------------------------------------------- + /** + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @return int hash code + * @throws IllegalArgumentException + * if the object is null + */ + public static int reflectionHashCode(Object object, boolean testTransients) { + return reflectionHashCode(17, 37, object, testTransients, null, null); + } /** - *

        Adds the result of super.hashCode() to this builder.

        - * - * @param superHashCode the result of calling super.hashCode() - * @return this HashCodeBuilder, used to chain calls. - * @since 2.0 + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + * @param object + * the Object to create a hashCode for + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is null */ - public HashCodeBuilder appendSuper(int superHashCode) { - iTotal = iTotal * iConstant + superHashCode; - return this; + public static int reflectionHashCode(Object object, Collection /* String */excludeFields) { + return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); } - //------------------------------------------------------------------------- + // ------------------------------------------------------------------------- /** - *

        Append a hashCode for an Object.

        - * - * @param object the Object to add to the hashCode - * @return this + *

        + * This method uses reflection to build a valid hash code. + *

        + * + *

        + * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

        + * + *

        + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

        + * + *

        + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

        + * + *

        + * Static fields will not be tested. Superclass fields will be included. + *

        + * + * @param object + * the Object to create a hashCode for + * @param excludeFields + * array of field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is null */ - public HashCodeBuilder append(Object object) { - if (object == null) { - iTotal = iTotal * iConstant; + public static int reflectionHashCode(Object object, String[] excludeFields) { + return reflectionHashCode(17, 37, object, false, null, excludeFields); + } - } else { - if (object.getClass().isArray() == false) { - //the simple case, not an array, just the element - iTotal = iTotal * iConstant + object.hashCode(); + /** + *

        + * Registers the given object. Used by the reflection methods to avoid infinite loops. + *

        + * + * @param value + * The object to register. + */ + static void register(Object value) { + synchronized (HashCodeBuilder.class) { + if (getRegistry() == null) { + REGISTRY.set(new HashSet()); + } + } + getRegistry().add(new IDKey(value)); + } - } else { - //'Switch' on type of array, to dispatch to the correct handler - // This handles multi dimensional arrays - if (object instanceof long[]) { - append((long[]) object); - } else if (object instanceof int[]) { - append((int[]) object); - } else if (object instanceof short[]) { - append((short[]) object); - } else if (object instanceof char[]) { - append((char[]) object); - } else if (object instanceof byte[]) { - append((byte[]) object); - } else if (object instanceof double[]) { - append((double[]) object); - } else if (object instanceof float[]) { - append((float[]) object); - } else if (object instanceof boolean[]) { - append((boolean[]) object); - } else { - // Not an array of primitives - append((Object[]) object); + /** + *

        + * Unregisters the given object. + *

        + * + *

        + * Used by the reflection methods to avoid infinite loops. + * + * @param value + * The object to unregister. + * @since 2.3 + */ + static void unregister(Object value) { + Set registry = getRegistry(); + if (registry != null) { + registry.remove(new IDKey(value)); + synchronized (HashCodeBuilder.class) { + //read again + registry = getRegistry(); + if (registry != null && registry.isEmpty()) { + REGISTRY.set(null); } } } - return this; } /** - *

        Append a hashCode for a long.

        - * - * @param value the long to add to the hashCode - * @return this + * Constant to use in building the hashCode. */ - public HashCodeBuilder append(long value) { - iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32))); - return this; + private final int iConstant; + + /** + * Running total of the hashCode. + */ + private int iTotal = 0; + + /** + *

        + * Uses two hard coded choices for the constants needed to build a hashCode. + *

        + */ + public HashCodeBuilder() { + iConstant = 37; + iTotal = 17; } /** - *

        Append a hashCode for an int.

        - * - * @param value the int to add to the hashCode - * @return this + *

        + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. + *

        + * + *

        + * Prime numbers are preferred, especially for the multiplier. + *

        + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @throws IllegalArgumentException + * if the number is zero or even */ - public HashCodeBuilder append(int value) { - iTotal = iTotal * iConstant + value; - return this; + public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { + if (initialNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); + } + if (initialNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); + } + if (multiplierNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); + } + if (multiplierNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); + } + iConstant = multiplierNonZeroOddNumber; + iTotal = initialNonZeroOddNumber; } /** - *

        Append a hashCode for a short.

        - * - * @param value the short to add to the hashCode + *

        + * Append a hashCode for a boolean. + *

        + *

        + * This adds 1 when true, and 0 when false to the hashCode. + *

        + *

        + * This is in contrast to the standard java.lang.Boolean.hashCode handling, which computes + * a hashCode value of 1231 for java.lang.Boolean instances + * that represent true or 1237 for java.lang.Boolean instances + * that represent false. + *

        + *

        + * This is in accordance with the Effective Java design. + *

        + * + * @param value + * the boolean to add to the hashCode * @return this */ - public HashCodeBuilder append(short value) { - iTotal = iTotal * iConstant + value; + public HashCodeBuilder append(boolean value) { + iTotal = iTotal * iConstant + (value ? 0 : 1); return this; } /** - *

        Append a hashCode for a char.

        - * - * @param value the char to add to the hashCode + *

        + * Append a hashCode for a boolean array. + *

        + * + * @param array + * the array to add to the hashCode * @return this */ - public HashCodeBuilder append(char value) { - iTotal = iTotal * iConstant + value; + public HashCodeBuilder append(boolean[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (int i = 0; i < array.length; i++) { + append(array[i]); + } + } return this; } + // ------------------------------------------------------------------------- + /** - *

        Append a hashCode for a byte.

        - * - * @param value the byte to add to the hashCode + *

        + * Append a hashCode for a byte. + *

        + * + * @param value + * the byte to add to the hashCode * @return this */ public HashCodeBuilder append(byte value) { iTotal = iTotal * iConstant + value; return this; } - /** - *

        Append a hashCode for a double.

        - * - * @param value the double to add to the hashCode - * @return this - */ - public HashCodeBuilder append(double value) { - return append(Double.doubleToLongBits(value)); - } + // ------------------------------------------------------------------------- /** - *

        Append a hashCode for a float.

        - * - * @param value the float to add to the hashCode + *

        + * Append a hashCode for a byte array. + *

        + * + * @param array + * the array to add to the hashCode * @return this */ - public HashCodeBuilder append(float value) { - iTotal = iTotal * iConstant + Float.floatToIntBits(value); + public HashCodeBuilder append(byte[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (int i = 0; i < array.length; i++) { + append(array[i]); + } + } return this; } /** - *

        Append a hashCode for a boolean.

        - * - * @param value the boolean to add to the hashCode + *

        + * Append a hashCode for a char. + *

        + * + * @param value + * the char to add to the hashCode * @return this */ - public HashCodeBuilder append(boolean value) { - iTotal = iTotal * iConstant + (value ? 0 : 1); + public HashCodeBuilder append(char value) { + iTotal = iTotal * iConstant + value; return this; } /** - *

        Append a hashCode for an Object array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a char array. + *

        + * + * @param array + * the array to add to the hashCode * @return this */ - public HashCodeBuilder append(Object[] array) { + public HashCodeBuilder append(char[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -524,12 +730,28 @@ } /** - *

        Append a hashCode for a long array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a double. + *

        + * + * @param value + * the double to add to the hashCode * @return this */ - public HashCodeBuilder append(long[] array) { + public HashCodeBuilder append(double value) { + return append(Double.doubleToLongBits(value)); + } + + /** + *

        + * Append a hashCode for a double array. + *

        + * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(double[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -541,29 +763,29 @@ } /** - *

        Append a hashCode for an int array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a float. + *

        + * + * @param value + * the float to add to the hashCode * @return this */ - public HashCodeBuilder append(int[] array) { - if (array == null) { - iTotal = iTotal * iConstant; - } else { - for (int i = 0; i < array.length; i++) { - append(array[i]); - } - } + public HashCodeBuilder append(float value) { + iTotal = iTotal * iConstant + Float.floatToIntBits(value); return this; } /** - *

        Append a hashCode for a short array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a float array. + *

        + * + * @param array + * the array to add to the hashCode * @return this */ - public HashCodeBuilder append(short[] array) { + public HashCodeBuilder append(float[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -575,12 +797,29 @@ } /** - *

        Append a hashCode for a char array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for an int. + *

        + * + * @param value + * the int to add to the hashCode * @return this */ - public HashCodeBuilder append(char[] array) { + public HashCodeBuilder append(int value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + *

        + * Append a hashCode for an int array. + *

        + * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(int[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -592,12 +831,33 @@ } /** - *

        Append a hashCode for a byte array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a long. + *

        + * + * @param value + * the long to add to the hashCode * @return this */ - public HashCodeBuilder append(byte[] array) { + // NOTE: This method uses >> and not >>> as Effective Java and + // Long.hashCode do. Ideally we should switch to >>> at + // some stage. There are backwards compat issues, so + // that will have to wait for the time being. cf LANG-342. + public HashCodeBuilder append(long value) { + iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32))); + return this; + } + + /** + *

        + * Append a hashCode for a long array. + *

        + * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(long[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -609,29 +869,59 @@ } /** - *

        Append a hashCode for a double array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for an Object. + *

        + * + * @param object + * the Object to add to the hashCode * @return this */ - public HashCodeBuilder append(double[] array) { - if (array == null) { + public HashCodeBuilder append(Object object) { + if (object == null) { iTotal = iTotal * iConstant; + } else { - for (int i = 0; i < array.length; i++) { - append(array[i]); + if(object.getClass().isArray()) { + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays + if (object instanceof long[]) { + append((long[]) object); + } else if (object instanceof int[]) { + append((int[]) object); + } else if (object instanceof short[]) { + append((short[]) object); + } else if (object instanceof char[]) { + append((char[]) object); + } else if (object instanceof byte[]) { + append((byte[]) object); + } else if (object instanceof double[]) { + append((double[]) object); + } else if (object instanceof float[]) { + append((float[]) object); + } else if (object instanceof boolean[]) { + append((boolean[]) object); + } else { + // Not an array of primitives + append((Object[]) object); + } + } else { + iTotal = iTotal * iConstant + object.hashCode(); } } return this; } /** - *

        Append a hashCode for a float array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for an Object array. + *

        + * + * @param array + * the array to add to the hashCode * @return this */ - public HashCodeBuilder append(float[] array) { + public HashCodeBuilder append(Object[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -643,12 +933,29 @@ } /** - *

        Append a hashCode for a boolean array.

        - * - * @param array the array to add to the hashCode + *

        + * Append a hashCode for a short. + *

        + * + * @param value + * the short to add to the hashCode * @return this */ - public HashCodeBuilder append(boolean[] array) { + public HashCodeBuilder append(short value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + *

        + * Append a hashCode for a short array. + *

        + * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(short[] array) { if (array == null) { iTotal = iTotal * iConstant; } else { @@ -660,12 +967,42 @@ } /** - *

        Return the computed hashCode.

        - * + *

        + * Adds the result of super.hashCode() to this builder. + *

        + * + * @param superHashCode + * the result of calling super.hashCode() + * @return this HashCodeBuilder, used to chain calls. + * @since 2.0 + */ + public HashCodeBuilder appendSuper(int superHashCode) { + iTotal = iTotal * iConstant + superHashCode; + return this; + } + + /** + *

        + * Return the computed hashCode. + *

        + * * @return hashCode based on the fields appended */ public int toHashCode() { return iTotal; } + /** + *

        + * The computed hashCode from toHashCode() is returned due to the likelyhood + * of bugs in mis-calling toHashCode() and the unlikelyness of it mattering what the hashCode for + * HashCodeBuilder itself is. + * + * @return hashCode based on the fields appended + * @since 2.5 + */ + public int hashCode() { + return toHashCode(); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/IDKey.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/IDKey.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/IDKey.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.commons.lang.builder; + +// adapted from org.apache.axis.utils.IDKey + +/** + * Wrap an identity key (System.identityHashCode()) + * so that an object can only be equal() to itself. + * + * This is necessary to disambiguate the occasional duplicate + * identityHashCodes that can occur. + * + * @author Apache Software Foundation + */ +final class IDKey { + private final Object value; + private final int id; + + /** + * Constructor for IDKey + * @param _value The value + */ + public IDKey(Object _value) { + // This is the Object hashcode + id = System.identityHashCode(_value); + // There have been some cases (LANG-459) that return the + // same identity hash code for different objects. So + // the value is also added to disambiguate these cases. + value = _value; + } + + /** + * returns hashcode - i.e. the system identity hashcode. + * @return the hashcode + */ + public int hashCode() { + return id; + } + + /** + * checks if instances are equal + * @param other The other object to compare to + * @return if the instances are for the same object + */ + public boolean equals(Object other) { + if (!(other instanceof IDKey)) { + return false; + } + IDKey idKey = (IDKey) other; + if (id != idKey.id) { + return false; + } + // Note that identity equals is used. + return value == idKey.value; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ReflectionToStringBuilder.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ReflectionToStringBuilder.java (.../ReflectionToStringBuilder.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ReflectionToStringBuilder.java (.../ReflectionToStringBuilder.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,93 +1,76 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.apache.commons.lang.builder; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ClassUtils; /** - *

        Assists in implementing {@link Object#toString()} methods using reflection.

        - * - *

        This class uses reflection to determine the fields to append. - * Because these fields are usually private, the class - * uses AccessibleObject.setAccessible to - * change the visibility of the fields. This will fail under a security manager, - * unless the appropriate permissions are set up correctly.

        - * - *

        A typical invocation for this method would look like:

        + *

        + * Assists in implementing {@link Object#toString()} methods using reflection. + *

        + * + *

        + * This class uses reflection to determine the fields to append. Because these fields are usually private, the class + * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to + * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are + * set up correctly. + *

        + * + *

        + * A typical invocation for this method would look like: + *

        + * *
          * public String toString() {
          *   return ReflectionToStringBuilder.toString(this);
          * }
        - * - *

        You can also use the builder to debug 3rd party objects:

        + * + * + * + *

        + * You can also use the builder to debug 3rd party objects: + *

        + * *
          * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
        * - *

        A subclass can control field output by overriding the methods: - *

          - *
        • {@link #accept(java.lang.reflect.Field)}
        • - *
        • {@link #getValue(java.lang.reflect.Field)}
        • + * + * + *

          + * A subclass can control field output by overriding the methods: + *

            + *
          • {@link #accept(java.lang.reflect.Field)}
          • + *
          • {@link #getValue(java.lang.reflect.Field)}
          • *
          *

          - *

          For example, this method does not include the password field in the returned - * String:

          + *

          + * For example, this method does not include the password field in the returned + * String: + *

          + * *
            * public String toString() {
            *     return (new ReflectionToStringBuilder(this) {
          @@ -97,241 +80,415 @@
            *     }).toString();
            * }
          * - *

          The exact format of the toString is determined by - * the {@link ToStringStyle} passed into the constructor.

          - * + * + * + *

          + * The exact format of the toString is determined by the {@link ToStringStyle} passed into the + * constructor. + *

          + * + * @author Apache Software Foundation * @author Gary Gregory - * @author Stephen Colebourne * @author Pete Gieser * @since 2.0 * @version $Id$ */ public class ReflectionToStringBuilder extends ToStringBuilder { /** - *

          A registry of objects used by reflectionToString methods - * to detect cyclical object references and avoid infinite loops.

          + *

          + * Builds a toString value using the default ToStringStyle through reflection. + *

          + * + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + *

          + * + * @param object + * the Object to be output + * @return the String result + * @throws IllegalArgumentException + * if the Object is null */ - private static ThreadLocal registry = new ThreadLocal() { - protected synchronized Object initialValue() { - // The HashSet implementation is not synchronized, - // which is just what we need here. - return new HashSet(); - } - }; + public static String toString(Object object) { + return toString(object, null, false, false, null); + } /** - *

          Returns the registry of objects being traversed by the - * reflectionToString methods in the current thread.

          - * - * @return Set the registry of objects being traversed + *

          + * Builds a toString value through reflection. + *

          + * + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + *

          + * + *

          + * If the style is null, the default ToStringStyle is used. + *

          + * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @return the String result + * @throws IllegalArgumentException + * if the Object or ToStringStyle is null */ - static Set getRegistry() { - return (Set) registry.get(); + public static String toString(Object object, ToStringStyle style) { + return toString(object, style, false, false, null); } /** - *

          Returns true if the registry contains the given object. - * Used by the reflection methods to avoid infinite loops.

          + *

          + * Builds a toString value through reflection. + *

          * - * @param value The object to lookup in the registry. - * @return boolean true if the registry contains the given object. + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * If the outputTransients is true, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

          + * + *

          + * Static fields will not be included. Superclass fields will be appended. + *

          + * + *

          + * If the style is null, the default ToStringStyle is used. + *

          + * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is null */ - static boolean isRegistered(Object value) { - return getRegistry().contains(value); + public static String toString(Object object, ToStringStyle style, boolean outputTransients) { + return toString(object, style, outputTransients, false, null); } /** - *

          Registers the given object. - * Used by the reflection methods to avoid infinite loops.

          + *

          + * Builds a toString value through reflection. + *

          * - * @param value The object to register. + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

          + * + *

          + * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. + *

          + * + *

          + * Static fields will not be included. Superclass fields will be appended. + *

          + * + *

          + * If the style is null, the default ToStringStyle is used. + *

          + * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is null + * @since 2.1 */ - static void register(Object value) { - getRegistry().add(value); + public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) { + return toString(object, style, outputTransients, outputStatics, null); } - + /** - *

          This method uses reflection to build a suitable - * toString using the default ToStringStyle. - * - *

          It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run - * under a security manager, if the permissions are not set up correctly. - * It is also not as efficient as testing explicitly.

          - * - *

          Transient members will be not be included, as they are likely derived. - * Static fields will not be included. Superclass fields will be appended.

          - * - * @param object the Object to be output + *

          + * Builds a toString value through reflection. + *

          + * + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

          + * + *

          + * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. + *

          + * + *

          + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * java.lang.Object. + *

          + * + *

          + * If the style is null, the default ToStringStyle is used. + *

          + * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null * @return the String result - * @throws IllegalArgumentException if the Object is null + * @throws IllegalArgumentException + * if the Object is null + * @since 2.1 */ - public static String toString(Object object) { - return toString(object, null, false, null); + public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics, + Class reflectUpToClass) { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) + .toString(); } /** - *

          This method uses reflection to build a suitable - * toString.

          - * - *

          It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run - * under a security manager, if the permissions are not set up correctly. - * It is also not as efficient as testing explicitly.

          - * - *

          Transient members will be not be included, as they are likely derived. - * Static fields will not be included. Superclass fields will be appended.

          - * - *

          If the style is null, the default - * ToStringStyle is used.

          + *

          + * Builds a toString value through reflection. + *

          * - * @param object the Object to be output - * @param style the style of the toString to create, - * may be null + *

          + * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

          + * + *

          + * If the outputTransients is true, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

          + * + *

          + * Static fields will not be included. Superclass fields will be appended up to and including the specified + * superclass. A null superclass is treated as java.lang.Object. + *

          + * + *

          + * If the style is null, the default ToStringStyle is used. + *

          + * + * @deprecated Use {@link #toString(Object,ToStringStyle,boolean,boolean,Class)} + * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null * @return the String result - * @throws IllegalArgumentException if the Object or - * ToStringStyle is null + * @throws IllegalArgumentException + * if the Object is null + * @since 2.0 */ - public static String toString(Object object, ToStringStyle style) { - return toString(object, style, false, null); + public static String toString(Object object, ToStringStyle style, + boolean outputTransients, Class reflectUpToClass) + { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString(); } /** - *

          This method uses reflection to build a suitable - * toString.

          - * - *

          It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run - * under a security manager, if the permissions are not set up correctly. - * It is also not as efficient as testing explicitly.

          - * - *

          If the outputTransients is true, - * transient members will be output, otherwise they are ignored, - * as they are likely derived fields, and not part of the value of the - * Object.

          - * - *

          Static fields will not be included. Superclass fields will be appended.

          - * - *

          If the style is null, the default - * ToStringStyle is used.

          + * Builds a String for a toString method excluding the given field name. * - * @param object the Object to be output - * @param style the style of the toString to create, - * may be null - * @param outputTransients whether to include transient fields - * @return the String result - * @throws IllegalArgumentException if the Object is null + * @param object + * The object to "toString". + * @param excludeFieldName + * The field name to exclude + * @return The toString value. */ - public static String toString(Object object, ToStringStyle style, boolean outputTransients) { - return toString(object, style, outputTransients, null); + public static String toStringExclude(Object object, final String excludeFieldName) { + return toStringExclude(object, new String[]{excludeFieldName}); } /** - *

          This method uses reflection to build a suitable - * toString.

          - * - *

          It uses AccessibleObject.setAccessible to gain access to private - * fields. This means that it will throw a security exception if run - * under a security manager, if the permissions are not set up correctly. - * It is also not as efficient as testing explicitly.

          - * - *

          If the outputTransients is true, - * transient members will be output, otherwise they are ignored, - * as they are likely derived fields, and not part of the value of the - * Object.

          - * - *

          Static fields will not be included. Superclass fields will be appended - * up to and including the specified superclass. A null superclass is treated - * as java.lang.Object.

          - * - *

          If the style is null, the default - * ToStringStyle is used.

          + * Builds a String for a toString method excluding the given field names. * - * @param object the Object to be output - * @param style the style of the toString to create, - * may be null - * @param outputTransients whether to include transient fields - * @param reflectUpToClass the superclass to reflect up to (inclusive), - * may be null - * @return the String result - * @throws IllegalArgumentException if the Object is null + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude. Null excludes nothing. + * @return The toString value. */ - public static String toString( - Object object, - ToStringStyle style, - boolean outputTransients, - Class reflectUpToClass) { - return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString(); + public static String toStringExclude(Object object, Collection /*String*/ excludeFieldNames) { + return toStringExclude(object, toNoNullStringArray(excludeFieldNames)); } /** - *

          Unregisters the given object.

          - * - *

          Used by the reflection methods to avoid infinite loops.

          + * Converts the given Collection into an array of Strings. The returned array does not contain null + * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element + * is null. * - * @param value The object to unregister. + * @param collection + * The collection to convert + * @return A new array of Strings. */ - static void unregister(Object value) { - getRegistry().remove(value); + static String[] toNoNullStringArray(Collection collection) { + if (collection == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return toNoNullStringArray(collection.toArray()); } /** + * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists + * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} + * if an array element is null. + * + * @param array + * The array to check + * @return The given array or a new array without null. + */ + static String[] toNoNullStringArray(Object[] array) { + ArrayList list = new ArrayList(array.length); + for (int i = 0; i < array.length; i++) { + Object e = array[i]; + if (e != null) { + list.add(e.toString()); + } + } + return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + + /** + * Builds a String for a toString method excluding the given field names. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude + * @return The toString value. + */ + public static String toStringExclude(Object object, String[] excludeFieldNames) { + return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString(); + } + + /** + * Whether or not to append static fields. + */ + private boolean appendStatics = false; + + /** * Whether or not to append transient fields. */ private boolean appendTransients = false; /** + * Which field names to exclude from output. Intended for fields like "password". + */ + private String[] excludeFieldNames; + + /** * The last super class to stop appending fields for. */ private Class upToClass = null; /** - *

          Constructor.

          - * - *

          This constructor outputs using the default style set with - * setDefaultStyle.

          + *

          + * Constructor. + *

          * - * @param object the Object to build a toString for, - * must not be null - * @throws IllegalArgumentException if the Object passed in is - * null + *

          + * This constructor outputs using the default style set with setDefaultStyle. + *

          + * + * @param object + * the Object to build a toString for, must not be null + * @throws IllegalArgumentException + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object) { super(object); } /** - *

          Constructor.

          - * - *

          If the style is null, the default style is used.

          + *

          + * Constructor. + *

          * - * @param object the Object to build a toString for, - * must not be null - * @param style the style of the toString to create, - * may be null - * @throws IllegalArgumentException if the Object passed in is - * null + *

          + * If the style is null, the default style is used. + *

          + * + * @param object + * the Object to build a toString for, must not be null + * @param style + * the style of the toString to create, may be null + * @throws IllegalArgumentException + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object, ToStringStyle style) { super(object, style); } /** - *

          Constructor.

          - * - *

          If the style is null, the default style is used.

          - * - *

          If the buffer is null, a new one is created.

          + *

          + * Constructor. + *

          * - * @param object the Object to build a toString for, - * must not be null - * @param style the style of the toString to create, - * may be null - * @param buffer the StringBuffer to populate, may be - * null - * @throws IllegalArgumentException if the Object passed in is - * null + *

          + * If the style is null, the default style is used. + *

          + * + *

          + * If the buffer is null, a new one is created. + *

          + * + * @param object + * the Object to build a toString for + * @param style + * the style of the toString to create, may be null + * @param buffer + * the StringBuffer to populate, may be null + * @throws IllegalArgumentException + * if the Object passed in is null */ public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { super(object, style, buffer); @@ -340,107 +497,135 @@ /** * Constructor. * - * @param object the Object to build a toString for, - * must not be null - * @param style the style of the toString to create, - * may be null - * @param buffer the StringBuffer to populate, may be - * null - * @param reflectUpToClass the superclass to reflect up to (inclusive), - * may be null - * @param outputTransients whether to include transient fields + * @deprecated Use {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}. + * + * @param object + * the Object to build a toString for + * @param style + * the style of the toString to create, may be null + * @param buffer + * the StringBuffer to populate, may be null + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @param outputTransients + * whether to include transient fields */ - public ReflectionToStringBuilder( - Object object, - ToStringStyle style, - StringBuffer buffer, - Class reflectUpToClass, - boolean outputTransients) { + public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, + boolean outputTransients) { super(object, style, buffer); this.setUpToClass(reflectUpToClass); this.setAppendTransients(outputTransients); } /** + * Constructor. + * + * @param object + * the Object to build a toString for + * @param style + * the style of the toString to create, may be null + * @param buffer + * the StringBuffer to populate, may be null + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @since 2.1 + */ + public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, + boolean outputTransients, boolean outputStatics) { + super(object, style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + } + + /** * Returns whether or not to append the given Field. *
            - *
          • Static fields are not appended.
          • - *
          • Transient fields are appended only if {@link #isAppendTransients()} returns true. - *
          • Inner class fields are not appened.
          • + *
          • Transient fields are appended only if {@link #isAppendTransients()} returns true. + *
          • Static fields are appended only if {@link #isAppendStatics()} returns true. + *
          • Inner class fields are not appened.
          • *
          - * @param field The Field to test. + * + * @param field + * The Field to test. * @return Whether or not to append the given Field. */ protected boolean accept(Field field) { - String fieldName = field.getName(); - return (fieldName.indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) == -1) - && (this.isAppendTransients() || !Modifier.isTransient(field.getModifiers())) - && (!Modifier.isStatic(field.getModifiers())); + if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) { + // Reject field from inner class. + return false; + } + if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) { + // Reject transient fields. + return false; + } + if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) { + // Reject static fields. + return false; + } + if (this.getExcludeFieldNames() != null + && Arrays.binarySearch(this.getExcludeFieldNames(), field.getName()) >= 0) { + // Reject fields from the getExcludeFieldNames list. + return false; + } + return true; } /** - *

          Appends the fields and values defined by the given object of the - * given Class.

          - * - *

          If a cycle is detected as an object is "toString()'ed", - * such an object is rendered as if Object.toString() - * had been called and not implemented by the object.

          + *

          + * Appends the fields and values defined by the given object of the given Class. + *

          * - * @param clazz The class of object parameter + *

          + * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if + * Object.toString() had been called and not implemented by the object. + *

          + * + * @param clazz + * The class of object parameter */ protected void appendFieldsIn(Class clazz) { - if (isRegistered(this.getObject())) { - // The object has already been appended, therefore we have an object cycle. - // Append a simple Object.toString style string. The field name is already appended at this point. - this.appendAsObjectToString(this.getObject()); + if (clazz.isArray()) { + this.reflectionAppendArray(this.getObject()); return; } - try { - this.registerObject(); - if (clazz.isArray()) { - this.reflectionAppendArray(this.getObject()); - return; - } - Field[] fields = clazz.getDeclaredFields(); - AccessibleObject.setAccessible(fields, true); - for (int i = 0; i < fields.length; i++) { - Field field = fields[i]; - String fieldName = field.getName(); - if (this.accept(field)) { - try { - // Warning: Field.get(Object) creates wrappers objects for primitive types. - Object fieldValue = this.getValue(field); - if (isRegistered(fieldValue) && !field.getType().isPrimitive()) { - // A known field value has already been appended, therefore we have an object cycle, - // append a simple Object.toString style string. - this.getStyle().appendFieldStart(this.getStringBuffer(), fieldName); - this.appendAsObjectToString(fieldValue); - // The recursion out of - // builder.append(fieldName, fieldValue); - // below will append the field - // end marker. - } else { - try { - this.registerObject(); - this.append(fieldName, fieldValue); - } finally { - this.unregisterObject(); - } - } - } catch (IllegalAccessException ex) { - //this can't happen. Would get a Security exception instead - //throw a runtime exception in case the impossible happens. - throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage()); - } + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + String fieldName = field.getName(); + if (this.accept(field)) { + try { + // Warning: Field.get(Object) creates wrappers objects + // for primitive types. + Object fieldValue = this.getValue(field); + this.append(fieldName, fieldValue); + } catch (IllegalAccessException ex) { + //this can't happen. Would get a Security exception + // instead + //throw a runtime exception in case the impossible + // happens. + throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage()); } } - } finally { - this.unregisterObject(); } } - + /** - *

          Gets the last super class to stop appending fields for.

          + * @return Returns the excludeFieldNames. + */ + public String[] getExcludeFieldNames() { + return this.excludeFieldNames; + } + + /** + *

          + * Gets the last super class to stop appending fields for. + *

          * * @return The last super class to stop appending fields for. */ @@ -449,30 +634,55 @@ } /** - *

          Calls java.lang.reflect.Field.get(Object).

          - * - * @see java.lang.reflect.Field#get(Object) + *

          + * Calls java.lang.reflect.Field.get(Object). + *

          + * + * @param field + * The Field to query. + * @return The Object from the given Field. + * * @throws IllegalArgumentException + * see {@link java.lang.reflect.Field#get(Object)} * @throws IllegalAccessException + * see {@link java.lang.reflect.Field#get(Object)} + * + * @see java.lang.reflect.Field#get(Object) */ protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException { return field.get(this.getObject()); } /** - *

          Gets whether or not to append transient fields.

          + *

          + * Gets whether or not to append static fields. + *

          * + * @return Whether or not to append static fields. + * @since 2.1 + */ + public boolean isAppendStatics() { + return this.appendStatics; + } + + /** + *

          + * Gets whether or not to append transient fields. + *

          + * * @return Whether or not to append transient fields. */ public boolean isAppendTransients() { return this.appendTransients; } - + /** - *

          Append to the toString an Object - * array.

          - * - * @param array the array to add to the toString + *

          + * Append to the toString an Object array. + *

          + * + * @param array + * the array to add to the toString * @return this */ public ToStringBuilder reflectionAppendArray(Object array) { @@ -481,34 +691,70 @@ } /** - *

          Registers this builder's source object to avoid infinite - * loops when processing circular object references.

          + *

          + * Sets whether or not to append static fields. + *

          + * + * @param appendStatics + * Whether or not to append static fields. + * @since 2.1 */ - void registerObject() { - register(this.getObject()); + public void setAppendStatics(boolean appendStatics) { + this.appendStatics = appendStatics; } /** - *

          Sets whether or not to append transient fields.

          + *

          + * Sets whether or not to append transient fields. + *

          * - * @param appendTransients Whether or not to append transient fields. + * @param appendTransients + * Whether or not to append transient fields. */ public void setAppendTransients(boolean appendTransients) { this.appendTransients = appendTransients; } /** - *

          Sets the last super class to stop appending fields for.

          + * Sets the field names to exclude. * - * @param clazz The last super class to stop appending fields for. + * @param excludeFieldNamesParam + * The excludeFieldNames to excluding from toString or null. + * @return this */ + public ReflectionToStringBuilder setExcludeFieldNames(String[] excludeFieldNamesParam) { + if (excludeFieldNamesParam == null) { + this.excludeFieldNames = null; + } else { + this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam); + Arrays.sort(this.excludeFieldNames); + } + return this; + } + + /** + *

          + * Sets the last super class to stop appending fields for. + *

          + * + * @param clazz + * The last super class to stop appending fields for. + */ public void setUpToClass(Class clazz) { + if (clazz != null) { + Object object = getObject(); + if (object != null && clazz.isInstance(object) == false) { + throw new IllegalArgumentException("Specified class is not a superclass of the object"); + } + } this.upToClass = clazz; } /** - *

          Gets the String built by this builder.

          - * + *

          + * Gets the String built by this builder. + *

          + * * @return the built string */ public String toString() { @@ -524,12 +770,4 @@ return super.toString(); } - /** - *

          Unegisters this builder's source object to avoid infinite - * loops when processing circular object references.

          - */ - void unregisterObject() { - unregister(this.getObject()); - } - } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/StandardToStringStyle.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/StandardToStringStyle.java (.../StandardToStringStyle.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/StandardToStringStyle.java (.../StandardToStringStyle.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.builder; @@ -62,7 +25,7 @@ * store the result in a public static final variable for the rest of the * program to access.

          * - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Pete Gieser * @author Gary Gregory * @since 1.0 @@ -71,6 +34,13 @@ public class StandardToStringStyle extends ToStringStyle { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** *

          Constructor.

          */ public StandardToStringStyle() { Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringBuilder.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringBuilder.java (.../ToStringBuilder.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringBuilder.java (.../ToStringBuilder.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.builder; @@ -76,7 +39,7 @@ * public class Person { * String name; * int age; - * boolean isSmoker; + * boolean smoker; * * ... * @@ -121,7 +84,7 @@ *

          The exact format of the toString is determined by * the {@link ToStringStyle} passed into the constructor.

          * - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Gary Gregory * @author Pete Gieser * @since 1.0 @@ -130,150 +93,166 @@ public class ToStringBuilder { /** - * The default style of output to use. + * The default style of output to use, not null. */ - private static ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE; + private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE; //---------------------------------------------------------------------------- /** *

          Gets the default ToStringStyle to use.

          - * - *

          This could allow the ToStringStyle to be - * controlled for an entire application with one call.

          - * - *

          This might be used to have a verbose - * ToStringStyle during development and a compact - * ToStringStyle in production.

          * - * @return the default ToStringStyle + *

          This method gets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a ToStringStyle to the constructor instead + * of using this global default.

          + * + *

          This method can be used from multiple threads. + * Internally, a volatile variable is used to provide the guarantee + * that the latest value set using {@link #setDefaultStyle} is the value returned. + * It is strongly recommended that the default style is only changed during application startup.

          + * + *

          One reason for changing the default could be to have a verbose style during + * development and a compact style in production.

          + * + * @return the default ToStringStyle, never null */ public static ToStringStyle getDefaultStyle() { return defaultStyle; } /** - *

          Forwards to ReflectionToStringBuilder.

          + *

          Sets the default ToStringStyle to use.

          * + *

          This method sets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a ToStringStyle to the constructor instead + * of changing this global default.

          + * + *

          This method is not intended for use from multiple threads. + * Internally, a volatile variable is used to provide the guarantee + * that the latest value set is the value returned from {@link #getDefaultStyle}.

          + * + * @param style the default ToStringStyle + * @throws IllegalArgumentException if the style is null + */ + public static void setDefaultStyle(ToStringStyle style) { + if (style == null) { + throw new IllegalArgumentException("The style must not be null"); + } + defaultStyle = style; + } + + //---------------------------------------------------------------------------- + /** + *

          Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

          + * + * @param object the Object to be output + * @return the String result * @see ReflectionToStringBuilder#toString(Object) */ public static String reflectionToString(Object object) { return ReflectionToStringBuilder.toString(object); } /** - *

          Forwards to ReflectionToStringBuilder.

          + *

          Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

          * + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @return the String result * @see ReflectionToStringBuilder#toString(Object,ToStringStyle) */ public static String reflectionToString(Object object, ToStringStyle style) { return ReflectionToStringBuilder.toString(object, style); } /** - *

          Forwards to ReflectionToStringBuilder.

          + *

          Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

          * + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @param outputTransients whether to include transient fields + * @return the String result * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean) */ public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) { - return ReflectionToStringBuilder.toString(object, style, outputTransients, null); + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, null); } /** - *

          Forwards to ReflectionToStringBuilder.

          + *

          Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

          * - * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,Class) + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @param outputTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), may be null + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,boolean,Class) * @since 2.0 */ public static String reflectionToString( Object object, ToStringStyle style, boolean outputTransients, Class reflectUpToClass) { - return ReflectionToStringBuilder.toString(object, style, outputTransients, reflectUpToClass); + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass); } - /** - *

          Sets the default ToStringStyle to use.

          - * - * @param style the default ToStringStyle - * @throws IllegalArgumentException if the style is null - */ - public static void setDefaultStyle(ToStringStyle style) { - if (style == null) { - throw new IllegalArgumentException("The style must not be null"); - } - defaultStyle = style; - } + //---------------------------------------------------------------------------- /** - * Current toString buffer. + * Current toString buffer, not null. */ private final StringBuffer buffer; - /** - * The object being output. + * The object being output, may be null. */ private final Object object; - /** - * The style of output to use. + * The style of output to use, not null. */ private final ToStringStyle style; /** - *

          Constructor for ToStringBuilder.

          + *

          Constructs a builder for the specified object using the default output style.

          * - *

          This constructor outputs using the default style set with - * setDefaultStyle.

          + *

          This default style is obtained from {@link #getDefaultStyle()}.

          * - * @param object the Object to build a toString for, - * must not be null - * @throws IllegalArgumentException if the Object passed in is - * null + * @param object the Object to build a toString for, not recommended to be null */ public ToStringBuilder(Object object) { - this(object, getDefaultStyle(), null); + this(object, null, null); } /** - *

          Constructor for ToStringBuilder specifying the - * output style.

          + *

          Constructs a builder for the specified object using the a defined output style.

          * *

          If the style is null, the default style is used.

          * - * @param object the Object to build a toString for, - * must not be null - * @param style the style of the toString to create, - * may be null - * @throws IllegalArgumentException if the Object passed in is - * null + * @param object the Object to build a toString for, not recommended to be null + * @param style the style of the toString to create, null uses the default style */ public ToStringBuilder(Object object, ToStringStyle style) { this(object, style, null); } /** - *

          Constructor for ToStringBuilder.

          + *

          Constructs a builder for the specified object.

          * *

          If the style is null, the default style is used.

          * *

          If the buffer is null, a new one is created.

          * - * @param object the Object to build a toString for, - * must not be null - * @param style the style of the toString to create, - * may be null - * @param buffer the StringBuffer to populate, may be - * null - * @throws IllegalArgumentException if the Object passed in is - * null + * @param object the Object to build a toString for, not recommended to be null + * @param style the style of the toString to create, null uses the default style + * @param buffer the StringBuffer to populate, may be null */ public ToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { - super(); - if (object == null) { - throw new IllegalArgumentException("The object to create a toString for must not be null"); - } if (style == null) { style = getDefaultStyle(); } @@ -489,11 +468,11 @@ *

          Append to the toString an Object * value.

          * - * @param object the value to add to the toString + * @param obj the value to add to the toString * @return this */ - public ToStringBuilder append(Object object) { - style.append(buffer, null, object, null); + public ToStringBuilder append(Object obj) { + style.append(buffer, null, obj, null); return this; } @@ -865,11 +844,11 @@ * value.

          * * @param fieldName the field name - * @param object the value to add to the toString + * @param obj the value to add to the toString * @return this */ - public ToStringBuilder append(String fieldName, Object object) { - style.append(buffer, fieldName, object, null); + public ToStringBuilder append(String fieldName, Object obj) { + style.append(buffer, fieldName, obj, null); return this; } @@ -878,13 +857,13 @@ * value.

          * * @param fieldName the field name - * @param object the value to add to the toString + * @param obj the value to add to the toString * @param fullDetail true for detail, * false for summary info * @return this */ - public ToStringBuilder append(String fieldName, Object object, boolean fullDetail) { - style.append(buffer, fieldName, object, BooleanUtils.toBooleanObject(fullDetail)); + public ToStringBuilder append(String fieldName, Object obj, boolean fullDetail) { + style.append(buffer, fieldName, obj, BooleanUtils.toBooleanObject(fullDetail)); return this; } @@ -973,10 +952,11 @@ * {@link System#identityHashCode(java.lang.Object)}.

          * * @param object the Object whose class name and id to output + * @return this * @since 2.0 */ public ToStringBuilder appendAsObjectToString(Object object) { - ObjectUtils.appendIdentityToString(this.getStringBuffer(), object); + ObjectUtils.identityToString(this.getStringBuffer(), object); return this; } @@ -1036,6 +1016,16 @@ } /** + *

          Returns the Object being output.

          + * + * @return The object being output. + * @since 2.0 + */ + public Object getObject() { + return object; + } + + /** *

          Gets the StringBuffer being populated.

          * * @return the StringBuffer being populated @@ -1062,21 +1052,17 @@ *

          This method appends the end of data indicator, and can only be called once. * Use {@link #getStringBuffer} to get the current string state.

          * + *

          If the object is null, return the style's nullText

          + * * @return the String toString */ public String toString() { - style.appendEnd(buffer, object); - return buffer.toString(); + if (this.getObject() == null) { + this.getStringBuffer().append(this.getStyle().getNullText()); + } else { + style.appendEnd(this.getStringBuffer(), this.getObject()); + } + return this.getStringBuffer().toString(); } - /** - *

          Returns the Object being output.

          - * - * @return The object being output. - * @since 2.0 - */ - public Object getObject() { - return object; - } - } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringStyle.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringStyle.java (.../ToStringStyle.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/ToStringStyle.java (.../ToStringStyle.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,62 +1,26 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.builder; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; +import java.util.WeakHashMap; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.ObjectUtils; @@ -82,43 +46,181 @@ * output the whole array, whereas the summary method will just output * the array length.

          * - * @author Stephen Colebourne + *

          If you want to format the output of certain objects, such as dates, you + * must create a subclass and override a method. + *

          + * public class MyStyle extends ToStringStyle {
          + *   protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
          + *     if (value instanceof Date) {
          + *       value = new SimpleDateFormat("yyyy-MM-dd").format(value);
          + *     }
          + *     buffer.append(value);
          + *   }
          + * }
          + * 
          + *

          + * + * @author Apache Software Foundation * @author Gary Gregory * @author Pete Gieser + * @author Masato Tezuka * @since 1.0 * @version $Id$ */ public abstract class ToStringStyle implements Serializable { /** - * The default toString style. + * The default toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
          +     * Person@182f0db[name=John Doe,age=33,smoker=false]
          +     * 
          */ public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); + /** - * The multi line toString style. + * The multi line toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
          +     * Person@182f0db[
          +     *   name=John Doe
          +     *   age=33
          +     *   smoker=false
          +     * ]
          +     * 
          */ public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); + /** - * The no field names toString style. + * The no field names toString style. Using the Using the + * Person example from {@link ToStringBuilder}, the output + * would look like this: + * + *
          +     * Person@182f0db[John Doe,33,false]
          +     * 
          */ public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); + /** - * The simple toString style. + * The short prefix toString style. Using the Person example + * from {@link ToStringBuilder}, the output would look like this: + * + *
          +     * Person[name=John Doe,age=33,smoker=false]
          +     * 
          + * + * @since 2.1 */ + public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle(); + + /** + * The simple toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
          +     * John Doe,33,false
          +     * 
          + */ public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); /** + *

          + * A registry of objects used by reflectionToString methods + * to detect cyclical object references and avoid infinite loops. + *

          + */ + private static final ThreadLocal REGISTRY = new ThreadLocal(); + + /** + *

          + * Returns the registry of objects being traversed by the reflectionToString + * methods in the current thread. + *

          + * + * @return Set the registry of objects being traversed + */ + static Map getRegistry() { + return (Map) REGISTRY.get(); + } + + /** + *

          + * Returns true if the registry contains the given object. + * Used by the reflection methods to avoid infinite loops. + *

          + * + * @param value + * The object to lookup in the registry. + * @return boolean true if the registry contains the given + * object. + */ + static boolean isRegistered(Object value) { + Map m = getRegistry(); + return m != null && m.containsKey(value); + } + + /** + *

          + * Registers the given object. Used by the reflection methods to avoid + * infinite loops. + *

          + * + * @param value + * The object to register. + */ + static void register(Object value) { + if (value != null) { + Map m = getRegistry(); + if (m == null) { + m = new WeakHashMap(); + REGISTRY.set(m); + } + m.put(value, null); + } + } + + /** + *

          + * Unregisters the given object. + *

          + * + *

          + * Used by the reflection methods to avoid infinite loops. + *

          + * + * @param value + * The object to unregister. + */ + static void unregister(Object value) { + if (value != null) { + Map m = getRegistry(); + if (m != null) { + m.remove(value); + if (m.isEmpty()) { + REGISTRY.set(null); + } + } + } + } + + /** * Whether to use the field names, the default is true. */ private boolean useFieldNames = true; + /** * Whether to use the class name, the default is true. */ private boolean useClassName = true; + /** * Whether to use short class names, the default is false. */ private boolean useShortClassName = false; + /** * Whether to use the identity hash code, the default is true. */ @@ -128,63 +230,78 @@ * The content start '['. */ private String contentStart = "["; + /** * The content end ']'. */ private String contentEnd = "]"; + /** * The field name value separator '='. */ private String fieldNameValueSeparator = "="; + /** * Whether the field separator should be added before any other fields. */ private boolean fieldSeparatorAtStart = false; + /** * Whether the field separator should be added after any other fields. */ private boolean fieldSeparatorAtEnd = false; + /** * The field separator ','. */ private String fieldSeparator = ","; + /** * The array start '{'. */ private String arrayStart = "{"; + /** * The array separator ','. */ private String arraySeparator = ","; + /** * The detail for array content. */ private boolean arrayContentDetail = true; + /** * The array end '}'. */ private String arrayEnd = "}"; + /** * The value to use when fullDetail is null, * the default value is true. */ private boolean defaultFullDetail = true; + /** * The null text '<null>'. */ private String nullText = ""; + /** * The summary size text start '. */ private String sizeStartText = "'>'. */ private String sizeEndText = ">"; + /** * The summary object text start '<'. */ private String summaryObjectStartText = "<"; + /** * The summary object text start '>'. */ @@ -203,9 +320,10 @@ /** *

          Append to the toString the superclass toString.

          - * + *

          NOTE: It assumes that the toString has been created from the same ToStringStyle.

          + * *

          A null superToString is ignored.

          - * + * * @param buffer the StringBuffer to populate * @param superToString the super.toString() * @since 2.0 @@ -216,9 +334,10 @@ /** *

          Append to the toString another toString.

          - * + *

          NOTE: It assumes that the toString has been created from the same ToStringStyle.

          + * *

          A null toString is ignored.

          - * + * * @param buffer the StringBuffer to populate * @param toString the additional toString * @since 2.0 @@ -240,37 +359,39 @@ /** *

          Append to the toString the start of data indicator.

          - * + * * @param buffer the StringBuffer to populate - * @param object the Object to build a - * toString for, must not be null + * @param object the Object to build a toString for */ public void appendStart(StringBuffer buffer, Object object) { - appendClassName(buffer, object); - appendIdentityHashCode(buffer, object); - appendContentStart(buffer); - if (fieldSeparatorAtStart) { - appendFieldSeparator(buffer); + if (object != null) { + appendClassName(buffer, object); + appendIdentityHashCode(buffer, object); + appendContentStart(buffer); + if (fieldSeparatorAtStart) { + appendFieldSeparator(buffer); + } } } /** *

          Append to the toString the end of data indicator.

          - * + * * @param buffer the StringBuffer to populate * @param object the Object to build a - * toString for, must not be null + * toString for. */ public void appendEnd(StringBuffer buffer, Object object) { - if (fieldSeparatorAtEnd == false) { + if (this.fieldSeparatorAtEnd == false) { removeLastFieldSeparator(buffer); } appendContentEnd(buffer); + unregister(object); } /** *

          Remove the last field separator from the buffer.

          - * + * * @param buffer the StringBuffer to populate * @since 2.0 */ @@ -337,98 +458,122 @@ * @param detail output detail or not */ protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) { - if (ReflectionToStringBuilder.isRegistered(value) + if (isRegistered(value) && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { - ObjectUtils.appendIdentityToString(buffer, value); + appendCyclicObject(buffer, fieldName, value); + return; + } - } else if (value instanceof Collection) { - if (detail) { - appendDetail(buffer, fieldName, (Collection) value); - } else { - appendSummarySize(buffer, fieldName, ((Collection) value).size()); - } + register(value); - } else if (value instanceof Map) { - if (detail) { - appendDetail(buffer, fieldName, (Map) value); - } else { - appendSummarySize(buffer, fieldName, ((Map) value).size()); - } + try { + if (value instanceof Collection) { + if (detail) { + appendDetail(buffer, fieldName, (Collection) value); + } else { + appendSummarySize(buffer, fieldName, ((Collection) value).size()); + } - } else if (value instanceof long[]) { - if (detail) { - appendDetail(buffer, fieldName, (long[]) value); - } else { - appendSummary(buffer, fieldName, (long[]) value); - } + } else if (value instanceof Map) { + if (detail) { + appendDetail(buffer, fieldName, (Map) value); + } else { + appendSummarySize(buffer, fieldName, ((Map) value).size()); + } - } else if (value instanceof int[]) { - if (detail) { - appendDetail(buffer, fieldName, (int[]) value); - } else { - appendSummary(buffer, fieldName, (int[]) value); - } + } else if (value instanceof long[]) { + if (detail) { + appendDetail(buffer, fieldName, (long[]) value); + } else { + appendSummary(buffer, fieldName, (long[]) value); + } - } else if (value instanceof short[]) { - if (detail) { - appendDetail(buffer, fieldName, (short[]) value); - } else { - appendSummary(buffer, fieldName, (short[]) value); - } + } else if (value instanceof int[]) { + if (detail) { + appendDetail(buffer, fieldName, (int[]) value); + } else { + appendSummary(buffer, fieldName, (int[]) value); + } - } else if (value instanceof byte[]) { - if (detail) { - appendDetail(buffer, fieldName, (byte[]) value); - } else { - appendSummary(buffer, fieldName, (byte[]) value); - } + } else if (value instanceof short[]) { + if (detail) { + appendDetail(buffer, fieldName, (short[]) value); + } else { + appendSummary(buffer, fieldName, (short[]) value); + } - } else if (value instanceof char[]) { - if (detail) { - appendDetail(buffer, fieldName, (char[]) value); - } else { - appendSummary(buffer, fieldName, (char[]) value); - } + } else if (value instanceof byte[]) { + if (detail) { + appendDetail(buffer, fieldName, (byte[]) value); + } else { + appendSummary(buffer, fieldName, (byte[]) value); + } - } else if (value instanceof double[]) { - if (detail) { - appendDetail(buffer, fieldName, (double[]) value); - } else { - appendSummary(buffer, fieldName, (double[]) value); - } + } else if (value instanceof char[]) { + if (detail) { + appendDetail(buffer, fieldName, (char[]) value); + } else { + appendSummary(buffer, fieldName, (char[]) value); + } - } else if (value instanceof float[]) { - if (detail) { - appendDetail(buffer, fieldName, (float[]) value); - } else { - appendSummary(buffer, fieldName, (float[]) value); - } + } else if (value instanceof double[]) { + if (detail) { + appendDetail(buffer, fieldName, (double[]) value); + } else { + appendSummary(buffer, fieldName, (double[]) value); + } - } else if (value instanceof boolean[]) { - if (detail) { - appendDetail(buffer, fieldName, (boolean[]) value); - } else { - appendSummary(buffer, fieldName, (boolean[]) value); - } + } else if (value instanceof float[]) { + if (detail) { + appendDetail(buffer, fieldName, (float[]) value); + } else { + appendSummary(buffer, fieldName, (float[]) value); + } - } else if (value.getClass().isArray()) { - if (detail) { - appendDetail(buffer, fieldName, (Object[]) value); - } else { - appendSummary(buffer, fieldName, (Object[]) value); - } + } else if (value instanceof boolean[]) { + if (detail) { + appendDetail(buffer, fieldName, (boolean[]) value); + } else { + appendSummary(buffer, fieldName, (boolean[]) value); + } - } else { - if (detail) { - appendDetail(buffer, fieldName, value); + } else if (value.getClass().isArray()) { + if (detail) { + appendDetail(buffer, fieldName, (Object[]) value); + } else { + appendSummary(buffer, fieldName, (Object[]) value); + } + } else { - appendSummary(buffer, fieldName, value); + if (detail) { + appendDetail(buffer, fieldName, value); + } else { + appendSummary(buffer, fieldName, value); + } } + } finally { + unregister(value); } } /** *

          Append to the toString an Object + * value that has been detected to participate in a cycle. This + * implementation will print the standard string value of the value.

          + * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString, + * not null + * + * @since 2.2 + */ + protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) { + ObjectUtils.identityToString(buffer, value); + } + + /** + *

          Append to the toString an Object * value, printing the full detail of the Object.

          * * @param buffer the StringBuffer to populate @@ -1289,12 +1434,13 @@ /** *

          Append to the toString the class name.

          - * + * * @param buffer the StringBuffer to populate * @param object the Object whose name to output */ protected void appendClassName(StringBuffer buffer, Object object) { - if (useClassName) { + if (useClassName && object != null) { + register(object); if (useShortClassName) { buffer.append(getShortClassName(object.getClass())); } else { @@ -1305,20 +1451,21 @@ /** *

          Append the {@link System#identityHashCode(java.lang.Object)}.

          - * + * * @param buffer the StringBuffer to populate * @param object the Object whose id to output */ protected void appendIdentityHashCode(StringBuffer buffer, Object object) { - if (useIdentityHashCode) { + if (this.isUseIdentityHashCode() && object!=null) { + register(object); buffer.append('@'); buffer.append(Integer.toHexString(System.identityHashCode(object))); } } /** *

          Append to the toString the content start.

          - * + * * @param buffer the StringBuffer to populate */ protected void appendContentStart(StringBuffer buffer) { @@ -1327,7 +1474,7 @@ /** *

          Append to the toString the content end.

          - * + * * @param buffer the StringBuffer to populate */ protected void appendContentEnd(StringBuffer buffer) { @@ -1338,7 +1485,7 @@ *

          Append to the toString an indicator for null.

          * *

          The default indicator is '<null>'.

          - * + * * @param buffer the StringBuffer to populate * @param fieldName the field name, typically not used as already appended */ @@ -1348,7 +1495,7 @@ /** *

          Append to the toString the field separator.

          - * + * * @param buffer the StringBuffer to populate */ protected void appendFieldSeparator(StringBuffer buffer) { @@ -1357,7 +1504,7 @@ /** *

          Append to the toString the field start.

          - * + * * @param buffer the StringBuffer to populate * @param fieldName the field name */ @@ -1370,7 +1517,7 @@ /** *

          Append to the toString the field end.

          - * + * * @param buffer the StringBuffer to populate * @param fieldName the field name, typically not used as already appended */ @@ -1409,7 +1556,7 @@ * null indicating that it doesn't care about * the detail level. In this case the default detail level is * used.

          - * + * * @param fullDetailRequest the detail level requested * @return whether full detail is to be shown */ @@ -1628,8 +1775,8 @@ * @param arrayEnd the new array end text */ protected void setArrayEnd(String arrayEnd) { - if (arrayStart == null) { - arrayStart = ""; + if (arrayEnd == null) { + arrayEnd = ""; } this.arrayEnd = arrayEnd; } @@ -1767,9 +1914,9 @@ //--------------------------------------------------------------------- /** - *

          Gets whether the field separator should be added at the start + *

          Gets whether the field separator should be added at the start * of each buffer.

          - * + * * @return the fieldSeparatorAtStart flag * @since 2.0 */ @@ -1778,9 +1925,9 @@ } /** - *

          Sets whether the field separator should be added at the start + *

          Sets whether the field separator should be added at the start * of each buffer.

          - * + * * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag * @since 2.0 */ @@ -1791,9 +1938,9 @@ //--------------------------------------------------------------------- /** - *

          Gets whether the field separator should be added at the end + *

          Gets whether the field separator should be added at the end * of each buffer.

          - * + * * @return fieldSeparatorAtEnd flag * @since 2.0 */ @@ -1802,9 +1949,9 @@ } /** - *

          Sets whether the field separator should be added at the end + *

          Sets whether the field separator should be added at the end * of each buffer.

          - * + * * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag * @since 2.0 */ @@ -1977,11 +2124,18 @@ private static final class DefaultToStringStyle extends ToStringStyle { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** *

          Constructor.

          * *

          Use the static constant rather than instantiating.

          */ - private DefaultToStringStyle() { + DefaultToStringStyle() { super(); } @@ -2007,12 +2161,14 @@ */ private static final class NoFieldNameToStringStyle extends ToStringStyle { + private static final long serialVersionUID = 1L; + /** *

          Constructor.

          * *

          Use the static constant rather than instantiating.

          */ - private NoFieldNameToStringStyle() { + NoFieldNameToStringStyle() { super(); this.setUseFieldNames(false); } @@ -2031,6 +2187,38 @@ //---------------------------------------------------------------------------- /** + *

          ToStringStyle that prints out the short + * class name and no identity hashcode.

          + * + *

          This is an inner class rather than using + * StandardToStringStyle to ensure its immutability.

          + */ + private static final class ShortPrefixToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + *

          Constructor.

          + * + *

          Use the static constant rather than instantiating.

          + */ + ShortPrefixToStringStyle() { + super(); + this.setUseShortClassName(true); + this.setUseIdentityHashCode(false); + } + + /** + *

          Ensure Singleton after serialization.

          + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.SHORT_PREFIX_STYLE; + } + + } + + /** *

          ToStringStyle that does not print out the * classname, identity hashcode, content start or field name.

          * @@ -2039,12 +2227,14 @@ */ private static final class SimpleToStringStyle extends ToStringStyle { + private static final long serialVersionUID = 1L; + /** *

          Constructor.

          * *

          Use the static constant rather than instantiating.

          */ - private SimpleToStringStyle() { + SimpleToStringStyle() { super(); this.setUseClassName(false); this.setUseIdentityHashCode(false); @@ -2073,12 +2263,14 @@ */ private static final class MultiLineToStringStyle extends ToStringStyle { + private static final long serialVersionUID = 1L; + /** *

          Constructor.

          * *

          Use the static constant rather than instantiating.

          */ - private MultiLineToStringStyle() { + MultiLineToStringStyle() { super(); this.setContentStart("["); this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " "); @@ -2097,73 +2289,4 @@ } - //---------------------------------------------------------------------------- - -// Removed, as the XML style needs more work for escaping characters, arrays, -// collections, maps and embedded beans. -// /** -// * ToStringStyle that outputs in XML style -// */ -// private static class XMLToStringStyle extends ToStringStyle { -// -// /** -// * Constructor - use the static constant rather than instantiating. -// */ -// private XMLToStringStyle() { -// super(); -// nullText = "null"; -// sizeStartText = "size="; -// sizeEndText = ""; -// } -// -// /** -// * @see ToStringStyle#appendStart(StringBuffer, Object) -// */ -// public void appendStart(StringBuffer buffer, Object object) { -// buffer.append('<'); -// buffer.append(getShortClassName(object.getClass())); -// buffer.append(" class=\""); -// appendClassName(buffer, object); -// buffer.append("\" hashCode=\""); -// appendIdentityHashCode(buffer, object); -// buffer.append("\">"); -// buffer.append(SystemUtils.LINE_SEPARATOR); -// buffer.append(" "); -// } -// -// /** -// * @see ToStringStyle#appendFieldStart(StringBuffer, String) -// */ -// protected void appendFieldStart(StringBuffer buffer, String fieldName) { -// buffer.append('<'); -// buffer.append(fieldName); -// buffer.append('>'); -// } -// -// /** -// * @see ToStringStyle#appendFieldEnd(StringBuffer, String) -// */ -// protected void appendFieldEnd(StringBuffer buffer, String fieldName) { -// buffer.append("'); -// buffer.append(SystemUtils.LINE_SEPARATOR); -// buffer.append(" "); -// } -// -// /** -// * @see ToStringStyle#appendEnd(StringBuffer, Object) -// */ -// public void appendEnd(StringBuffer buffer, Object object) { -// int len = buffer.length(); -// if (len > 2 && buffer.charAt(len - 1) == ' ' && buffer.charAt(len - 2) == ' ') { -// buffer.setLength(len - 2); -// } -// buffer.append(""); -// } -// -// } - } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/builder/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,7 +1,28 @@ + Assists in creating consistent equals(Object), toString(), hashCode(), and compareTo(Object) methods. +@see java.lang.Object#equals(Object) +@see java.lang.Object#toString() +@see java.lang.Object#hashCode() +@see java.lang.Comparable#compareTo(Object) @since 1.0 +

          These classes are not thread-safe.

          Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/Enum.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/Enum.java (.../Enum.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/Enum.java (.../Enum.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.enum; @@ -62,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.StringUtils; @@ -172,7 +136,7 @@ * super("Plus"); * } * public int eval(int a, int b) { - * return (a + b); + * return a + b; * } * } * public static final OperationEnum MINUS = new MinusOperation(); @@ -181,7 +145,7 @@ * super("Minus"); * } * public int eval(int a, int b) { - * return (a - b); + * return a - b; * } * } * @@ -214,17 +178,72 @@ *
        *

        The code above will work on JDK 1.2. If JDK1.3 and later is used, * the subclasses may be defined as anonymous.

        + * + *

        Nested class Enums

        * + *

        Care must be taken with class loading when defining a static nested class + * for enums. The static nested class can be loaded without the surrounding outer + * class being loaded. This can result in an empty list/map/iterator being returned. + * One solution is to define a static block that references the outer class where + * the constants are defined. For example:

        + * + *
        + * public final class Outer {
        + *   public static final BWEnum BLACK = new BWEnum("Black");
        + *   public static final BWEnum WHITE = new BWEnum("White");
        + *
        + *   // static nested enum class
        + *   public static final class BWEnum extends Enum {
        + * 
        + *     static {
        + *       // explicitly reference BWEnum class to force constants to load
        + *       Object obj = Outer.BLACK;
        + *     }
        + * 
        + *     // ... other methods omitted
        + *   }
        + * }
        + * 
        + * + *

        Although the above solves the problem, it is not recommended. The best solution + * is to define the constants in the enum class, and hold references in the outer class: + * + *

        + * public final class Outer {
        + *   public static final BWEnum BLACK = BWEnum.BLACK;
        + *   public static final BWEnum WHITE = BWEnum.WHITE;
        + *
        + *   // static nested enum class
        + *   public static final class BWEnum extends Enum {
        + *     // only define constants in enum classes - private if desired
        + *     private static final BWEnum BLACK = new BWEnum("Black");
        + *     private static final BWEnum WHITE = new BWEnum("White");
        + * 
        + *     // ... other methods omitted
        + *   }
        + * }
        + * 
        + * + *

        For more details, see the 'Nested' test cases. + * + * @deprecated Replaced by {@link org.apache.commons.lang.enums.Enum org.apache.commons.lang.enums.Enum} + * and will be removed in version 3.0. All classes in this package are deprecated and repackaged to + * {@link org.apache.commons.lang.enums} since enum is a Java 1.5 keyword. + * @see org.apache.commons.lang.enums.Enum * @author Apache Avalon project - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Chris Webb * @author Mike Bowler * @since 1.0 * @version $Id$ */ public abstract class Enum implements Comparable, Serializable { - /** Lang version 1.0.1 serial compatability */ + /** + * Required for serialization support. Lang version 1.0.1 serial compatibility. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = -487045951170455942L; // After discussion, the default size for HashMaps is used, as the @@ -237,7 +256,11 @@ /** * Map, key of class name, value of Entry. */ - private static final Map cEnumClasses = new HashMap(); + private static Map cEnumClasses + // LANG-334: To avoid exposing a mutating map, + // we copy it each time we add to it. This is cheaper than + // using a synchronized map since we are almost entirely reads + = new WeakHashMap(); /** * The string representation of the Enum. @@ -279,7 +302,8 @@ /** *

        Restrictive constructor.

        */ - private Entry() { + protected Entry() { + super(); } } @@ -329,12 +353,18 @@ if (ok == false) { throw new IllegalArgumentException("getEnumClass() must return a superclass of this class"); } - - // create entry - Entry entry = (Entry) cEnumClasses.get(enumClass); - if (entry == null) { - entry = createEntry(enumClass); - cEnumClasses.put(enumClass, entry); + + Entry entry; + synchronized( Enum.class ) { // LANG-334 + // create entry + entry = (Entry) cEnumClasses.get(enumClass); + if (entry == null) { + entry = createEntry(enumClass); + Map myMap = new WeakHashMap( ); // we avoid the (Map) constructor to achieve JDK 1.2 support + myMap.putAll( cEnumClasses ); + myMap.put(enumClass, entry); + cEnumClasses = myMap; + } } if (entry.map.containsKey(name)) { throw new IllegalArgumentException("The Enum name must be unique, '" + name + "' has already been added"); @@ -354,7 +384,7 @@ if (entry == null) { return null; } - return (Enum) entry.map.get(getName()); + return entry.map.get(getName()); } //-------------------------------------------------------------------------------- @@ -366,7 +396,7 @@ * be null * @param name the name of the Enum to get, * may be null - * @return the enum object, or null if the enum does not exist + * @return the enum object, or null if the enum does not exist * @throws IllegalArgumentException if the enum class * is null */ @@ -454,6 +484,17 @@ throw new IllegalArgumentException("The Class must be a subclass of Enum"); } Entry entry = (Entry) cEnumClasses.get(enumClass); + + if (entry == null) { + try { + // LANG-76 - try to force class initialization for JDK 1.5+ + Class.forName(enumClass.getName(), true, enumClass.getClassLoader()); + entry = (Entry) cEnumClasses.get(enumClass); + } catch (Exception e) { + // Ignore + } + } + return entry; } @@ -510,6 +551,9 @@ *

        Two Enum objects are considered equal * if they have the same class names and the same names. * Identity is tested for first, so this method usually runs fast.

        + * + *

        If the parameter is in a different class loader than this instance, + * reflection is used to compare the names.

        * * @param other the other object to compare for equality * @return true if the Enums are equal @@ -520,34 +564,19 @@ } else if (other == null) { return false; } else if (other.getClass() == this.getClass()) { - // shouldn't happen, but... + // Ok to do a class cast to Enum here since the test above + // guarantee both + // classes are in the same class loader. return iName.equals(((Enum) other).iName); - } else if (((Enum) other).getEnumClass().getName().equals(getEnumClass().getName())) { - // different classloaders - try { - // try to avoid reflection - return iName.equals(((Enum) other).iName); - - } catch (ClassCastException ex) { - // use reflection - try { - Method mth = other.getClass().getMethod("getName", null); - String name = (String) mth.invoke(other, null); - return iName.equals(name); - } catch (NoSuchMethodException ex2) { - // ignore - should never happen - } catch (IllegalAccessException ex2) { - // ignore - should never happen - } catch (InvocationTargetException ex2) { - // ignore - should never happen - } + } else { + // This and other are in different class loaders, we must check indirectly + if (other.getClass().getName().equals(this.getClass().getName()) == false) { return false; } - } else { - return false; + return iName.equals( getNameInOtherClassLoader(other) ); } } - + /** *

        Returns a suitable hashCode for the enumeration.

        * @@ -563,6 +592,9 @@ *

        The default ordering is alphabetic by name, but this * can be overridden by subclasses.

        * + *

        If the parameter is in a different class loader than this instance, + * reflection is used to compare the names.

        + * * @see java.lang.Comparable#compareTo(Object) * @param other the other object to compare to * @return -ve if this is less than the other object, +ve if greater @@ -574,10 +606,38 @@ if (other == this) { return 0; } + if (other.getClass() != this.getClass()) { + if (other.getClass().getName().equals(this.getClass().getName())) { + return iName.compareTo( getNameInOtherClassLoader(other) ); + } + throw new ClassCastException( + "Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'"); + } return iName.compareTo(((Enum) other).iName); } /** + *

        Use reflection to return an objects class name.

        + * + * @param other The object to determine the class name for + * @return The class name + */ + private String getNameInOtherClassLoader(Object other) { + try { + Method mth = other.getClass().getMethod("getName", null); + String name = (String) mth.invoke(other, null); + return name; + } catch (NoSuchMethodException e) { + // ignore - should never happen + } catch (IllegalAccessException e) { + // ignore - should never happen + } catch (InvocationTargetException e) { + // ignore - should never happen + } + throw new IllegalStateException("This should not happen"); + } + + /** *

        Human readable description of this Enum item.

        * * @return String in the form type[name], for example: Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/EnumUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/EnumUtils.java (.../EnumUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/EnumUtils.java (.../EnumUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.enum; @@ -60,9 +23,13 @@ /** *

        Utility class for accessing and manipulating {@link Enum}s.

        * + * @deprecated Replaced by {@link org.apache.commons.lang.enums.EnumUtils org.apache.commons.lang.enums.EnumUtils} + * and will be removed in version 3.0. All classes in this package are deprecated and repackaged to + * {@link org.apache.commons.lang.enums} since enum is a Java 1.5 keyword. + * @see org.apache.commons.lang.enums.EnumUtils * @see Enum * @see ValuedEnum - * @author Stephen Colebourne + * @author Apache Software Foundation * @author Gary Gregory * @since 1.0 * @version $Id$ @@ -74,6 +41,7 @@ * @since 2.0 */ public EnumUtils() { + super(); } /** Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/ValuedEnum.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/ValuedEnum.java (.../ValuedEnum.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/ValuedEnum.java (.../ValuedEnum.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.enum; @@ -110,7 +73,7 @@ *

        The above class could then be used as follows:

        * *
        - * public void doSomething(JavaVersion ver) {
        + * public void doSomething(JavaVersionEnum ver) {
          *   switch (ver.getValue()) {
          *     case JAVA1_0_VALUE:
          *       // ...
        @@ -130,14 +93,22 @@
          * Unfortunately, Java restrictions require these to be coded as shown in each subclass.
          * An alternative choice is to use the {@link EnumUtils} class.

        * + * @deprecated Replaced by {@link org.apache.commons.lang.enums.ValuedEnum org.apache.commons.lang.enums.ValuedEnum} + * and will be removed in version 3.0. All classes in this package are deprecated and repackaged to + * {@link org.apache.commons.lang.enums} since enum is a Java 1.5 keyword. + * @see org.apache.commons.lang.enums.ValuedEnum * @author Apache Avalon project - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 1.0 * @version $Id$ */ public abstract class ValuedEnum extends Enum { - /** Lang version 1.0.1 serial compatability */ + /** + * Required for serialization support. Lang version 1.0.1 serial compatibility. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = -7129650521543789085L; /** @@ -174,9 +145,9 @@ } List list = Enum.getEnumList(enumClass); for (Iterator it = list.iterator(); it.hasNext();) { - ValuedEnum enum = (ValuedEnum) it.next(); - if (enum.getValue() == value) { - return enum; + ValuedEnum enumeration = (ValuedEnum) it.next(); + if (enumeration.getValue() == value) { + return enumeration; } } return null; Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enum/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,8 +1,35 @@ + -Provides an implementation of the C style 'enum' in the Java world. - +

        +Deprecated and replaced by {@link org.apache.commons.lang.enums} +and will be removed in version 3.0. +

        +

        +All classes in this package are deprecated and repackaged to {@link org.apache.commons.lang.enums} +since enum is a Java 1.5 keyword. +

        +

        +Provides an implementation of the C style enum in the Java world. +

        +

        The classic example being an RGB color enumeration. +

         public final class ColorEnum extends Enum {
             public static final ColorEnum RED = new ColorEnum("Red");
        Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/Enum.java
        ===================================================================
        diff -u
        --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/Enum.java	(revision 0)
        +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/Enum.java	(revision 6aa36ddefbf750d2b246992fee82df738a66eefa)
        @@ -0,0 +1,701 @@
        +/*
        + * Licensed to the Apache Software Foundation (ASF) under one or more
        + * contributor license agreements.  See the NOTICE file distributed with
        + * this work for additional information regarding copyright ownership.
        + * The ASF licenses this file to You under the Apache License, Version 2.0
        + * (the "License"); you may not use this file except in compliance with
        + * the License.  You may obtain a copy of the License at
        + * 
        + *      http://www.apache.org/licenses/LICENSE-2.0
        + * 
        + * Unless required by applicable law or agreed to in writing, software
        + * distributed under the License is distributed on an "AS IS" BASIS,
        + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        + * See the License for the specific language governing permissions and
        + * limitations under the License.
        + */
        +package org.apache.commons.lang.enums;
        +
        +import java.io.Serializable;
        +import java.lang.reflect.InvocationTargetException;
        +import java.lang.reflect.Method;
        +import java.util.ArrayList;
        +import java.util.Collections;
        +import java.util.HashMap;
        +import java.util.Iterator;
        +import java.util.List;
        +import java.util.Map;
        +import java.util.WeakHashMap;
        +
        +import org.apache.commons.lang.ClassUtils;
        +import org.apache.commons.lang.StringUtils;
        +
        +/**
        + * 

        Abstract superclass for type-safe enums.

        + * + *

        One feature of the C programming language lacking in Java is enumerations. The + * C implementation based on ints was poor and open to abuse. The original Java + * recommendation and most of the JDK also uses int constants. It has been recognised + * however that a more robust type-safe class-based solution can be designed. This + * class follows the basic Java type-safe enumeration pattern.

        + * + *

        NOTE: Due to the way in which Java ClassLoaders work, comparing + * Enum objects should always be done using equals(), not ==. + * The equals() method will try == first so in most cases the effect is the same.

        + * + *

        Of course, if you actually want (or don't mind) Enums in different class + * loaders being non-equal, then you can use ==.

        + * + *

        Simple Enums

        + * + *

        To use this class, it must be subclassed. For example:

        + * + *
        + * public final class ColorEnum extends Enum {
        + *   public static final ColorEnum RED = new ColorEnum("Red");
        + *   public static final ColorEnum GREEN = new ColorEnum("Green");
        + *   public static final ColorEnum BLUE = new ColorEnum("Blue");
        + *
        + *   private ColorEnum(String color) {
        + *     super(color);
        + *   }
        + * 
        + *   public static ColorEnum getEnum(String color) {
        + *     return (ColorEnum) getEnum(ColorEnum.class, color);
        + *   }
        + * 
        + *   public static Map getEnumMap() {
        + *     return getEnumMap(ColorEnum.class);
        + *   }
        + * 
        + *   public static List getEnumList() {
        + *     return getEnumList(ColorEnum.class);
        + *   }
        + * 
        + *   public static Iterator iterator() {
        + *     return iterator(ColorEnum.class);
        + *   }
        + * }
        + * 
        + * + *

        As shown, each enum has a name. This can be accessed using getName.

        + * + *

        The getEnum and iterator methods are recommended. + * Unfortunately, Java restrictions require these to be coded as shown in each subclass. + * An alternative choice is to use the {@link EnumUtils} class.

        + * + *

        Subclassed Enums

        + *

        A hierarchy of Enum classes can be built. In this case, the superclass is + * unaffected by the addition of subclasses (as per normal Java). The subclasses + * may add additional Enum constants of the type of the superclass. The + * query methods on the subclass will return all of the Enum constants from the + * superclass and subclass.

        + * + *
        + * public final class ExtraColorEnum extends ColorEnum {
        + *   // NOTE: Color enum declared above is final, change that to get this
        + *   // example to compile.
        + *   public static final ColorEnum YELLOW = new ExtraColorEnum("Yellow");
        + *
        + *   private ExtraColorEnum(String color) {
        + *     super(color);
        + *   }
        + * 
        + *   public static ColorEnum getEnum(String color) {
        + *     return (ColorEnum) getEnum(ExtraColorEnum.class, color);
        + *   }
        + * 
        + *   public static Map getEnumMap() {
        + *     return getEnumMap(ExtraColorEnum.class);
        + *   }
        + * 
        + *   public static List getEnumList() {
        + *     return getEnumList(ExtraColorEnum.class);
        + *   }
        + * 
        + *   public static Iterator iterator() {
        + *     return iterator(ExtraColorEnum.class);
        + *   }
        + * }
        + * 
        + * + *

        This example will return RED, GREEN, BLUE, YELLOW from the List and iterator + * methods in that order. The RED, GREEN and BLUE instances will be the same (==) + * as those from the superclass ColorEnum. Note that YELLOW is declared as a + * ColorEnum and not an ExtraColorEnum.

        + * + *

        Functional Enums

        + * + *

        The enums can have functionality by defining subclasses and + * overriding the getEnumClass() method:

        + * + *
        + *   public static final OperationEnum PLUS = new PlusOperation();
        + *   private static final class PlusOperation extends OperationEnum {
        + *     private PlusOperation() {
        + *       super("Plus");
        + *     }
        + *     public int eval(int a, int b) {
        + *       return a + b;
        + *     }
        + *   }
        + *   public static final OperationEnum MINUS = new MinusOperation();
        + *   private static final class MinusOperation extends OperationEnum {
        + *     private MinusOperation() {
        + *       super("Minus");
        + *     }
        + *     public int eval(int a, int b) {
        + *       return a - b;
        + *     }
        + *   }
        + *
        + *   private OperationEnum(String color) {
        + *     super(color);
        + *   }
        + * 
        + *   public final Class getEnumClass() {     // NOTE: new method!
        + *     return OperationEnum.class;
        + *   }
        + *
        + *   public abstract double eval(double a, double b);
        + * 
        + *   public static OperationEnum getEnum(String name) {
        + *     return (OperationEnum) getEnum(OperationEnum.class, name);
        + *   }
        + * 
        + *   public static Map getEnumMap() {
        + *     return getEnumMap(OperationEnum.class);
        + *   }
        + * 
        + *   public static List getEnumList() {
        + *     return getEnumList(OperationEnum.class);
        + *   }
        + * 
        + *   public static Iterator iterator() {
        + *     return iterator(OperationEnum.class);
        + *   }
        + * }
        + * 
        + *

        The code above will work on JDK 1.2. If JDK1.3 and later is used, + * the subclasses may be defined as anonymous.

        + * + *

        Nested class Enums

        + * + *

        Care must be taken with class loading when defining a static nested class + * for enums. The static nested class can be loaded without the surrounding outer + * class being loaded. This can result in an empty list/map/iterator being returned. + * One solution is to define a static block that references the outer class where + * the constants are defined. For example:

        + * + *
        + * public final class Outer {
        + *   public static final BWEnum BLACK = new BWEnum("Black");
        + *   public static final BWEnum WHITE = new BWEnum("White");
        + *
        + *   // static nested enum class
        + *   public static final class BWEnum extends Enum {
        + * 
        + *     static {
        + *       // explicitly reference BWEnum class to force constants to load
        + *       Object obj = Outer.BLACK;
        + *     }
        + * 
        + *     // ... other methods omitted
        + *   }
        + * }
        + * 
        + * + *

        Although the above solves the problem, it is not recommended. The best solution + * is to define the constants in the enum class, and hold references in the outer class: + * + *

        + * public final class Outer {
        + *   public static final BWEnum BLACK = BWEnum.BLACK;
        + *   public static final BWEnum WHITE = BWEnum.WHITE;
        + *
        + *   // static nested enum class
        + *   public static final class BWEnum extends Enum {
        + *     // only define constants in enum classes - private if desired
        + *     private static final BWEnum BLACK = new BWEnum("Black");
        + *     private static final BWEnum WHITE = new BWEnum("White");
        + * 
        + *     // ... other methods omitted
        + *   }
        + * }
        + * 
        + * + *

        For more details, see the 'Nested' test cases. + * + *

        Lang Enums and Java 5.0 Enums

        + * + *

        Enums were added to Java in Java 5.0. The main differences between Lang's + * implementation and the new official JDK implementation are:

        + *
          + *
        • The standard Enum is a not just a superclass, but is a type baked into the + * language.
        • + *
        • The standard Enum does not support extension, so the standard methods that + * are provided in the Lang enum are not available.
        • + *
        • Lang mandates a String name, whereas the standard Enum uses the class + * name as its name. getName() changes to name().
        • + *
        + * + *

        Generally people should use the standard Enum. Migrating from the Lang + * enum to the standard Enum is not as easy as it might be due to the lack of + * class inheritence in standard Enums. This means that it's not possible + * to provide a 'super-enum' which could provide the same utility methods + * that the Lang enum does. The following utility class is a Java 5.0 + * version of our EnumUtils class and provides those utility methods.

        + * + *
        + * import java.util.*;
        + * 
        + * public class EnumUtils {
        + * 
        + *   public static Enum getEnum(Class enumClass, String token) {
        + *     return Enum.valueOf(enumClass, token);
        + *   }
        + * 
        + *   public static Map getEnumMap(Class enumClass) {
        + *     HashMap map = new HashMap();
        + *     Iterator itr = EnumUtils.iterator(enumClass);
        + *     while(itr.hasNext()) {
        + *       Enum enm = (Enum) itr.next();
        + *       map.put( enm.name(), enm );
        + *     }
        + *     return map;
        + *   }
        + * 
        + *   public static List getEnumList(Class enumClass) {
        + *     return new ArrayList( EnumSet.allOf(enumClass) );
        + *   }
        + * 
        + *   public static Iterator iterator(Class enumClass) {
        + *     return EnumUtils.getEnumList(enumClass).iterator();
        + *   }
        + * }
        + * 
        + * + * @author Apache Avalon project + * @author Apache Software Foundation + * @author Chris Webb + * @author Mike Bowler + * @author Matthias Eichel + * @since 2.1 (class existed in enum package from v1.0) + * @version $Id$ + */ +public abstract class Enum implements Comparable, Serializable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -487045951170455942L; + + // After discussion, the default size for HashMaps is used, as the + // sizing algorithm changes across the JDK versions + /** + * An empty Map, as JDK1.2 didn't have an empty map. + */ + private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap(0)); + + /** + * Map, key of class name, value of Entry. + */ + private static Map cEnumClasses + // LANG-334: To avoid exposing a mutating map, + // we copy it each time we add to it. This is cheaper than + // using a synchronized map since we are almost entirely reads + = new WeakHashMap(); + + /** + * The string representation of the Enum. + */ + private final String iName; + + /** + * The hashcode representation of the Enum. + */ + private transient final int iHashCode; + + /** + * The toString representation of the Enum. + * @since 2.0 + */ + protected transient String iToString = null; + + /** + *

        Enable the iterator to retain the source code order.

        + */ + private static class Entry { + /** + * Map of Enum name to Enum. + */ + final Map map = new HashMap(); + /** + * Map of Enum name to Enum. + */ + final Map unmodifiableMap = Collections.unmodifiableMap(map); + /** + * List of Enums in source code order. + */ + final List list = new ArrayList(25); + /** + * Map of Enum name to Enum. + */ + final List unmodifiableList = Collections.unmodifiableList(list); + + /** + *

        Restrictive constructor.

        + */ + protected Entry() { + super(); + } + } + + /** + *

        Constructor to add a new named item to the enumeration.

        + * + * @param name the name of the enum object, + * must not be empty or null + * @throws IllegalArgumentException if the name is null + * or an empty string + * @throws IllegalArgumentException if the getEnumClass() method returns + * a null or invalid Class + */ + protected Enum(String name) { + super(); + init(name); + iName = name; + iHashCode = 7 + getEnumClass().hashCode() + 3 * name.hashCode(); + // cannot create toString here as subclasses may want to include other data + } + + /** + * Initializes the enumeration. + * + * @param name the enum name + * @throws IllegalArgumentException if the name is null or empty or duplicate + * @throws IllegalArgumentException if the enumClass is null or invalid + */ + private void init(String name) { + if (StringUtils.isEmpty(name)) { + throw new IllegalArgumentException("The Enum name must not be empty or null"); + } + + Class enumClass = getEnumClass(); + if (enumClass == null) { + throw new IllegalArgumentException("getEnumClass() must not be null"); + } + Class cls = getClass(); + boolean ok = false; + while (cls != null && cls != Enum.class && cls != ValuedEnum.class) { + if (cls == enumClass) { + ok = true; + break; + } + cls = cls.getSuperclass(); + } + if (ok == false) { + throw new IllegalArgumentException("getEnumClass() must return a superclass of this class"); + } + + Entry entry; + synchronized( Enum.class ) { // LANG-334 + // create entry + entry = (Entry) cEnumClasses.get(enumClass); + if (entry == null) { + entry = createEntry(enumClass); + Map myMap = new WeakHashMap( ); // we avoid the (Map) constructor to achieve JDK 1.2 support + myMap.putAll( cEnumClasses ); + myMap.put(enumClass, entry); + cEnumClasses = myMap; + } + } + if (entry.map.containsKey(name)) { + throw new IllegalArgumentException("The Enum name must be unique, '" + name + "' has already been added"); + } + entry.map.put(name, this); + entry.list.add(this); + } + + /** + *

        Handle the deserialization of the class to ensure that multiple + * copies are not wastefully created, or illegal enum types created.

        + * + * @return the resolved object + */ + protected Object readResolve() { + Entry entry = (Entry) cEnumClasses.get(getEnumClass()); + if (entry == null) { + return null; + } + return entry.map.get(getName()); + } + + //-------------------------------------------------------------------------------- + + /** + *

        Gets an Enum object by class and name.

        + * + * @param enumClass the class of the Enum to get, must not + * be null + * @param name the name of the Enum to get, + * may be null + * @return the enum object, or null if the enum does not exist + * @throws IllegalArgumentException if the enum class + * is null + */ + protected static Enum getEnum(Class enumClass, String name) { + Entry entry = getEntry(enumClass); + if (entry == null) { + return null; + } + return (Enum) entry.map.get(name); + } + + /** + *

        Gets the Map of Enum objects by + * name using the Enum class.

        + * + *

        If the requested class has no enum objects an empty + * Map is returned.

        + * + * @param enumClass the class of the Enum to get, + * must not be null + * @return the enum object Map + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass of Enum + */ + protected static Map getEnumMap(Class enumClass) { + Entry entry = getEntry(enumClass); + if (entry == null) { + return EMPTY_MAP; + } + return entry.unmodifiableMap; + } + + /** + *

        Gets the List of Enum objects using the + * Enum class.

        + * + *

        The list is in the order that the objects were created (source code order). + * If the requested class has no enum objects an empty List is + * returned.

        + * + * @param enumClass the class of the Enum to get, + * must not be null + * @return the enum object Map + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass of Enum + */ + protected static List getEnumList(Class enumClass) { + Entry entry = getEntry(enumClass); + if (entry == null) { + return Collections.EMPTY_LIST; + } + return entry.unmodifiableList; + } + + /** + *

        Gets an Iterator over the Enum objects in + * an Enum class.

        + * + *

        The Iterator is in the order that the objects were + * created (source code order). If the requested class has no enum + * objects an empty Iterator is returned.

        + * + * @param enumClass the class of the Enum to get, + * must not be null + * @return an iterator of the Enum objects + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass of Enum + */ + protected static Iterator iterator(Class enumClass) { + return Enum.getEnumList(enumClass).iterator(); + } + + //----------------------------------------------------------------------- + /** + *

        Gets an Entry from the map of Enums.

        + * + * @param enumClass the class of the Enum to get + * @return the enum entry + */ + private static Entry getEntry(Class enumClass) { + if (enumClass == null) { + throw new IllegalArgumentException("The Enum Class must not be null"); + } + if (Enum.class.isAssignableFrom(enumClass) == false) { + throw new IllegalArgumentException("The Class must be a subclass of Enum"); + } + Entry entry = (Entry) cEnumClasses.get(enumClass); + + if (entry == null) { + try { + // LANG-76 - try to force class initialization for JDK 1.5+ + Class.forName(enumClass.getName(), true, enumClass.getClassLoader()); + entry = (Entry) cEnumClasses.get(enumClass); + } catch (Exception e) { + // Ignore + } + } + + return entry; + } + + /** + *

        Creates an Entry for storing the Enums.

        + * + *

        This accounts for subclassed Enums.

        + * + * @param enumClass the class of the Enum to get + * @return the enum entry + */ + private static Entry createEntry(Class enumClass) { + Entry entry = new Entry(); + Class cls = enumClass.getSuperclass(); + while (cls != null && cls != Enum.class && cls != ValuedEnum.class) { + Entry loopEntry = (Entry) cEnumClasses.get(cls); + if (loopEntry != null) { + entry.list.addAll(loopEntry.list); + entry.map.putAll(loopEntry.map); + break; // stop here, as this will already have had superclasses added + } + cls = cls.getSuperclass(); + } + return entry; + } + + //----------------------------------------------------------------------- + /** + *

        Retrieve the name of this Enum item, set in the constructor.

        + * + * @return the String name of this Enum item + */ + public final String getName() { + return iName; + } + + /** + *

        Retrieves the Class of this Enum item, set in the constructor.

        + * + *

        This is normally the same as getClass(), but for + * advanced Enums may be different. If overridden, it must return a + * constant value.

        + * + * @return the Class of the enum + * @since 2.0 + */ + public Class getEnumClass() { + return getClass(); + } + + /** + *

        Tests for equality.

        + * + *

        Two Enum objects are considered equal + * if they have the same class names and the same names. + * Identity is tested for first, so this method usually runs fast.

        + * + *

        If the parameter is in a different class loader than this instance, + * reflection is used to compare the names.

        + * + * @param other the other object to compare for equality + * @return true if the Enums are equal + */ + public final boolean equals(Object other) { + if (other == this) { + return true; + } else if (other == null) { + return false; + } else if (other.getClass() == this.getClass()) { + // Ok to do a class cast to Enum here since the test above + // guarantee both + // classes are in the same class loader. + return iName.equals(((Enum) other).iName); + } else { + // This and other are in different class loaders, we must check indirectly + if (other.getClass().getName().equals(this.getClass().getName()) == false) { + return false; + } + return iName.equals( getNameInOtherClassLoader(other) ); + } + } + + /** + *

        Returns a suitable hashCode for the enumeration.

        + * + * @return a hashcode based on the name + */ + public final int hashCode() { + return iHashCode; + } + + /** + *

        Tests for order.

        + * + *

        The default ordering is alphabetic by name, but this + * can be overridden by subclasses.

        + * + *

        If the parameter is in a different class loader than this instance, + * reflection is used to compare the names.

        + * + * @see java.lang.Comparable#compareTo(Object) + * @param other the other object to compare to + * @return -ve if this is less than the other object, +ve if greater + * than, 0 of equal + * @throws ClassCastException if other is not an Enum + * @throws NullPointerException if other is null + */ + public int compareTo(Object other) { + if (other == this) { + return 0; + } + if (other.getClass() != this.getClass()) { + if (other.getClass().getName().equals(this.getClass().getName())) { + return iName.compareTo( getNameInOtherClassLoader(other) ); + } + throw new ClassCastException( + "Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'"); + } + return iName.compareTo(((Enum) other).iName); + } + + /** + *

        Use reflection to return an objects class name.

        + * + * @param other The object to determine the class name for + * @return The class name + */ + private String getNameInOtherClassLoader(Object other) { + try { + Method mth = other.getClass().getMethod("getName", null); + String name = (String) mth.invoke(other, null); + return name; + } catch (NoSuchMethodException e) { + // ignore - should never happen + } catch (IllegalAccessException e) { + // ignore - should never happen + } catch (InvocationTargetException e) { + // ignore - should never happen + } + throw new IllegalStateException("This should not happen"); + } + + /** + *

        Human readable description of this Enum item.

        + * + * @return String in the form type[name], for example: + * Color[Red]. Note that the package name is stripped from + * the type name. + */ + public String toString() { + if (iToString == null) { + String shortName = ClassUtils.getShortClassName(getEnumClass()); + iToString = shortName + "[" + getName() + "]"; + } + return iToString; + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/EnumUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/EnumUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/EnumUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.enums; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + *

        Utility class for accessing and manipulating {@link Enum}s.

        + * + * @see Enum + * @see ValuedEnum + * @author Apache Software Foundation + * @author Gary Gregory + * @since 2.1 (class existed in enum package from v1.0) + * @version $Id$ + */ +public class EnumUtils { + + /** + * Public constructor. This class should not normally be instantiated. + * @since 2.0 + */ + public EnumUtils() { + super(); + } + + /** + *

        Gets an Enum object by class and name.

        + * + * @param enumClass the class of the Enum to get + * @param name the name of the Enum to get, may be null + * @return the enum object + * @throws IllegalArgumentException if the enum class is null + */ + public static Enum getEnum(Class enumClass, String name) { + return Enum.getEnum(enumClass, name); + } + + /** + *

        Gets a ValuedEnum object by class and value.

        + * + * @param enumClass the class of the Enum to get + * @param value the value of the Enum to get + * @return the enum object, or null if the enum does not exist + * @throws IllegalArgumentException if the enum class is null + */ + public static ValuedEnum getEnum(Class enumClass, int value) { + return (ValuedEnum) ValuedEnum.getEnum(enumClass, value); + } + + /** + *

        Gets the Map of Enum objects by + * name using the Enum class.

        + * + *

        If the requested class has no enum objects an empty + * Map is returned. The Map is unmodifiable.

        + * + * @param enumClass the class of the Enum to get + * @return the enum object Map + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass + * of Enum + */ + public static Map getEnumMap(Class enumClass) { + return Enum.getEnumMap(enumClass); + } + + /** + *

        Gets the List of Enum objects using + * the Enum class.

        + * + *

        The list is in the order that the objects were created + * (source code order).

        + * + *

        If the requested class has no enum objects an empty + * List is returned. The List is unmodifiable.

        + * + * @param enumClass the class of the Enum to get + * @return the enum object Map + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass + * of Enum + */ + public static List getEnumList(Class enumClass) { + return Enum.getEnumList(enumClass); + } + + /** + *

        Gets an Iterator over the Enum objects + * in an Enum class.

        + * + *

        The iterator is in the order that the objects were created + * (source code order).

        + * + *

        If the requested class has no enum objects an empty + * Iterator is returned. The Iterator + * is unmodifiable.

        + * + * @param enumClass the class of the Enum to get + * @return an Iterator of the Enum objects + * @throws IllegalArgumentException if the enum class is null + * @throws IllegalArgumentException if the enum class is not a subclass of Enum + */ + public static Iterator iterator(Class enumClass) { + return Enum.getEnumList(enumClass).iterator(); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/ValuedEnum.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/ValuedEnum.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/ValuedEnum.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.enums; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.ClassUtils; + +/** + *

        Abstract superclass for type-safe enums with integer values suitable + * for use in switch statements.

        + * + *

        NOTE:Due to the way in which Java ClassLoaders work, comparing + * Enum objects should always be done using the equals() method, + * not ==. The equals() method will try == first so + * in most cases the effect is the same.

        + * + *

        To use this class, it must be subclassed. For example:

        + * + *
        + * public final class JavaVersionEnum extends ValuedEnum {
        + *   //standard enums for version of JVM
        + *   public static final int  JAVA1_0_VALUE  = 100;
        + *   public static final int  JAVA1_1_VALUE  = 110;
        + *   public static final int  JAVA1_2_VALUE  = 120;
        + *   public static final int  JAVA1_3_VALUE  = 130;
        + *   public static final JavaVersionEnum  JAVA1_0  = new JavaVersionEnum( "Java 1.0", JAVA1_0_VALUE );
        + *   public static final JavaVersionEnum  JAVA1_1  = new JavaVersionEnum( "Java 1.1", JAVA1_1_VALUE );
        + *   public static final JavaVersionEnum  JAVA1_2  = new JavaVersionEnum( "Java 1.2", JAVA1_2_VALUE );
        + *   public static final JavaVersionEnum  JAVA1_3  = new JavaVersionEnum( "Java 1.3", JAVA1_3_VALUE );
        + *
        + *   private JavaVersionEnum(String name, int value) {
        + *     super( name, value );
        + *   }
        + * 
        + *   public static JavaVersionEnum getEnum(String javaVersion) {
        + *     return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
        + *   }
        + * 
        + *   public static JavaVersionEnum getEnum(int javaVersion) {
        + *     return (JavaVersionEnum) getEnum(JavaVersionEnum.class, javaVersion);
        + *   }
        + * 
        + *   public static Map getEnumMap() {
        + *     return getEnumMap(JavaVersionEnum.class);
        + *   }
        + * 
        + *   public static List getEnumList() {
        + *     return getEnumList(JavaVersionEnum.class);
        + *   }
        + * 
        + *   public static Iterator iterator() {
        + *     return iterator(JavaVersionEnum.class);
        + *   }
        + * }
        + * 
        + * + *

        NOTE:These are declared final, so compilers may + * inline the code. Ensure you recompile everything when using final.

        + * + *

        The above class could then be used as follows:

        + * + *
        + * public void doSomething(JavaVersionEnum ver) {
        + *   switch (ver.getValue()) {
        + *     case JAVA1_0_VALUE:
        + *       // ...
        + *       break;
        + *     case JAVA1_1_VALUE:
        + *       // ...
        + *       break;
        + *     //...
        + *   }
        + * }
        + * 
        + * + *

        As shown, each enum has a name and a value. These can be accessed using + * getName and getValue.

        + * + *

        NOTE: Because the switch is ultimately sitting on top of an + * int, the example above is not type-safe. That is, there is nothing that + * checks that JAVA1_0_VALUE is a legal constant for JavaVersionEnum.

        + * + *

        The getEnum and iterator methods are recommended. + * Unfortunately, Java restrictions require these to be coded as shown in each subclass. + * An alternative choice is to use the {@link EnumUtils} class.

        + * + * @author Apache Avalon project + * @author Apache Software Foundation + * @since 2.1 (class existed in enum package from v1.0) + * @version $Id$ + */ +public abstract class ValuedEnum extends Enum { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -7129650521543789085L; + + /** + * The value contained in enum. + */ + private final int iValue; + + /** + * Constructor for enum item. + * + * @param name the name of enum item + * @param value the value of enum item + */ + protected ValuedEnum(String name, int value) { + super(name); + iValue = value; + } + + /** + *

        Gets an Enum object by class and value.

        + * + *

        This method loops through the list of Enum, + * thus if there are many Enums this will be + * slow.

        + * + * @param enumClass the class of the Enum to get + * @param value the value of the Enum to get + * @return the enum object, or null if the enum does not exist + * @throws IllegalArgumentException if the enum class is null + */ + protected static Enum getEnum(Class enumClass, int value) { + if (enumClass == null) { + throw new IllegalArgumentException("The Enum Class must not be null"); + } + List list = Enum.getEnumList(enumClass); + for (Iterator it = list.iterator(); it.hasNext();) { + ValuedEnum enumeration = (ValuedEnum) it.next(); + if (enumeration.getValue() == value) { + return enumeration; + } + } + return null; + } + + /** + *

        Get value of enum item.

        + * + * @return the enum item's value. + */ + public final int getValue() { + return iValue; + } + + /** + *

        Tests for order.

        + * + *

        The default ordering is numeric by value, but this + * can be overridden by subclasses.

        + * + *

        NOTE: From v2.2 the enums must be of the same type. + * If the parameter is in a different class loader than this instance, + * reflection is used to compare the values.

        + * + * @see java.lang.Comparable#compareTo(Object) + * @param other the other object to compare to + * @return -ve if this is less than the other object, +ve if greater than, + * 0 of equal + * @throws ClassCastException if other is not an Enum + * @throws NullPointerException if other is null + */ + public int compareTo(Object other) { + if (other == this) { + return 0; + } + if (other.getClass() != this.getClass()) { + if (other.getClass().getName().equals(this.getClass().getName())) { + return iValue - getValueInOtherClassLoader(other); + } + throw new ClassCastException( + "Different enum class '" + ClassUtils.getShortClassName(other.getClass()) + "'"); + } + return iValue - ((ValuedEnum) other).iValue; + } + + /** + *

        Use reflection to return an objects value.

        + * + * @param other the object to determine the value for + * @return the value + */ + private int getValueInOtherClassLoader(Object other) { + try { + Method mth = other.getClass().getMethod("getValue", null); + Integer value = (Integer) mth.invoke(other, null); + return value.intValue(); + } catch (NoSuchMethodException e) { + // ignore - should never happen + } catch (IllegalAccessException e) { + // ignore - should never happen + } catch (InvocationTargetException e) { + // ignore - should never happen + } + throw new IllegalStateException("This should not happen"); + } + + /** + *

        Human readable description of this Enum item.

        + * + * @return String in the form type[name=value], for example: + * JavaVersion[Java 1.0=100]. Note that the package name is + * stripped from the type name. + */ + public String toString() { + if (iToString == null) { + String shortName = ClassUtils.getShortClassName(getEnumClass()); + iToString = shortName + "[" + getName() + "=" + getValue() + "]"; + } + return iToString; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/package.html =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/package.html (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/enums/package.html (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,54 @@ + + + +

        +Provides an implementation of the C style enum in the Java world. +

        +

        +The classic example being an RGB color enumeration. +

        +
        +public final class ColorEnum extends Enum {
        +    public static final ColorEnum RED = new ColorEnum("Red");
        +    public static final ColorEnum GREEN = new ColorEnum("Green");
        +    public static final ColorEnum BLUE = new ColorEnum("Blue");
        +
        +    private ColorEnum(String color) {
        +        super(color);
        +    }
        +
        +    public static ColorEnum getEnum(String color) {
        +        return (ColorEnum) getEnum(ColorEnum.class, color);
        +    }
        +
        +    public static Map getEnumMap() {
        +        return getEnumMap(ColorEnum.class);
        +    }
        +
        +    public static List getEnumList() {
        +        return getEnumList(ColorEnum.class);
        +    }
        +
        +    public static Iterator iterator() {
        +        return iterator(ColorEnum.class);
        +    }
        +}
        +
        +@since 2.1 + + Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/CloneFailedException.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/CloneFailedException.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/CloneFailedException.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.exception; + +/** + * Exception thrown when a clone cannot be created. In contrast to + * {@link CloneNotSupportedException} this is a {@link RuntimeException}. + * + * @author Apache Software Foundation + * @since 2.6 + */ +public class CloneFailedException extends NestableRuntimeException { + // ~ Static fields/initializers --------------------------------------------- + + private static final long serialVersionUID = 20091223L; + + // ~ Constructors ----------------------------------------------------------- + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @since upcoming + */ + public CloneFailedException(final String message) { + super(message); + } + + /** + * Constructs a CloneFailedException. + * + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final String message, final Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/ExceptionUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/ExceptionUtils.java (.../ExceptionUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/ExceptionUtils.java (.../ExceptionUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -62,21 +25,22 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.ClassUtils; +import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; /** *

        Provides utilities for manipulating and examining * Throwable objects.

        * - * @author Daniel Rall + * @author Apache Software Foundation + * @author Daniel L. Rall * @author Dmitri Plotnikov - * @author Stephen Colebourne * @author Gary Gregory * @author Pete Gieser * @since 1.0 @@ -92,6 +56,9 @@ */ static final String WRAPPED_MARKER = " [wrapped] "; + // Lock object for CAUSE_METHOD_NAMES + private static final Object CAUSE_METHOD_NAMES_LOCK = new Object(); + /** *

        The names of methods commonly used to access a wrapped exception.

        */ @@ -103,28 +70,47 @@ "getSourceException", "getRootCause", "getCausedByException", - "getNested" + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", }; /** - *

        The Method object for JDK1.4 getCause.

        + *

        The Method object for Java 1.4 getCause.

        */ private static final Method THROWABLE_CAUSE_METHOD; + + /** + *

        The Method object for Java 1.4 initCause.

        + */ + private static final Method THROWABLE_INITCAUSE_METHOD; + static { - Method getCauseMethod; + Method causeMethod; try { - getCauseMethod = Throwable.class.getMethod("getCause", null); + causeMethod = Throwable.class.getMethod("getCause", null); } catch (Exception e) { - getCauseMethod = null; + causeMethod = null; } - THROWABLE_CAUSE_METHOD = getCauseMethod; + THROWABLE_CAUSE_METHOD = causeMethod; + try { + causeMethod = Throwable.class.getMethod("initCause", new Class[]{Throwable.class}); + } catch (Exception e) { + causeMethod = null; + } + THROWABLE_INITCAUSE_METHOD = causeMethod; } /** - *

        Public constructor allows an instance of ExceptionUtils - * to be created, although that is not normally necessary.

        + *

        + * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

        */ public ExceptionUtils() { + super(); } //----------------------------------------------------------------------- @@ -137,16 +123,132 @@ * @since 2.0 */ public static void addCauseMethodName(String methodName) { + if (StringUtils.isNotEmpty(methodName) && !isCauseMethodName(methodName)) { + List list = getCauseMethodNameList(); + if (list.add(methodName)) { + synchronized(CAUSE_METHOD_NAMES_LOCK) { + CAUSE_METHOD_NAMES = toArray(list); + } + } + } + } + + /** + *

        Removes from the list of method names used in the search for Throwable + * objects.

        + * + * @param methodName the methodName to remove from the list, null + * and empty strings are ignored + * @since 2.1 + */ + public static void removeCauseMethodName(String methodName) { if (StringUtils.isNotEmpty(methodName)) { - List list = new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); - list.add(methodName); - CAUSE_METHOD_NAMES = (String[]) list.toArray(new String[list.size()]); + List list = getCauseMethodNameList(); + if (list.remove(methodName)) { + synchronized(CAUSE_METHOD_NAMES_LOCK) { + CAUSE_METHOD_NAMES = toArray(list); + } + } } } /** - *

        Introspects the Throwable to obtain the cause.

        + *

        Sets the cause of a Throwable using introspection, allowing + * source code compatibility between pre-1.4 and post-1.4 Java releases.

        + * + *

        The typical use of this method is inside a constructor as in + * the following example:

        + * + *
        +     * import org.apache.commons.lang.exception.ExceptionUtils;
        +     *  
        +     * public class MyException extends Exception {
        +     *  
        +     *    public MyException(String msg) {
        +     *       super(msg);
        +     *    }
        +     *
        +     *    public MyException(String msg, Throwable cause) {
        +     *       super(msg);
        +     *       ExceptionUtils.setCause(this, cause);
        +     *    }
        +     * }
        +     * 
        + * + * @param target the target Throwable + * @param cause the Throwable to set in the target + * @return a true if the target has been modified + * @since 2.2 + */ + public static boolean setCause(Throwable target, Throwable cause) { + if (target == null) { + throw new NullArgumentException("target"); + } + Object[] causeArgs = new Object[]{cause}; + boolean modifiedTarget = false; + if (THROWABLE_INITCAUSE_METHOD != null) { + try { + THROWABLE_INITCAUSE_METHOD.invoke(target, causeArgs); + modifiedTarget = true; + } catch (IllegalAccessException ignored) { + // Exception ignored. + } catch (InvocationTargetException ignored) { + // Exception ignored. + } + } + try { + Method setCauseMethod = target.getClass().getMethod("setCause", new Class[]{Throwable.class}); + setCauseMethod.invoke(target, causeArgs); + modifiedTarget = true; + } catch (NoSuchMethodException ignored) { + // Exception ignored. + } catch (IllegalAccessException ignored) { + // Exception ignored. + } catch (InvocationTargetException ignored) { + // Exception ignored. + } + return modifiedTarget; + } + + /** + * Returns the given list as a String[]. + * @param list a list to transform. + * @return the given list as a String[]. + */ + private static String[] toArray(List list) { + return (String[]) list.toArray(new String[list.size()]); + } + + /** + * Returns {@link #CAUSE_METHOD_NAMES} as a List. + * + * @return {@link #CAUSE_METHOD_NAMES} as a List. + */ + private static ArrayList getCauseMethodNameList() { + synchronized(CAUSE_METHOD_NAMES_LOCK) { + return new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); + } + } + + /** + *

        Tests if the list of method names used in the search for Throwable + * objects include the given name.

        * + * @param methodName the methodName to search in the list. + * @return if the list of method names used in the search for Throwable + * objects include the given name. + * @since 2.1 + */ + public static boolean isCauseMethodName(String methodName) { + synchronized(CAUSE_METHOD_NAMES_LOCK) { + return ArrayUtils.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; + } + } + + //----------------------------------------------------------------------- + /** + *

        Introspects the Throwable to obtain the cause.

        + * *

        The method searches for methods with specific names that return a * Throwable object. This will pick up most wrapping exceptions, * including those from JDK 1.4, and @@ -167,33 +269,37 @@ * *

        In the absence of any such method, the object is inspected for a * detail field assignable to a Throwable.

        - * + * *

        If none of the above is found, returns null.

        * * @param throwable the throwable to introspect for a cause, may be null * @return the cause of the Throwable, * null if none found or null throwable input + * @since 1.0 */ public static Throwable getCause(Throwable throwable) { - return getCause(throwable, CAUSE_METHOD_NAMES); + synchronized(CAUSE_METHOD_NAMES_LOCK) { + return getCause(throwable, CAUSE_METHOD_NAMES); + } } /** *

        Introspects the Throwable to obtain the cause.

        - * + * *
          *
        1. Try known exception types.
        2. *
        3. Try the supplied array of method names.
        4. *
        5. Try the field 'detail'.
        6. *
        - * + * *

        A null set of method names means use the default set. * A null in the set of method names will be ignored.

        * * @param throwable the throwable to introspect for a cause, may be null * @param methodNames the method names, null treated as default set * @return the cause of the Throwable, * null if none found or null throwable input + * @since 1.0 */ public static Throwable getCause(Throwable throwable, String[] methodNames) { if (throwable == null) { @@ -202,7 +308,9 @@ Throwable cause = getCauseUsingWellKnownTypes(throwable); if (cause == null) { if (methodNames == null) { - methodNames = CAUSE_METHOD_NAMES; + synchronized(CAUSE_METHOD_NAMES_LOCK) { + methodNames = CAUSE_METHOD_NAMES; + } } for (int i = 0; i < methodNames.length; i++) { String methodName = methodNames[i]; @@ -223,24 +331,24 @@ /** *

        Introspects the Throwable to obtain the root cause.

        - * + * *

        This method walks through the exception chain to the last element, * "root" of the tree, using {@link #getCause(Throwable)}, and * returns that exception.

        * + *

        From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

        + * * @param throwable the throwable to get the root cause for, may be null * @return the root cause of the Throwable, * null if none found or null throwable input */ public static Throwable getRootCause(Throwable throwable) { - Throwable cause = getCause(throwable); - if (cause != null) { - throwable = cause; - while ((throwable = getCause(throwable)) != null) { - cause = throwable; - } - } - return cause; + List list = getThrowableList(throwable); + return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1)); } /** @@ -267,7 +375,7 @@ /** *

        Finds a Throwable by method name.

        - * + * * @param throwable the exception to examine * @param methodName the name of the method to find and invoke * @return the wrapped exception, or null if not found @@ -277,23 +385,28 @@ try { method = throwable.getClass().getMethod(methodName, null); } catch (NoSuchMethodException ignored) { + // exception ignored } catch (SecurityException ignored) { + // exception ignored } if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { try { return (Throwable) method.invoke(throwable, ArrayUtils.EMPTY_OBJECT_ARRAY); } catch (IllegalAccessException ignored) { + // exception ignored } catch (IllegalArgumentException ignored) { + // exception ignored } catch (InvocationTargetException ignored) { + // exception ignored } } return null; } /** *

        Finds a Throwable by field name.

        - * + * * @param throwable the exception to examine * @param fieldName the name of the attribute to examine * @return the wrapped exception, or null if not found @@ -303,14 +416,18 @@ try { field = throwable.getClass().getField(fieldName); } catch (NoSuchFieldException ignored) { + // exception ignored } catch (SecurityException ignored) { + // exception ignored } if (field != null && Throwable.class.isAssignableFrom(field.getType())) { try { return (Throwable) field.get(throwable); } catch (IllegalAccessException ignored) { + // exception ignored } catch (IllegalArgumentException ignored) { + // exception ignored } } return null; @@ -319,19 +436,19 @@ //----------------------------------------------------------------------- /** *

        Checks if the Throwable class has a getCause method.

        - * + * *

        This is true for JDK 1.4 and above.

        - * + * * @return true if Throwable is nestable * @since 2.0 */ public static boolean isThrowableNested() { - return (THROWABLE_CAUSE_METHOD != null); + return THROWABLE_CAUSE_METHOD != null; } /** *

        Checks whether this Throwable class can store a cause.

        - * + * *

        This method does not check whether it actually does store a cause.

        * * @param throwable the Throwable to examine, may be null @@ -354,14 +471,18 @@ } Class cls = throwable.getClass(); - for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) { - try { - Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null); - if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { - return true; + synchronized(CAUSE_METHOD_NAMES_LOCK) { + for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) { + try { + Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null); + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + return true; + } + } catch (NoSuchMethodException ignored) { + // exception ignored + } catch (SecurityException ignored) { + // exception ignored } - } catch (NoSuchMethodException ignored) { - } catch (SecurityException ignored) { } } @@ -371,7 +492,9 @@ return true; } } catch (NoSuchFieldException ignored) { + // exception ignored } catch (SecurityException ignored) { + // exception ignored } return false; @@ -381,81 +504,174 @@ /** *

        Counts the number of Throwable objects in the * exception chain.

        - * + * *

        A throwable without cause will return 1. * A throwable with one cause will return 2 and so on. * A null throwable will return 0.

        - * + * + *

        From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

        + * * @param throwable the throwable to inspect, may be null * @return the count of throwables, zero if null input */ public static int getThrowableCount(Throwable throwable) { - int count = 0; - while (throwable != null) { - count++; - throwable = ExceptionUtils.getCause(throwable); - } - return count; + return getThrowableList(throwable).size(); } /** *

        Returns the list of Throwable objects in the * exception chain.

        - * + * *

        A throwable without cause will return an array containing * one element - the input throwable. * A throwable with one cause will return an array containing * two elements. - the input throwable and the cause throwable. - * A null throwable will return an array size zero.

        + * A null throwable will return an array of size zero.

        * + *

        From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

        + * + * @see #getThrowableList(Throwable) * @param throwable the throwable to inspect, may be null * @return the array of throwables, never null */ public static Throwable[] getThrowables(Throwable throwable) { + List list = getThrowableList(throwable); + return (Throwable[]) list.toArray(new Throwable[list.size()]); + } + + /** + *

        Returns the list of Throwable objects in the + * exception chain.

        + * + *

        A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

        + * + *

        This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

        + * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { List list = new ArrayList(); - while (throwable != null) { + while (throwable != null && list.contains(throwable) == false) { list.add(throwable); throwable = ExceptionUtils.getCause(throwable); } - return (Throwable[]) list.toArray(new Throwable[list.size()]); + return list; } //----------------------------------------------------------------------- /** *

        Returns the (zero based) index of the first Throwable - * that matches the specified type in the exception chain.

        - * + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

        + * *

        A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1.

        * * @param throwable the throwable to inspect, may be null - * @param type the type to search for + * @param clazz the class to search for, subclasses do not match, null returns -1 * @return the index into the throwable chain, -1 if no match or null input */ - public static int indexOfThrowable(Throwable throwable, Class type) { - return indexOfThrowable(throwable, type, 0); + public static int indexOfThrowable(Throwable throwable, Class clazz) { + return indexOf(throwable, clazz, 0, false); } /** *

        Returns the (zero based) index of the first Throwable * that matches the specified type in the exception chain from - * a specified index.

        - * + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

        + * *

        A null throwable returns -1. * A null type returns -1. * No match in the chain returns -1. * A negative start index is treated as zero. * A start index greater than the number of throwables returns -1.

        * * @param throwable the throwable to inspect, may be null - * @param type the type to search for + * @param clazz the class to search for, subclasses do not match, null returns -1 * @param fromIndex the (zero based) index of the starting position, * negative treated as zero, larger than chain size returns -1 * @return the index into the throwable chain, -1 if no match or null input */ - public static int indexOfThrowable(Throwable throwable, Class type, int fromIndex) { - if (throwable == null) { + public static int indexOfThrowable(Throwable throwable, Class clazz, int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

        Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

        + * + *

        A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

        + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

        Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

        + * + *

        A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

        + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class type, int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

        Worker method for the indexOfType methods.

        + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested withing the specified throwable + */ + private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) { + if (throwable == null || type == null) { return -1; } if (fromIndex < 0) { @@ -465,10 +681,18 @@ if (fromIndex >= throwables.length) { return -1; } - for (int i = fromIndex; i < throwables.length; i++) { - if (throwables[i].getClass().equals(type)) { - return i; + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } } return -1; } @@ -477,15 +701,18 @@ /** *

        Prints a compact stack trace for the root cause of a throwable * to System.err.

        - * + * *

        The compact stack trace starts with the root cause and prints * stack frames up to the place where it was caught and wrapped. * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

        * + *

        The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

        + * *

        The method is equivalent to printStackTrace for throwables * that don't have nested causes.

        - * + * * @param throwable the throwable to output * @since 2.0 */ @@ -501,9 +728,12 @@ * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

        * + *

        The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

        + * *

        The method is equivalent to printStackTrace for throwables * that don't have nested causes.

        - * + * * @param throwable the throwable to output, may be null * @param stream the stream to output to, may not be null * @throws IllegalArgumentException if the stream is null @@ -531,9 +761,12 @@ * Then it prints the wrapped exception and continues with stack frames * until the wrapper exception is caught and wrapped again, etc.

        * + *

        The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

        + * *

        The method is equivalent to printStackTrace for throwables * that don't have nested causes.

        - * + * * @param throwable the throwable to output, may be null * @param writer the writer to output to, may not be null * @throws IllegalArgumentException if the writer is null @@ -557,7 +790,12 @@ /** *

        Creates a compact stack trace for the root cause of the supplied * Throwable.

        - * + * + *

        The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

        + * * @param throwable the throwable to examine, may be null * @return an array of stack trace frames, never null * @since 2.0 @@ -590,7 +828,7 @@ /** *

        Removes common frames from the cause trace given the two stack traces.

        - * + * * @param causeFrames stack trace of a cause throwable * @param wrapperFrames stack trace of a wrapper throwable * @throws IllegalArgumentException if either argument is null @@ -617,22 +855,11 @@ //----------------------------------------------------------------------- /** - *

        Gets the stack trace from a Throwable as a String.

        - * - * @param throwable the Throwable to be examined - * @return the stack trace as generated by the exception's - * printStackTrace(PrintWriter) method - */ - public static String getStackTrace(Throwable throwable) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw, true); - throwable.printStackTrace(pw); - return sw.getBuffer().toString(); - } - - /** *

        A way to get the entire nested stack-trace of an throwable.

        * + *

        The result of this method is highly dependent on the JDK version + * and whether the exceptions override printStackTrace or not.

        + * * @param throwable the Throwable to be examined * @return the nested stack trace, with the root cause first * @since 2.0 @@ -652,11 +879,35 @@ //----------------------------------------------------------------------- /** + *

        Gets the stack trace from a Throwable as a String.

        + * + *

        The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

        + * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** *

        Captures the stack trace associated with the specified * Throwable object, decomposing it into a list of * stack frames.

        * - * @param throwable the Throwable to exaamine, may be null + *

        The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

        + * + * @param throwable the Throwable to examine, may be null * @return an array of strings describing each stack frame, never null */ public static String[] getStackFrames(Throwable throwable) { @@ -666,25 +917,33 @@ return getStackFrames(getStackTrace(throwable)); } + //----------------------------------------------------------------------- /** + *

        Returns an array where each element is a line from the argument.

        + * + *

        The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

        + * *

        Functionality shared between the * getStackFrames(Throwable) methods of this and the - * {@link org.apache.commons.lang.exception.NestableDelegate} - * classes.

        + * {@link org.apache.commons.lang.exception.NestableDelegate} classes.

        + * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument */ static String[] getStackFrames(String stackTrace) { String linebreak = SystemUtils.LINE_SEPARATOR; StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); - List list = new LinkedList(); + List list = new ArrayList(); while (frames.hasMoreTokens()) { list.add(frames.nextToken()); } - return (String[]) list.toArray(new String[list.size()]); + return toArray(list); } /** *

        Produces a List of stack frames - the message - * is not included.

        + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

        * *

        This works in most cases - it will only fail if the exception * message contains a line that starts with: @@ -697,7 +956,7 @@ String stackTrace = getStackTrace(t); String linebreak = SystemUtils.LINE_SEPARATOR; StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); - List list = new LinkedList(); + List list = new ArrayList(); boolean traceStarted = false; while (frames.hasMoreTokens()) { String token = frames.nextToken(); @@ -712,5 +971,42 @@ } return list; } - + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

        + * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(Throwable th) { + if (th == null) { + return ""; + } + String clsName = ClassUtils.getShortClassName(th, null); + String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

        + * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = (root == null ? th : root); + return getMessage(root); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/Nestable.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/Nestable.java (.../Nestable.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/Nestable.java (.../Nestable.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -61,7 +24,7 @@ * extensions which would like to be able to nest root exceptions * inside themselves. * - * @author Daniel Rall + * @author Daniel L. Rall * @author Kasper Nielsen * @author Steven Caswell * @author Pete Gieser @@ -88,7 +51,7 @@ /** * Returns the error message of the Throwable in the chain - * of Throwables at the specified index, numbererd from 0. + * of Throwables at the specified index, numbered from 0. * * @param index the index of the Throwable in the chain of * Throwables @@ -114,7 +77,7 @@ /** * Returns the Throwable in the chain of - * Throwables at the specified index, numbererd from 0. + * Throwables at the specified index, numbered from 0. * * @param index the index, numbered from 0, of the Throwable in * the chain of Throwables @@ -144,22 +107,34 @@ /** * Returns the index, numbered from 0, of the first occurrence of the - * specified type in the chain of Throwables, or -1 if the - * specified type is not found in the chain. + * specified type, or a subclass, in the chain of Throwables. + * The method returns -1 if the specified type is not found in the chain. + *

        + * NOTE: From v2.1, we have clarified the Nestable interface + * such that this method matches subclasses. + * If you want to NOT match subclasses, please use + * {@link ExceptionUtils#indexOfThrowable(Throwable, Class)} + * (which is avaiable in all versions of lang). * - * @param type Class to be found + * @param type the type to find, subclasses match, null returns -1 * @return index of the first occurrence of the type in the chain, or -1 if * the type is not found */ public int indexOfThrowable(Class type); /** * Returns the index, numbered from 0, of the first Throwable - * that matches the specified type in the chain of Throwables - * with an index greater than or equal to the specified index, or -1 if - * the type is not found. + * that matches the specified type, or a subclass, in the chain of Throwables + * with an index greater than or equal to the specified index. + * The method returns -1 if the specified type is not found in the chain. + *

        + * NOTE: From v2.1, we have clarified the Nestable interface + * such that this method matches subclasses. + * If you want to NOT match subclasses, please use + * {@link ExceptionUtils#indexOfThrowable(Throwable, Class, int)} + * (which is avaiable in all versions of lang). * - * @param type Class to be found + * @param type the type to find, subclasses match, null returns -1 * @param fromIndex the index, numbered from 0, of the starting position in * the chain to be searched * @return index of the first occurrence of the type in the chain, or -1 if @@ -181,7 +156,7 @@ /** * Prints the stack trace of this exception to the specified print - * stream. Includes inforamation from the exception, if any, + * stream. Includes information from the exception, if any, * which caused this exception. * * @param out PrintStream to use for output. @@ -190,8 +165,8 @@ /** * Prints the stack trace for this exception only--root cause not - * included--using the provided writer. Used by {@link - * org.apache.commons.lang.exception.NestableDelegate} to write + * included--using the provided writer. Used by + * {@link org.apache.commons.lang.exception.NestableDelegate} to write * individual stack traces to a buffer. The implementation of * this method should call * super.printStackTrace(out); in most cases. Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableDelegate.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableDelegate.java (.../NestableDelegate.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableDelegate.java (.../NestableDelegate.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -72,18 +35,25 @@ * {@link org.apache.commons.lang.exception.NestableRuntimeException NestableRuntimeException}. *

        * + * @author Apache Software Foundation * @author Rafal Krzewski - * @author Daniel Rall + * @author Daniel L. Rall * @author Kasper Nielsen * @author Steven Caswell * @author Sean C. Sullivan - * @author Stephen Colebourne * @since 1.0 * @version $Id$ */ public class NestableDelegate implements Serializable { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** * Constructor error message. */ private transient static final String MUST_BE_THROWABLE = @@ -100,16 +70,32 @@ /** * Whether to print the stack trace top-down. * This public flag may be set by calling code, typically in initialisation. + * This exists for backwards compatability, setting it to false will return + * the library to v1.0 behaviour (but will affect all users of the library + * in the classloader). * @since 2.0 */ public static boolean topDown = true; /** * Whether to trim the repeated stack trace. * This public flag may be set by calling code, typically in initialisation. + * This exists for backwards compatability, setting it to false will return + * the library to v1.0 behaviour (but will affect all users of the library + * in the classloader). * @since 2.0 */ public static boolean trimStackFrames = true; + + /** + * Whether to match subclasses via indexOf. + * This public flag may be set by calling code, typically in initialisation. + * This exists for backwards compatability, setting it to false will return + * the library to v2.0 behaviour (but will affect all users of the library + * in the classloader). + * @since 2.1 + */ + public static boolean matchSubclasses = true; /** * Constructs a new NestableDelegate instance to manage the @@ -128,68 +114,54 @@ } /** - * Returns the error message of the Throwable in the chain - * of Throwables at the specified index, numbererd from 0. - * - * @param index the index of the Throwable in the chain of - * Throwables - * @return the error message, or null if the Throwable at the - * specified index in the chain does not contain a message - * @throws IndexOutOfBoundsException if the index argument is - * negative or not less than the count of Throwables in the - * chain + * Returns the error message of the Throwable in the chain of Throwables at the + * specified index, numbered from 0. + * + * @param index + * the index of the Throwable in the chain of Throwables + * @return the error message, or null if the Throwable at the specified index in the chain does not + * contain a message + * @throws IndexOutOfBoundsException + * if the index argument is negative or not less than the count of Throwables + * in the chain * @since 2.0 */ public String getMessage(int index) { Throwable t = this.getThrowable(index); if (Nestable.class.isInstance(t)) { return ((Nestable) t).getMessage(0); - } else { - return t.getMessage(); } + return t.getMessage(); } /** - * Returns the full message contained by the Nestable - * and any nested Throwables. - * - * @param baseMsg the base message to use when creating the full - * message. Should be generally be called via - * nestableHelper.getMessage(super.getMessage()), - * where super is an instance of {@link - * java.lang.Throwable}. - * @return The concatenated message for this and all nested - * Throwables + * Returns the full message contained by the Nestable and any nested Throwables. + * + * @param baseMsg + * the base message to use when creating the full message. Should be generally be called via + * nestableHelper.getMessage(super.getMessage()), where super is an + * instance of {@link java.lang.Throwable}. + * @return The concatenated message for this and all nested Throwables * @since 2.0 */ public String getMessage(String baseMsg) { - StringBuffer msg = new StringBuffer(); - if (baseMsg != null) { - msg.append(baseMsg); - } - Throwable nestedCause = ExceptionUtils.getCause(this.nestable); - if (nestedCause != null) { - String causeMsg = nestedCause.getMessage(); - if (causeMsg != null) { - if (baseMsg != null) { - msg.append(": "); - } - msg.append(causeMsg); - } - + String causeMsg = nestedCause == null ? null : nestedCause.getMessage(); + if (nestedCause == null || causeMsg == null) { + return baseMsg; // may be null, which is a valid result } - return (msg.length() > 0 ? msg.toString() : null); + if (baseMsg == null) { + return causeMsg; + } + return baseMsg + ": " + causeMsg; } /** - * Returns the error message of this and any nested Throwables - * in an array of Strings, one element for each message. Any - * Throwable not containing a message is represented in the - * array by a null. This has the effect of cause the length of the returned - * array to be equal to the result of the {@link #getThrowableCount()} - * operation. - * + * Returns the error message of this and any nested Throwables in an array of Strings, one element + * for each message. Any Throwable not containing a message is represented in the array by a null. + * This has the effect of cause the length of the returned array to be equal to the result of the + * {@link #getThrowableCount()} operation. + * * @return the error messages * @since 2.0 */ @@ -207,7 +179,7 @@ /** * Returns the Throwable in the chain of - * Throwables at the specified index, numbererd from 0. + * Throwables at the specified index, numbered from 0. * * @param index the index, numbered from 0, of the Throwable in * the chain of Throwables @@ -250,11 +222,19 @@ /** * Returns the index, numbered from 0, of the first Throwable - * that matches the specified type in the chain of Throwables - * held in this delegate's Nestable with an index greater than - * or equal to the specified index, or -1 if the type is not found. + * that matches the specified type, or a subclass, in the chain of Throwables + * with an index greater than or equal to the specified index. + * The method returns -1 if the specified type is not found in the chain. + *

        + * NOTE: From v2.1, we have clarified the Nestable interface + * such that this method matches subclasses. + * If you want to NOT match subclasses, please use + * {@link ExceptionUtils#indexOfThrowable(Throwable, Class, int)} + * (which is avaiable in all versions of lang). + * An alternative is to use the public static flag {@link #matchSubclasses} + * on NestableDelegate, however this is not recommended. * - * @param type Class to be found + * @param type the type to find, subclasses match, null returns -1 * @param fromIndex the index, numbered from 0, of the starting position in * the chain to be searched * @return index of the first occurrence of the type in the chain, or -1 if @@ -265,6 +245,9 @@ * @since 2.0 */ public int indexOfThrowable(Class type, int fromIndex) { + if (type == null) { + return -1; + } if (fromIndex < 0) { throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex); } @@ -273,10 +256,18 @@ throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex + " >= " + throwables.length); } - for (int i = fromIndex; i < throwables.length; i++) { - if (throwables[i].getClass().equals(type)) { - return i; + if (matchSubclasses) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } } return -1; } @@ -344,16 +335,19 @@ } // Remove the repeated lines in the stack - if (trimStackFrames) trimStackFrames(stacks); + if (trimStackFrames) { + trimStackFrames(stacks); + } synchronized (out) { for (Iterator iter=stacks.iterator(); iter.hasNext();) { String[] st = (String[]) iter.next(); for (int i=0, len=st.length; i < len; i++) { out.println(st[i]); } - if (iter.hasNext()) + if (iter.hasNext()) { out.print(separatorLine); + } } } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableError.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableError.java (.../NestableError.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableError.java (.../NestableError.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -59,14 +22,21 @@ /** * The base class of all errors which can contain other exceptions. * - * @author Daniel Rall + * @author Daniel L. Rall * @see org.apache.commons.lang.exception.NestableException * @since 1.0 * @version $Id$ */ public class NestableError extends Error implements Nestable { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** * The helper instance which contains much of the code which we * delegate to. */ @@ -121,6 +91,9 @@ this.cause = cause; } + /** + * {@inheritDoc} + */ public Throwable getCause() { return cause; } @@ -129,6 +102,8 @@ * Returns the detail message string of this throwable. If it was * created with a null message, returns the following: * (cause==null ? null : cause.toString()). + * + * @return String message string of the throwable */ public String getMessage() { if (super.getMessage() != null) { @@ -140,50 +115,82 @@ } } + /** + * {@inheritDoc} + */ public String getMessage(int index) { if (index == 0) { return super.getMessage(); - } else { - return delegate.getMessage(index); } + return delegate.getMessage(index); } + /** + * {@inheritDoc} + */ public String[] getMessages() { return delegate.getMessages(); } + /** + * {@inheritDoc} + */ public Throwable getThrowable(int index) { return delegate.getThrowable(index); } + /** + * {@inheritDoc} + */ public int getThrowableCount() { return delegate.getThrowableCount(); } + /** + * {@inheritDoc} + */ public Throwable[] getThrowables() { return delegate.getThrowables(); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type) { return delegate.indexOfThrowable(type, 0); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type, int fromIndex) { return delegate.indexOfThrowable(type, fromIndex); } + /** + * {@inheritDoc} + */ public void printStackTrace() { delegate.printStackTrace(); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintStream out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintWriter out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public final void printPartialStackTrace(PrintWriter out) { super.printStackTrace(out); } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableException.java (.../NestableException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableException.java (.../NestableException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -63,9 +26,9 @@ * about the exception which was caught and provoked throwing the * current exception. Catching and rethrowing may occur multiple * times, and provided that all exceptions except the first one - * are descendands of NestedException, when the + * are descendants of NestedException, when the * exception is finally printed out using any of the - * printStackTrace() methods, the stacktrace will contain + * printStackTrace() methods, the stack trace will contain * the information about all exceptions thrown and caught on * the way. *

        Running the following program @@ -102,7 +65,7 @@ * 30 } * 31 } *

        - *

        Yields the following stacktrace: + *

        Yields the following stack trace: *

          * org.apache.commons.lang.exception.NestableException: foo
          *         at Test.a(Test.java:16)
        @@ -118,7 +81,7 @@
          * 

        * * @author Rafal Krzewski - * @author Daniel Rall + * @author Daniel L. Rall * @author Kasper Nielsen * @author Steven Caswell * @since 1.0 @@ -127,6 +90,13 @@ public class NestableException extends Exception implements Nestable { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** * The helper instance which contains much of the code which we * delegate to. */ @@ -181,6 +151,9 @@ this.cause = cause; } + /** + * {@inheritDoc} + */ public Throwable getCause() { return cause; } @@ -189,6 +162,8 @@ * Returns the detail message string of this throwable. If it was * created with a null message, returns the following: * (cause==null ? null : cause.toString()). + * + * @return String message string of the throwable */ public String getMessage() { if (super.getMessage() != null) { @@ -200,50 +175,82 @@ } } + /** + * {@inheritDoc} + */ public String getMessage(int index) { if (index == 0) { return super.getMessage(); - } else { - return delegate.getMessage(index); } + return delegate.getMessage(index); } + /** + * {@inheritDoc} + */ public String[] getMessages() { return delegate.getMessages(); } + /** + * {@inheritDoc} + */ public Throwable getThrowable(int index) { return delegate.getThrowable(index); } + /** + * {@inheritDoc} + */ public int getThrowableCount() { return delegate.getThrowableCount(); } + /** + * {@inheritDoc} + */ public Throwable[] getThrowables() { return delegate.getThrowables(); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type) { return delegate.indexOfThrowable(type, 0); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type, int fromIndex) { return delegate.indexOfThrowable(type, fromIndex); } + /** + * {@inheritDoc} + */ public void printStackTrace() { delegate.printStackTrace(); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintStream out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintWriter out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public final void printPartialStackTrace(PrintWriter out) { super.printStackTrace(out); } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableRuntimeException.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableRuntimeException.java (.../NestableRuntimeException.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/NestableRuntimeException.java (.../NestableRuntimeException.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.exception; @@ -62,7 +25,7 @@ * * @see org.apache.commons.lang.exception.NestableException * @author Rafal Krzewski - * @author Daniel Rall + * @author Daniel L. Rall * @author Kasper Nielsen * @author Steven Caswell * @since 1.0 @@ -71,6 +34,13 @@ public class NestableRuntimeException extends RuntimeException implements Nestable { /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** * The helper instance which contains much of the code which we * delegate to. */ @@ -125,6 +95,9 @@ this.cause = cause; } + /** + * {@inheritDoc} + */ public Throwable getCause() { return cause; } @@ -133,6 +106,8 @@ * Returns the detail message string of this throwable. If it was * created with a null message, returns the following: * (cause==null ? null : cause.toString()). + * + * @return String message string of the throwable */ public String getMessage() { if (super.getMessage() != null) { @@ -144,50 +119,82 @@ } } + /** + * {@inheritDoc} + */ public String getMessage(int index) { if (index == 0) { return super.getMessage(); - } else { - return delegate.getMessage(index); } + return delegate.getMessage(index); } + /** + * {@inheritDoc} + */ public String[] getMessages() { return delegate.getMessages(); } + /** + * {@inheritDoc} + */ public Throwable getThrowable(int index) { return delegate.getThrowable(index); } + /** + * {@inheritDoc} + */ public int getThrowableCount() { return delegate.getThrowableCount(); } + /** + * {@inheritDoc} + */ public Throwable[] getThrowables() { return delegate.getThrowables(); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type) { return delegate.indexOfThrowable(type, 0); } + /** + * {@inheritDoc} + */ public int indexOfThrowable(Class type, int fromIndex) { return delegate.indexOfThrowable(type, fromIndex); } + /** + * {@inheritDoc} + */ public void printStackTrace() { delegate.printStackTrace(); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintStream out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public void printStackTrace(PrintWriter out) { delegate.printStackTrace(out); } + /** + * {@inheritDoc} + */ public final void printPartialStackTrace(PrintWriter out) { super.printStackTrace(out); } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/exception/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,3 +1,19 @@ + Provides JDK 1.4 style Nested Exception functionality for those on prior Java Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/DoubleRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/DoubleRange.java (.../DoubleRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/DoubleRange.java (.../DoubleRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,69 +1,39 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; import java.io.Serializable; +import org.apache.commons.lang.text.StrBuilder; + /** *

        DoubleRange represents an inclusive range of doubles.

        * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ public final class DoubleRange extends Range implements Serializable { + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 71849363892740L; /** @@ -343,7 +313,7 @@ * range by double comparison */ public boolean containsDouble(double value) { - return (value >= min && value <= max); + return value >= min && value <= max; } // Range tests @@ -434,7 +404,7 @@ */ public String toString() { if (toString == null) { - StringBuffer buf = new StringBuffer(32); + StrBuilder buf = new StrBuilder(32); buf.append("Range["); buf.append(min); buf.append(','); Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/FloatRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/FloatRange.java (.../FloatRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/FloatRange.java (.../FloatRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; @@ -58,12 +21,17 @@ /** *

        FloatRange represents an inclusive range of floats.

        * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ public final class FloatRange extends Range implements Serializable { + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 71849363892750L; /** @@ -339,7 +307,7 @@ * range by float comparison */ public boolean containsFloat(float value) { - return (value >= min && value <= max); + return value >= min && value <= max; } // Range tests Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Fraction.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Fraction.java (.../Fraction.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Fraction.java (.../Fraction.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,77 +1,47 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; -import java.io.Serializable; +import java.math.BigInteger; +import org.apache.commons.lang.text.StrBuilder; + /** *

        Fraction is a Number implementation that * stores fractions accurately.

        * *

        This class is immutable, and interoperable with most methods that accept * a Number.

        * + * @author Apache Software Foundation * @author Travis Reeder - * @author Stephen Colebourne * @author Tim O'Brien * @author Pete Gieser + * @author C. Scott Ananian * @since 2.0 * @version $Id$ */ -public final class Fraction extends Number implements Serializable, Comparable { +public final class Fraction extends Number implements Comparable { - /** Serialization lock, Lang version 2.0 */ + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 65382027393090L; /** @@ -175,6 +145,10 @@ throw new ArithmeticException("The denominator must not be zero"); } if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } numerator = -numerator; denominator = -denominator; } @@ -192,7 +166,7 @@ * @param denominator the denominator, for example the seven in 'one and three sevenths' * @return a new fraction instance * @throws ArithmeticException if the denomiator is zero - * @throws ArithmeticException if the denomiator is negative + * @throws ArithmeticException if the denominator is negative * @throws ArithmeticException if the numerator is negative * @throws ArithmeticException if the resulting numerator exceeds * Integer.MAX_VALUE @@ -207,39 +181,57 @@ if (numerator < 0) { throw new ArithmeticException("The numerator must not be negative"); } - double numeratorValue = 0; + long numeratorValue; if (whole < 0) { - numeratorValue = (double) whole * denominator - numerator; + numeratorValue = whole * (long)denominator - numerator; } else { - numeratorValue = (double) whole * denominator + numerator; + numeratorValue = whole * (long)denominator + numerator; } - if (Math.abs(numeratorValue) > Integer.MAX_VALUE) { + if (numeratorValue < Integer.MIN_VALUE || + numeratorValue > Integer.MAX_VALUE) { throw new ArithmeticException("Numerator too large to represent as an Integer."); } return new Fraction((int) numeratorValue, denominator); } /** - *

        Creates a Fraction instance with the 2 parts + *

        Creates a reduced Fraction instance with the 2 parts * of a fraction Y/Z.

        * + *

        For example, if the input parameters represent 2/4, then the created + * fraction will be 1/2.

        + * *

        Any negative signs are resolved to be on the numerator.

        * * @param numerator the numerator, for example the three in 'three sevenths' * @param denominator the denominator, for example the seven in 'three sevenths' * @return a new fraction instance, with the numerator and denominator reduced - * @throws ArithmeticException if the denomiator is zero + * @throws ArithmeticException if the denominator is zero */ public static Fraction getReducedFraction(int numerator, int denominator) { if (denominator == 0) { throw new ArithmeticException("The denominator must not be zero"); } + if (numerator==0) { + return ZERO; // normalize zero. + } + // allow 2^k/-2^31 as a valid fraction (where k>0) + if (denominator==Integer.MIN_VALUE && (numerator&1)==0) { + numerator/=2; denominator/=2; + } if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } numerator = -numerator; denominator = -denominator; } - int gcd = greatestCommonDivisor(Math.abs(numerator), denominator); - return new Fraction(numerator / gcd, denominator / gcd); + // simplify fraction. + int gcd = greatestCommonDivisor(numerator, denominator); + numerator /= gcd; + denominator /= gcd; + return new Fraction(numerator, denominator); } /** @@ -253,7 +245,7 @@ * @return a new fraction instance that is close to the value * @throws ArithmeticException if |value| > Integer.MAX_VALUE * or value = NaN - * @throws ArithmeticException if the calculated denomiator is zero + * @throws ArithmeticException if the calculated denominator is zero * @throws ArithmeticException if the the algorithm does not converge */ public static Fraction getFraction(double value) { @@ -313,11 +305,11 @@ * *

        The formats accepted are:

        * - *

        *

          *
        1. double String containing a dot
        2. *
        3. 'X Y/Z'
        4. *
        5. 'Y/Z'
        6. + *
        7. 'X' (a simple whole number)
        8. *
        * and a .

        * @@ -345,11 +337,9 @@ if (pos < 0) { throw new NumberFormatException("The fraction could not be parsed as the format X Y/Z"); } else { + int numer = Integer.parseInt(str.substring(0, pos)); int denom = Integer.parseInt(str.substring(pos + 1)); - return getFraction( - Integer.parseInt(str.substring(0, pos)) + whole * denom, - denom - ); + return getFraction(whole, numer, denom); } } @@ -359,10 +349,9 @@ // simple whole number return getFraction(Integer.parseInt(str), 1); } else { - return getFraction( - Integer.parseInt(str.substring(0, pos)), - Integer.parseInt(str.substring(pos + 1)) - ); + int numer = Integer.parseInt(str.substring(0, pos)); + int denom = Integer.parseInt(str.substring(pos + 1)); + return getFraction(numer, denom); } } @@ -468,28 +457,45 @@ /** *

        Reduce the fraction to the smallest values for the numerator and - * denominator, returning the result..

        + * denominator, returning the result.

        + * + *

        For example, if this fraction represents 2/4, then the result + * will be 1/2.

        * - * @return a new reduce fraction instance, or this if no simplification possible + * @return a new reduced fraction instance, or this if no simplification possible */ public Fraction reduce() { + if (numerator == 0) { + return equals(ZERO) ? this : ZERO; + } int gcd = greatestCommonDivisor(Math.abs(numerator), denominator); + if (gcd == 1) { + return this; + } return Fraction.getFraction(numerator / gcd, denominator / gcd); } /** - *

        Gets a fraction that is the invert (1/fraction) of this one.

        - * + *

        Gets a fraction that is the inverse (1/fraction) of this one.

        + * *

        The returned fraction is not reduced.

        * - * @return a new fraction instance with the numerator and denominator inverted - * @throws ArithmeticException if the numerator is zero + * @return a new fraction instance with the numerator and denominator + * inverted. + * @throws ArithmeticException if the fraction represents zero. */ public Fraction invert() { if (numerator == 0) { - throw new ArithmeticException("Unable to invert a fraction with a zero numerator"); + throw new ArithmeticException("Unable to invert zero."); } - return getFraction(denominator, numerator); + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate numerator"); + } + if (numerator<0) { + return new Fraction(-denominator, -numerator); + } else { + return new Fraction(denominator, numerator); + } } /** @@ -500,12 +506,16 @@ * @return a new fraction instance with the opposite signed numerator */ public Fraction negate() { - return getFraction(-numerator, denominator); + // the positive range is one smaller than the negative range of an int. + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: too large to negate"); + } + return new Fraction(-numerator, denominator); } /** *

        Gets a fraction that is the positive equivalent of this one.

        - *

        More precisely:

        (fraction >= 0 ? this : -fraction)

        + *

        More precisely: (fraction >= 0 ? this : -fraction)

        * *

        The returned fraction is not reduced.

        * @@ -516,13 +526,13 @@ if (numerator >= 0) { return this; } - return getFraction(-numerator, denominator); + return negate(); } /** *

        Gets a fraction that is raised to the passed in power.

        * - *

        The returned fraction is not reduced.

        + *

        The returned fraction is in reduced form.

        * * @param power the power to raise the fraction to * @return this if the power is one, ONE if the power @@ -536,44 +546,153 @@ return this; } else if (power == 0) { return ONE; + } else if (power < 0) { + if (power==Integer.MIN_VALUE) { // MIN_VALUE can't be negated. + return this.invert().pow(2).pow(-(power/2)); + } + return this.invert().pow(-power); } else { - double denominatorValue = Math.pow(denominator, power); - double numeratorValue = Math.pow(numerator, power); - if (numeratorValue > Integer.MAX_VALUE || denominatorValue > Integer.MAX_VALUE) { - throw new ArithmeticException("Integer overflow"); + Fraction f = this.multiplyBy(this); + if ((power % 2) == 0) { // if even... + return f.pow(power/2); + } else { // if odd... + return f.pow(power/2).multiplyBy(this); } - if (power < 0) { - return getFraction((int) Math.pow(denominator, -power), - (int) Math.pow(numerator, -power)); - } - return getFraction((int) Math.pow(numerator, power), - (int) Math.pow(denominator, power)); } } /** - *

        Gets the greatest common divisor of two numbers.

        + *

        Gets the greatest common divisor of the absolute value of + * two numbers, using the "binary gcd" method which avoids + * division and modulo operations. See Knuth 4.5.2 algorithm B. + * This algorithm is due to Josef Stein (1961).

        * - * @param number1 a positive number - * @param number2 a positive number + * @param u a non-zero number + * @param v a non-zero number * @return the greatest common divisor, never zero */ - private static int greatestCommonDivisor(int number1, int number2) { - int remainder = number1 % number2; - while (remainder != 0) { - number1 = number2; - number2 = remainder; - remainder = number1 % number2; + private static int greatestCommonDivisor(int u, int v) { + //if either op. is abs 0 or 1, return 1: + if (Math.abs(u) <= 1 || Math.abs(v) <= 1) { + return 1; } - return number2; + // keep u and v negative, as negative integers range down to + // -2^31, while positive numbers can only be as large as 2^31-1 + // (i.e. we can't necessarily negate a negative number without + // overflow) + if (u>0) { u=-u; } // make u negative + if (v>0) { v=-v; } // make v negative + // B1. [Find power of 2] + int k=0; + while ((u&1)==0 && (v&1)==0 && k<31) { // while u and v are both even... + u/=2; v/=2; k++; // cast out twos. + } + if (k==31) { + throw new ArithmeticException("overflow: gcd is 2^31"); + } + // B2. Initialize: u and v have been divided by 2^k and at least + // one is odd. + int t = ((u&1)==1) ? v : -(u/2)/*B3*/; + // t negative: u was odd, v may be even (t replaces v) + // t positive: u was even, v is odd (t replaces u) + do { + /* assert u<0 && v<0; */ + // B4/B3: cast out twos from t. + while ((t&1)==0) { // while t is even.. + t/=2; // cast out twos + } + // B5 [reset max(u,v)] + if (t>0) { + u = -t; + } else { + v = t; + } + // B6/B3. at this point both u and v should be odd. + t = (v - u)/2; + // |u| larger: t positive (replace u) + // |v| larger: t negative (replace v) + } while (t!=0); + return -u*(1<x*y
        + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulAndCheck(int x, int y) { + long m = ((long)x)*((long)y); + if (m < Integer.MIN_VALUE || + m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mul"); + } + return (int)m; + } + /** - *

        Adds the value of this fraction to another, returning the result in - * reduced form.

        + * Multiply two non-negative integers, checking for overflow. + * + * @param x a non-negative factor + * @param y a non-negative factor + * @return the product x*y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulPosAndCheck(int x, int y) { + /* assert x>=0 && y>=0; */ + long m = ((long)x)*((long)y); + if (m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mulPos"); + } + return (int)m; + } + + /** + * Add two integers, checking for overflow. + * + * @param x an addend + * @param y an addend + * @return the sum x+y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int addAndCheck(int x, int y) { + long s = (long)x+(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + * Subtract two integers, checking for overflow. + * + * @param x the minuend + * @param y the subtrahend + * @return the difference x-y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int subAndCheck(int x, int y) { + long s = (long)x-(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + *

        Adds the value of this fraction to another, returning the result in reduced form. + * The algorithm follows Knuth, 4.5.1.

        * * @param fraction the fraction to add, must not be null * @return a Fraction instance with the resulting values @@ -582,50 +701,84 @@ * Integer.MAX_VALUE */ public Fraction add(Fraction fraction) { - if (fraction == null) { - throw new IllegalArgumentException("The fraction must not be null"); - } - if (numerator == 0) { - return fraction; - } - if (fraction.numerator == 0) { - return this; - } - // Compute lcd explicitly to limit overflow - int gcd = greatestCommonDivisor(Math.abs(fraction.denominator), Math.abs(denominator)); - int thisResidue = denominator/gcd; - int thatResidue = fraction.denominator/gcd; - double denominatorValue = Math.abs((double) gcd * thisResidue * thatResidue); - double numeratorValue = (double) numerator * thatResidue + fraction.numerator * thisResidue; - if (Math.abs(numeratorValue) > Integer.MAX_VALUE || - Math.abs(denominatorValue) > Integer.MAX_VALUE) { - throw new ArithmeticException("Integer overflow"); - } - return Fraction.getReducedFraction((int) numeratorValue, (int) denominatorValue); + return addSub(fraction, true /* add */); } /** - *

        Subtracts the value of another fraction from the value of this one, + *

        Subtracts the value of another fraction from the value of this one, * returning the result in reduced form.

        * * @param fraction the fraction to subtract, must not be null * @return a Fraction instance with the resulting values * @throws IllegalArgumentException if the fraction is null - * @throws ArithmeticException if the resulting numerator or denominator exceeds - * Integer.MAX_VALUE + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. */ public Fraction subtract(Fraction fraction) { + return addSub(fraction, false /* subtract */); + } + + /** + * Implement add and subtract using algorithm described in Knuth 4.5.1. + * + * @param fraction the fraction to subtract, must not be null + * @param isAdd true to add, false to subtract + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. + */ + private Fraction addSub(Fraction fraction, boolean isAdd) { if (fraction == null) { throw new IllegalArgumentException("The fraction must not be null"); } - return add(fraction.negate()); + // zero is identity for addition. + if (numerator == 0) { + return isAdd ? fraction : fraction.negate(); + } + if (fraction.numerator == 0) { + return this; + } + // if denominators are randomly distributed, d1 will be 1 about 61% + // of the time. + int d1 = greatestCommonDivisor(denominator, fraction.denominator); + if (d1==1) { + // result is ( (u*v' +/- u'v) / u'v') + int uvp = mulAndCheck(numerator, fraction.denominator); + int upv = mulAndCheck(fraction.numerator, denominator); + return new Fraction + (isAdd ? addAndCheck(uvp, upv) : subAndCheck(uvp, upv), + mulPosAndCheck(denominator, fraction.denominator)); + } + // the quantity 't' requires 65 bits of precision; see knuth 4.5.1 + // exercise 7. we're going to use a BigInteger. + // t = u(v'/d1) +/- v(u'/d1) + BigInteger uvp = BigInteger.valueOf(numerator) + .multiply(BigInteger.valueOf(fraction.denominator/d1)); + BigInteger upv = BigInteger.valueOf(fraction.numerator) + .multiply(BigInteger.valueOf(denominator/d1)); + BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); + // but d2 doesn't need extra precision because + // d2 = gcd(t,d1) = gcd(t mod d1, d1) + int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); + int d2 = (tmodd1==0)?d1:greatestCommonDivisor(tmodd1, d1); + + // result is (t/d2) / (u'/d1)(v'/d2) + BigInteger w = t.divide(BigInteger.valueOf(d2)); + if (w.bitLength() > 31) { + throw new ArithmeticException + ("overflow: numerator too large after multiply"); + } + return new Fraction + (w.intValue(), + mulPosAndCheck(denominator/d1, fraction.denominator/d2)); } /** - *

        Multiplies the value of this fraction by another, returning the result - * in reduced form.

        + *

        Multiplies the value of this fraction by another, returning the + * result in reduced form.

        * - * @param fraction the fraction to multipy by, must not be null + * @param fraction the fraction to multiply by, must not be null * @return a Fraction instance with the resulting values * @throws IllegalArgumentException if the fraction is null * @throws ArithmeticException if the resulting numerator or denominator exceeds @@ -638,18 +791,17 @@ if (numerator == 0 || fraction.numerator == 0) { return ZERO; } - double numeratorValue = (double) numerator * fraction.numerator; - double denominatorValue = (double) denominator * fraction.denominator; - if (Math.abs(numeratorValue) > Integer.MAX_VALUE || - Math.abs(denominatorValue) > Integer.MAX_VALUE) { - throw new ArithmeticException("Integer overflow"); - } - return getReducedFraction((int) numeratorValue, (int) denominatorValue); + // knuth 4.5.1 + // make sure we don't overflow unless the result *must* overflow. + int d1 = greatestCommonDivisor(numerator, fraction.denominator); + int d2 = greatestCommonDivisor(fraction.numerator, denominator); + return getReducedFraction + (mulAndCheck(numerator/d1, fraction.numerator/d2), + mulPosAndCheck(denominator/d2, fraction.denominator/d1)); } /** - *

        Divide the value of this fraction by another, returning the result - * in reduced form.

        + *

        Divide the value of this fraction by another.

        * * @param fraction the fraction to divide by, must not be null * @return a Fraction instance with the resulting values @@ -665,16 +817,7 @@ if (fraction.numerator == 0) { throw new ArithmeticException("The fraction to divide by must not be zero"); } - if (numerator == 0) { - return ZERO; - } - double numeratorValue = (double) numerator * fraction.denominator; - double denominatorValue = (double) denominator * fraction.numerator; - if (Math.abs(numeratorValue) > Integer.MAX_VALUE || - Math.abs(denominatorValue) > Integer.MAX_VALUE) { - throw new ArithmeticException("Integer overflow"); - } - return getReducedFraction((int) numeratorValue, (int) denominatorValue); + return multiplyBy(fraction.invert()); } // Basics @@ -696,8 +839,8 @@ return false; } Fraction other = (Fraction) obj; - return (numerator == other.numerator && - denominator == other.denominator); + return (getNumerator() == other.getNumerator() && + getDenominator() == other.getDenominator()); } /** @@ -707,23 +850,29 @@ */ public int hashCode() { if (hashCode == 0) { - hashCode = 17; - hashCode = 37 * hashCode + numerator; - hashCode = 37 * hashCode + denominator; + // hashcode update should be atomic. + hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator(); } return hashCode; } /** *

        Compares this object to another based on size.

        * + *

        Note: this class has a natural ordering that is inconsistent + * with equals, because, for example, equals treats 1/2 and 2/4 as + * different, whereas compareTo treats them as equal. + * * @param object the object to compare to * @return -1 if this is less, 0 if equal, +1 if greater * @throws ClassCastException if the object is not a Fraction * @throws NullPointerException if the object is null */ public int compareTo(Object object) { Fraction other = (Fraction) object; + if (this==other) { + return 0; + } if (numerator == other.numerator && denominator == other.denominator) { return 0; } @@ -749,10 +898,10 @@ */ public String toString() { if (toString == null) { - toString = new StringBuffer(32) - .append(numerator) + toString = new StrBuilder(32) + .append(getNumerator()) .append('/') - .append(denominator).toString(); + .append(getDenominator()).toString(); } return toString; } @@ -772,23 +921,28 @@ toProperString = "0"; } else if (numerator == denominator) { toProperString = "1"; - } else if (Math.abs(numerator) > denominator) { + } else if (numerator == -1 * denominator) { + toProperString = "-1"; + } else if ((numerator>0?-numerator:numerator) < -denominator) { + // note that we do the magnitude comparison test above with + // NEGATIVE (not positive) numbers, since negative numbers + // have a larger range. otherwise numerator==Integer.MIN_VALUE + // is handled incorrectly. int properNumerator = getProperNumerator(); if (properNumerator == 0) { toProperString = Integer.toString(getProperWhole()); } else { - toProperString = new StringBuffer(32) + toProperString = new StrBuilder(32) .append(getProperWhole()).append(' ') .append(properNumerator).append('/') - .append(denominator).toString(); + .append(getDenominator()).toString(); } } else { - toProperString = new StringBuffer(32) - .append(numerator).append('/') - .append(denominator).toString(); + toProperString = new StrBuilder(32) + .append(getNumerator()).append('/') + .append(getDenominator()).toString(); } } return toProperString; } - } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IEEE754rUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IEEE754rUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IEEE754rUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.math; + +/** + *

        Provides IEEE-754r variants of NumberUtils methods.

        + * + *

        See: http://en.wikipedia.org/wiki/IEEE_754r

        + * + * @since 2.4 + * @author Apache Software Foundation + * @version $Id$ + */ +public class IEEE754rUtils { + + /** + *

        Returns the minimum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static double min(double[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + double min = array[0]; + for (int i = 1; i < array.length; i++) { + min = min(array[i], min); + } + + return min; + } + + /** + *

        Returns the minimum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static float min(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + float min = array[0]; + for (int i = 1; i < array.length; i++) { + min = min(array[i], min); + } + + return min; + } + + /** + *

        Gets the minimum of three double values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static double min(double a, double b, double c) { + return min(min(a, b), c); + } + + /** + *

        Gets the minimum of two double values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @return the smallest of the values + */ + public static double min(double a, double b) { + if(Double.isNaN(a)) { + return b; + } else + if(Double.isNaN(b)) { + return a; + } else { + return Math.min(a, b); + } + } + + /** + *

        Gets the minimum of three float values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static float min(float a, float b, float c) { + return min(min(a, b), c); + } + + /** + *

        Gets the minimum of two float values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @return the smallest of the values + */ + public static float min(float a, float b) { + if(Float.isNaN(a)) { + return b; + } else + if(Float.isNaN(b)) { + return a; + } else { + return Math.min(a, b); + } + } + + /** + *

        Returns the maximum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static double max(double[] array) { + // Validates input + if (array== null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + double max = array[0]; + for (int j = 1; j < array.length; j++) { + max = max(array[j], max); + } + + return max; + } + + /** + *

        Returns the maximum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static float max(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + float max = array[0]; + for (int j = 1; j < array.length; j++) { + max = max(array[j], max); + } + + return max; + } + + /** + *

        Gets the maximum of three double values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static double max(double a, double b, double c) { + return max(max(a, b), c); + } + + /** + *

        Gets the maximum of two double values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @return the largest of the values + */ + public static double max(double a, double b) { + if(Double.isNaN(a)) { + return b; + } else + if(Double.isNaN(b)) { + return a; + } else { + return Math.max(a, b); + } + } + + /** + *

        Gets the maximum of three float values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static float max(float a, float b, float c) { + return max(max(a, b), c); + } + + /** + *

        Gets the maximum of two float values.

        + * + *

        NaN is only returned if all numbers are NaN as per IEEE-754r.

        + * + * @param a value 1 + * @param b value 2 + * @return the largest of the values + */ + public static float max(float a, float b) { + if(Float.isNaN(a)) { + return b; + } else + if(Float.isNaN(b)) { + return a; + } else { + return Math.max(a, b); + } + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IntRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IntRange.java (.../IntRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/IntRange.java (.../IntRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,69 +1,39 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; import java.io.Serializable; +import org.apache.commons.lang.text.StrBuilder; + /** *

        IntRange represents an inclusive range of ints.

        * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ public final class IntRange extends Range implements Serializable { + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 71849363892730L; /** @@ -314,7 +284,7 @@ * range by int comparison */ public boolean containsInteger(int value) { - return (value >= min && value <= max); + return value >= min && value <= max; } // Range tests @@ -375,7 +345,7 @@ return false; } IntRange range = (IntRange) obj; - return (min == range.min && max == range.max); + return min == range.min && max == range.max; } /** @@ -402,7 +372,7 @@ */ public String toString() { if (toString == null) { - StringBuffer buf = new StringBuffer(32); + StrBuilder buf = new StrBuilder(32); buf.append("Range["); buf.append(min); buf.append(','); @@ -413,4 +383,18 @@ return toString; } + /** + *

        Returns an array containing all the integer values in the range.

        + * + * @return the int[] representation of this range + * @since 2.4 + */ + public int[] toArray() { + int[] array = new int[max - min + 1]; + for (int i = 0; i < array.length; i++) { + array[i] = min + i; + } + + return array; + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/JVMRandom.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/JVMRandom.java (.../JVMRandom.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/JVMRandom.java (.../JVMRandom.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,20 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * Copyright (c) 2002 The Apache Software Foundation. All rights - * reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package org.apache.commons.lang.math; @@ -59,24 +24,47 @@ *

        JVMRandom is a wrapper that supports all possible * Random methods via the {@link java.lang.Math#random()} method * and its system-wide {@link Random} object.

        + *

        + * It does this to allow for a Random class in which the seed is + * shared between all members of the class - a better name would + * have been SharedSeedRandom. + *

        + * N.B. the current implementation overrides the methods + * {@link Random#nextInt(int)} and {@link Random#nextLong()} + * to produce positive numbers ranging from 0 (inclusive) + * to MAX_VALUE (exclusive). * - * @author Henri Yandell * @since 2.0 * @version $Id$ */ public final class JVMRandom extends Random { /** - * Ensures that only the constructor can call reseed. + * Required for serialization support. + * + * @see java.io.Serializable */ + private static final long serialVersionUID = 1L; + + private static final Random SHARED_RANDOM = new Random(); + + /** + * Ensures that only the parent constructor can call reseed. + */ private boolean constructed = false; + /** + * Constructs a new instance. + */ public JVMRandom() { this.constructed = true; } /** * Unsupported in 2.0. + * + * @param seed ignored + * @throws UnsupportedOperationException */ public synchronized void setSeed(long seed) { if (this.constructed) { @@ -86,13 +74,19 @@ /** * Unsupported in 2.0. + * + * @return Nothing, this method always throws an UnsupportedOperationException. + * @throws UnsupportedOperationException */ public synchronized double nextGaussian() { throw new UnsupportedOperationException(); } /** * Unsupported in 2.0. + * + * @param byteArray ignored + * @throws UnsupportedOperationException */ public void nextBytes(byte[] byteArray) { throw new UnsupportedOperationException(); @@ -101,12 +95,16 @@ /** *

        Returns the next pseudorandom, uniformly distributed int value * from the Math.random() sequence.

        - * + * Identical to nextInt(Integer.MAX_VALUE) + *

        + * N.B. All values are >= 0. + *

        * @return the random int */ public int nextInt() { return nextInt(Integer.MAX_VALUE); } + /** *

        Returns a pseudorandom, uniformly distributed int value between * 0 (inclusive) and the specified value (exclusive), from @@ -117,21 +115,19 @@ * @throws IllegalArgumentException when n <= 0 */ public int nextInt(int n) { - if (n <= 0) { - throw new IllegalArgumentException( - "Upper bound for nextInt must be positive" - ); - } - // TODO: check this cannot return 'n' - return (int)(Math.random() * n); + return SHARED_RANDOM.nextInt(n); } + /** *

        Returns the next pseudorandom, uniformly distributed long value * from the Math.random() sequence.

        + * Identical to nextLong(Long.MAX_VALUE) + *

        + * N.B. All values are >= 0. + *

        * @return the random long */ public long nextLong() { - // possible loss of precision? return nextLong(Long.MAX_VALUE); } @@ -151,8 +147,20 @@ "Upper bound for nextInt must be positive" ); } - // TODO: check this cannot return 'n' - return (long)(Math.random() * n); + // Code adapted from Harmony Random#nextInt(int) + if ((n & -n) == n) { // n is power of 2 + // dropping lower order bits improves behaviour for low values of n + return next63bits() >> 63 // drop all the bits + - bitsRequired(n-1); // except the ones we need + } + // Not a power of two + long val; + long bits; + do { // reject some values to improve distribution + bits = next63bits(); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; } /** @@ -162,8 +170,9 @@ * @return the random boolean */ public boolean nextBoolean() { - return (Math.random() > 0.5); + return SHARED_RANDOM.nextBoolean(); } + /** *

        Returns the next pseudorandom, uniformly distributed float value * between 0.0 and 1.0 from the Math.random() @@ -172,15 +181,48 @@ * @return the random float */ public float nextFloat() { - return (float)Math.random(); + return SHARED_RANDOM.nextFloat(); } + /** *

        Synonymous to the Math.random() call.

        * * @return the random double */ public double nextDouble() { - return Math.random(); + return SHARED_RANDOM.nextDouble(); } + /** + * Get the next unsigned random long + * @return unsigned random long + */ + private static long next63bits(){ + // drop the sign bit to leave 63 random bits + return SHARED_RANDOM.nextLong() & 0x7fffffffffffffffL; + } + + /** + * Count the number of bits required to represent a long number. + * + * @param num long number + * @return number of bits required + */ + private static int bitsRequired(long num){ + // Derived from Hacker's Delight, Figure 5-9 + long y=num; // for checking right bits + int n=0; // number of leading zeros found + while(true){ + // 64 = number of bits in a long + if (num < 0) { + return 64-n; // no leading zeroes left + } + if (y == 0) { + return n; // no bits left to check + } + n++; + num=num << 1; // check leading bits + y=y >> 1; // check trailing bits + } + } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/LongRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/LongRange.java (.../LongRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/LongRange.java (.../LongRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,69 +1,39 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; import java.io.Serializable; +import org.apache.commons.lang.text.StrBuilder; + /** *

        LongRange represents an inclusive range of longs.

        * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ public final class LongRange extends Range implements Serializable { + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 71849363892720L; /** @@ -264,9 +234,11 @@ } /** - *

        Gets the maximum number in this range as a int.

        + *

        Gets the maximum number in this range cast to an int.

        * *

        This conversion can lose information for large values.

        + * + * @return the maximum number in this range cast to an int. */ public int getMaximumInteger() { return (int) max; @@ -276,6 +248,8 @@ *

        Gets the maximum number in this range as a double.

        * *

        This conversion can lose information for large values.

        + * + * @return The maximum number in this range as a double. */ public double getMaximumDouble() { return max; @@ -285,6 +259,8 @@ *

        Gets the maximum number in this range as a float.

        * *

        This conversion can lose information for large values.

        + * + * @return The maximum number in this range as a float. */ public float getMaximumFloat() { return max; @@ -321,7 +297,7 @@ * range by long comparison */ public boolean containsLong(long value) { - return (value >= min && value <= max); + return value >= min && value <= max; } // Range tests @@ -382,7 +358,7 @@ return false; } LongRange range = (LongRange) obj; - return (min == range.min && max == range.max); + return min == range.min && max == range.max; } /** @@ -409,7 +385,7 @@ */ public String toString() { if (toString == null) { - StringBuffer buf = new StringBuffer(32); + StrBuilder buf = new StrBuilder(32); buf.append("Range["); buf.append(min); buf.append(','); @@ -420,4 +396,18 @@ return toString; } + /** + *

        Returns an array containing all the long values in the range.

        + * + * @return the long[] representation of this range + * @since 2.4 + */ + public long[] toArray() { + long[] array = new long[(int)(max - min + 1L)]; + for(int i = 0; i < array.length; i++) { + array[i] = min + i; + } + return array; + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberRange.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberRange.java (.../NumberRange.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberRange.java (.../NumberRange.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,71 +1,41 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; import java.io.Serializable; +import org.apache.commons.lang.text.StrBuilder; + /** *

        NumberRange represents an inclusive range of * {@link java.lang.Number} objects of the same type.

        * + * @author Apache Software Foundation * @author Christopher Elkins - * @author Stephen Colebourne * @since 2.0 (previously in org.apache.commons.lang) * @version $Id$ */ public final class NumberRange extends Range implements Serializable { + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ private static final long serialVersionUID = 71849363892710L; /** @@ -207,7 +177,7 @@ } int compareMin = ((Comparable) min).compareTo(number); int compareMax = ((Comparable) max).compareTo(number); - return (compareMin <= 0 && compareMax >= 0); + return compareMin <= 0 && compareMax >= 0; } // Range tests @@ -260,7 +230,7 @@ */ public String toString() { if (toString == null) { - StringBuffer buf = new StringBuffer(32); + StrBuilder buf = new StrBuilder(32); buf.append("Range["); buf.append(min); buf.append(','); Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberUtils.java (.../NumberUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/NumberUtils.java (.../NumberUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,18 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; @@ -61,14 +24,14 @@ /** *

        Provides extra functionality for Java Number classes.

        * - * @author Henri Yandell + * @author Apache Software Foundation * @author Rand McNeely - * @author Stephen Colebourne * @author Steve Downey * @author Eric Pugh * @author Phil Steitz * @author Matthew Hawthorne * @author Gary Gregory + * @author Fredrik Westermarck * @since 2.0 * @version $Id$ */ @@ -113,12 +76,13 @@ /** *

        NumberUtils instances should NOT be constructed in standard programming. - * Instead, the class should be used as NumberUtils.stringToInt("6");.

        + * Instead, the class should be used as NumberUtils.toInt("6");.

        * *

        This constructor is public to permit tools that require a JavaBean instance * to operate.

        */ public NumberUtils() { + super(); } //----------------------------------------------------------------------- @@ -128,32 +92,345 @@ * *

        If the string is null, zero is returned.

        * + *
        +     *   NumberUtils.stringToInt(null) = 0
        +     *   NumberUtils.stringToInt("")   = 0
        +     *   NumberUtils.stringToInt("1")  = 1
        +     * 
        + * * @param str the string to convert, may be null * @return the int represented by the string, or zero if * conversion fails + * @deprecated Use {@link #toInt(String)} + * This method will be removed in Commons Lang 3.0 */ public static int stringToInt(String str) { - return stringToInt(str, 0); + return toInt(str); } /** + *

        Convert a String to an int, returning + * zero if the conversion fails.

        + * + *

        If the string is null, zero is returned.

        + * + *
        +     *   NumberUtils.toInt(null) = 0
        +     *   NumberUtils.toInt("")   = 0
        +     *   NumberUtils.toInt("1")  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @return the int represented by the string, or zero if + * conversion fails + * @since 2.1 + */ + public static int toInt(String str) { + return toInt(str, 0); + } + + /** *

        Convert a String to an int, returning a * default value if the conversion fails.

        * *

        If the string is null, the default value is returned.

        * + *
        +     *   NumberUtils.stringToInt(null, 1) = 1
        +     *   NumberUtils.stringToInt("", 1)   = 1
        +     *   NumberUtils.stringToInt("1", 0)  = 1
        +     * 
        + * * @param str the string to convert, may be null * @param defaultValue the default value * @return the int represented by the string, or the default if conversion fails + * @deprecated Use {@link #toInt(String, int)} + * This method will be removed in Commons Lang 3.0 */ public static int stringToInt(String str, int defaultValue) { + return toInt(str, defaultValue); + } + + /** + *

        Convert a String to an int, returning a + * default value if the conversion fails.

        + * + *

        If the string is null, the default value is returned.

        + * + *
        +     *   NumberUtils.toInt(null, 1) = 1
        +     *   NumberUtils.toInt("", 1)   = 1
        +     *   NumberUtils.toInt("1", 0)  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 2.1 + */ + public static int toInt(String str, int defaultValue) { + if(str == null) { + return defaultValue; + } try { return Integer.parseInt(str); } catch (NumberFormatException nfe) { return defaultValue; } } + /** + *

        Convert a String to a long, returning + * zero if the conversion fails.

        + * + *

        If the string is null, zero is returned.

        + * + *
        +     *   NumberUtils.toLong(null) = 0L
        +     *   NumberUtils.toLong("")   = 0L
        +     *   NumberUtils.toLong("1")  = 1L
        +     * 
        + * + * @param str the string to convert, may be null + * @return the long represented by the string, or 0 if + * conversion fails + * @since 2.1 + */ + public static long toLong(String str) { + return toLong(str, 0L); + } + + /** + *

        Convert a String to a long, returning a + * default value if the conversion fails.

        + * + *

        If the string is null, the default value is returned.

        + * + *
        +     *   NumberUtils.toLong(null, 1L) = 1L
        +     *   NumberUtils.toLong("", 1L)   = 1L
        +     *   NumberUtils.toLong("1", 0L)  = 1L
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the long represented by the string, or the default if conversion fails + * @since 2.1 + */ + public static long toLong(String str, long defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Long.parseLong(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

        Convert a String to a float, returning + * 0.0f if the conversion fails.

        + * + *

        If the string str is null, + * 0.0f is returned.

        + * + *
        +     *   NumberUtils.toFloat(null)   = 0.0f
        +     *   NumberUtils.toFloat("")     = 0.0f
        +     *   NumberUtils.toFloat("1.5")  = 1.5f
        +     * 
        + * + * @param str the string to convert, may be null + * @return the float represented by the string, or 0.0f + * if conversion fails + * @since 2.1 + */ + public static float toFloat(String str) { + return toFloat(str, 0.0f); + } + + /** + *

        Convert a String to a float, returning a + * default value if the conversion fails.

        + * + *

        If the string str is null, the default + * value is returned.

        + * + *
        +     *   NumberUtils.toFloat(null, 1.1f)   = 1.0f
        +     *   NumberUtils.toFloat("", 1.1f)     = 1.1f
        +     *   NumberUtils.toFloat("1.5", 0.0f)  = 1.5f
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the float represented by the string, or defaultValue + * if conversion fails + * @since 2.1 + */ + public static float toFloat(String str, float defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Float.parseFloat(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

        Convert a String to a double, returning + * 0.0d if the conversion fails.

        + * + *

        If the string str is null, + * 0.0d is returned.

        + * + *
        +     *   NumberUtils.toDouble(null)   = 0.0d
        +     *   NumberUtils.toDouble("")     = 0.0d
        +     *   NumberUtils.toDouble("1.5")  = 1.5d
        +     * 
        + * + * @param str the string to convert, may be null + * @return the double represented by the string, or 0.0d + * if conversion fails + * @since 2.1 + */ + public static double toDouble(String str) { + return toDouble(str, 0.0d); + } + + /** + *

        Convert a String to a double, returning a + * default value if the conversion fails.

        + * + *

        If the string str is null, the default + * value is returned.

        + * + *
        +     *   NumberUtils.toDouble(null, 1.1d)   = 1.1d
        +     *   NumberUtils.toDouble("", 1.1d)     = 1.1d
        +     *   NumberUtils.toDouble("1.5", 0.0d)  = 1.5d
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the double represented by the string, or defaultValue + * if conversion fails + * @since 2.1 + */ + public static double toDouble(String str, double defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Double.parseDouble(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + //----------------------------------------------------------------------- + /** + *

        Convert a String to a byte, returning + * zero if the conversion fails.

        + * + *

        If the string is null, zero is returned.

        + * + *
        +     *   NumberUtils.toByte(null) = 0
        +     *   NumberUtils.toByte("")   = 0
        +     *   NumberUtils.toByte("1")  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @return the byte represented by the string, or zero if + * conversion fails + * @since 2.5 + */ + public static byte toByte(String str) { + return toByte(str, (byte) 0); + } + + /** + *

        Convert a String to a byte, returning a + * default value if the conversion fails.

        + * + *

        If the string is null, the default value is returned.

        + * + *
        +     *   NumberUtils.toByte(null, 1) = 1
        +     *   NumberUtils.toByte("", 1)   = 1
        +     *   NumberUtils.toByte("1", 0)  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the byte represented by the string, or the default if conversion fails + * @since 2.5 + */ + public static byte toByte(String str, byte defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Byte.parseByte(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

        Convert a String to a short, returning + * zero if the conversion fails.

        + * + *

        If the string is null, zero is returned.

        + * + *
        +     *   NumberUtils.toShort(null) = 0
        +     *   NumberUtils.toShort("")   = 0
        +     *   NumberUtils.toShort("1")  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @return the short represented by the string, or zero if + * conversion fails + * @since 2.5 + */ + public static short toShort(String str) { + return toShort(str, (short) 0); + } + + /** + *

        Convert a String to an short, returning a + * default value if the conversion fails.

        + * + *

        If the string is null, the default value is returned.

        + * + *
        +     *   NumberUtils.toShort(null, 1) = 1
        +     *   NumberUtils.toShort("", 1)   = 1
        +     *   NumberUtils.toShort("1", 0)  = 1
        +     * 
        + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the short represented by the string, or the default if conversion fails + * @since 2.5 + */ + public static short toShort(String str, short defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Short.parseShort(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + //----------------------------------------------------------------------- // must handle Long, Float, Integer, Float, Short, // BigDecimal, BigInteger and Byte @@ -243,7 +520,7 @@ if (decPos > -1) { if (expPos > -1) { - if (expPos < decPos) { + if (expPos < decPos || expPos > str.length()) { throw new NumberFormatException(str + " is not a valid number."); } dec = str.substring(decPos + 1, expPos); @@ -253,13 +530,16 @@ mant = str.substring(0, decPos); } else { if (expPos > -1) { + if (expPos > str.length()) { + throw new NumberFormatException(str + " is not a valid number."); + } mant = str.substring(0, expPos); } else { mant = str; } dec = null; } - if (!Character.isDigit(lastChar)) { + if (!Character.isDigit(lastChar) && lastChar != '.') { if (expPos > -1 && expPos < str.length() - 1) { exp = str.substring(expPos + 1, str.length() - 1); } else { @@ -273,8 +553,7 @@ case 'L' : if (dec == null && exp == null - && isDigits(numeric.substring(1)) - && (numeric.charAt(0) == '-' || Character.isDigit(numeric.charAt(0)))) { + && (numeric.charAt(0) == '-' && isDigits(numeric.substring(1)) || isDigits(numeric))) { try { return createLong(numeric); } catch (NumberFormatException nfe) { @@ -290,13 +569,14 @@ Float f = NumberUtils.createFloat(numeric); if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) { //If it's too big for a float or the float value = 0 and the string - //has non-zeros in it, then float doens't have the presision we want + //has non-zeros in it, then float does not have the precision we want return f; } } catch (NumberFormatException nfe) { + // ignore the bad number } - //Fall through + //$FALL-THROUGH$ case 'd' : case 'D' : try { @@ -305,12 +585,14 @@ return d; } } catch (NumberFormatException nfe) { + // ignore the bad number } try { return createBigDecimal(numeric); } catch (NumberFormatException e) { + // ignore the bad number } - //Fall through + //$FALL-THROUGH$ default : throw new NumberFormatException(str + " is not a valid number."); @@ -328,10 +610,12 @@ try { return createInteger(str); } catch (NumberFormatException nfe) { + // ignore the bad number } try { return createLong(str); } catch (NumberFormatException nfe) { + // ignore the bad number } return createBigInteger(str); @@ -344,13 +628,15 @@ return f; } } catch (NumberFormatException nfe) { + // ignore the bad number } try { Double d = createDouble(str); if (!(d.isInfinite() || (d.doubleValue() == 0.0D && !allZeros))) { return d; } } catch (NumberFormatException nfe) { + // ignore the bad number } return createBigDecimal(str); @@ -565,13 +851,41 @@ return min; } + /** + *

        Returns the minimum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static byte min(byte[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + byte min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + /** *

        Returns the minimum value in an array.

        * * @param array an array, must not be null or empty * @return the minimum value in the array * @throws IllegalArgumentException if array is null * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#min(double[]) IEEE754rUtils for a version of this method that handles NaN differently */ public static double min(double[] array) { // Validates input @@ -584,6 +898,9 @@ // Finds and returns min double min = array[0]; for (int i = 1; i < array.length; i++) { + if (Double.isNaN(array[i])) { + return Double.NaN; + } if (array[i] < min) { min = array[i]; } @@ -599,6 +916,7 @@ * @return the minimum value in the array * @throws IllegalArgumentException if array is null * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#min(float[]) IEEE754rUtils for a version of this method that handles NaN differently */ public static float min(float[] array) { // Validates input @@ -611,6 +929,9 @@ // Finds and returns min float min = array[0]; for (int i = 1; i < array.length; i++) { + if (Float.isNaN(array[i])) { + return Float.NaN; + } if (array[i] < min) { min = array[i]; } @@ -710,6 +1031,34 @@ * @throws IllegalArgumentException if array is null * @throws IllegalArgumentException if array is empty */ + public static byte max(byte[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + byte max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + /** + *

        Returns the maximum value in an array.

        + * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#max(double[]) IEEE754rUtils for a version of this method that handles NaN differently + */ public static double max(double[] array) { // Validates input if (array== null) { @@ -721,6 +1070,9 @@ // Finds and returns max double max = array[0]; for (int j = 1; j < array.length; j++) { + if (Double.isNaN(array[j])) { + return Double.NaN; + } if (array[j] > max) { max = array[j]; } @@ -736,6 +1088,7 @@ * @return the minimum value in the array * @throws IllegalArgumentException if array is null * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#max(float[]) IEEE754rUtils for a version of this method that handles NaN differently */ public static float max(float[] array) { // Validates input @@ -748,6 +1101,9 @@ // Finds and returns max float max = array[0]; for (int j = 1; j < array.length; j++) { + if (Float.isNaN(array[j])) { + return Float.NaN; + } if (array[j] > max) { max = array[j]; } @@ -840,6 +1196,7 @@ * @param b value 2 * @param c value 3 * @return the smallest of the values + * @see IEEE754rUtils#min(double, double, double) for a version of this method that handles NaN differently */ public static double min(double a, double b, double c) { return Math.min(Math.min(a, b), c); @@ -855,6 +1212,7 @@ * @param b value 2 * @param c value 3 * @return the smallest of the values + * @see IEEE754rUtils#min(float, float, float) for a version of this method that handles NaN differently */ public static float min(float a, float b, float c) { return Math.min(Math.min(a, b), c); @@ -944,6 +1302,7 @@ * @param b value 2 * @param c value 3 * @return the largest of the values + * @see IEEE754rUtils#max(double, double, double) for a version of this method that handles NaN differently */ public static double max(double a, double b, double c) { return Math.max(Math.max(a, b), c); @@ -959,6 +1318,7 @@ * @param b value 2 * @param c value 3 * @return the largest of the values + * @see IEEE754rUtils#max(float, float, float) for a version of this method that handles NaN differently */ public static float max(float a, float b, float c) { return Math.max(Math.max(a, b), c); @@ -982,7 +1342,7 @@ *
      • NaN *
      • Positive infinity *
      • Maximum double - *
      • Normal positve numbers + *
      • Normal positive numbers *
      • +0.0 *
      • -0.0 *
      • Normal negative numbers @@ -1031,7 +1391,7 @@ /** *

        Compares two floats for order.

        * - *

        This method is more comprhensive than the standard Java greater than, + *

        This method is more comprehensive than the standard Java greater than, * less than and equals operators.

        *
          *
        • It returns -1 if the first value is less than the second. @@ -1044,7 +1404,7 @@ *
        • NaN *
        • Positive infinity *
        • Maximum float - *
        • Normal positve numbers + *
        • Normal positive numbers *
        • +0.0 *
        • -0.0 *
        • Normal negative numbers @@ -1101,7 +1461,7 @@ * @return true if str contains only unicode numeric */ public static boolean isDigits(String str) { - if ((str == null) || (str.length() == 0)) { + if (StringUtils.isEmpty(str)) { return false; } for (int i = 0; i < str.length(); i++) { @@ -1126,7 +1486,7 @@ * @return true if the string is a correctly formatted number */ public static boolean isNumber(String str) { - if ((str == null) || (str.length() == 0)) { + if (StringUtils.isEmpty(str)) { return false; } char[] chars = str.toCharArray(); @@ -1201,6 +1561,14 @@ // can't have an E at the last byte return false; } + if (chars[i] == '.') { + if (hasDecPoint || hasExp) { + // two decimal points or dec in exponent + return false; + } + // single trailing decimal point after non-exponent is ok + return foundDigit; + } if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' @@ -1210,7 +1578,7 @@ } if (chars[i] == 'l' || chars[i] == 'L') { - // not allowing L with an exponoent + // not allowing L with an exponent return foundDigit && !hasExp; } // last character is illegal Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/RandomUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/RandomUtils.java (.../RandomUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/RandomUtils.java (.../RandomUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,55 +1,20 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * Copyright (c) 2002 The Apache Software Foundation. All rights - * reserved. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package org.apache.commons.lang.math; @@ -60,12 +25,15 @@ * {@link java.util.Random} methods via the {@link java.lang.Math#random()} * method and its system-wide Random object. * - * @author Henri Yandell + * @author Gary D. Gregory * @since 2.0 * @version $Id$ */ public class RandomUtils { + /** + * An instance of {@link JVMRandom}. + */ public static final Random JVM_RANDOM = new JVMRandom(); // should be possible for JVM_RANDOM? @@ -76,43 +44,71 @@ /** *

          Returns the next pseudorandom, uniformly distributed int value * from the Math.random() sequence.

          - * + * N.B. All values are >= 0. * @return the random int */ public static int nextInt() { return nextInt(JVM_RANDOM); } - public static int nextInt(Random rnd) { - return rnd.nextInt(); + + /** + *

          Returns the next pseudorandom, uniformly distributed int value + * from the given random sequence.

          + * + * @param random the Random sequence generator. + * @return the random int + */ + public static int nextInt(Random random) { + return random.nextInt(); } + /** *

          Returns a pseudorandom, uniformly distributed int value * between 0 (inclusive) and the specified value * (exclusive), from the Math.random() sequence.

          * * @param n the specified exclusive max-value - * * @return the random int */ public static int nextInt(int n) { return nextInt(JVM_RANDOM, n); } - public static int nextInt(Random rnd, int n) { + + /** + *

          Returns a pseudorandom, uniformly distributed int value + * between 0 (inclusive) and the specified value + * (exclusive), from the given Random sequence.

          + * + * @param random the Random sequence generator. + * @param n the specified exclusive max-value + * @return the random int + */ + public static int nextInt(Random random, int n) { // check this cannot return 'n' - return rnd.nextInt(n); + return random.nextInt(n); } + /** *

          Returns the next pseudorandom, uniformly distributed long value * from the Math.random() sequence.

          - * + * N.B. All values are >= 0. * @return the random long */ public static long nextLong() { return nextLong(JVM_RANDOM); } - public static long nextLong(Random rnd) { - return rnd.nextLong(); + + /** + *

          Returns the next pseudorandom, uniformly distributed long value + * from the given Random sequence.

          + * + * @param random the Random sequence generator. + * @return the random long + */ + public static long nextLong(Random random) { + return random.nextLong(); } + /** *

          Returns the next pseudorandom, uniformly distributed boolean value * from the Math.random() sequence.

          @@ -122,9 +118,18 @@ public static boolean nextBoolean() { return nextBoolean(JVM_RANDOM); } - public static boolean nextBoolean(Random rnd) { - return rnd.nextBoolean(); + + /** + *

          Returns the next pseudorandom, uniformly distributed boolean value + * from the given random sequence.

          + * + * @param random the Random sequence generator. + * @return the random boolean + */ + public static boolean nextBoolean(Random random) { + return random.nextBoolean(); } + /** *

          Returns the next pseudorandom, uniformly distributed float value * between 0.0 and 1.0 from the Math.random() @@ -135,19 +140,40 @@ public static float nextFloat() { return nextFloat(JVM_RANDOM); } - public static float nextFloat(Random rnd) { - return rnd.nextFloat(); + + /** + *

          Returns the next pseudorandom, uniformly distributed float value + * between 0.0 and 1.0 from the given Random + * sequence.

          + * + * @param random the Random sequence generator. + * @return the random float + */ + public static float nextFloat(Random random) { + return random.nextFloat(); } + /** - *

          Synonymous to the Math.random() call.

          + *

          Returns the next pseudorandom, uniformly distributed float value + * between 0.0 and 1.0 from the Math.random() + * sequence.

          * * @return the random double */ public static double nextDouble() { return nextDouble(JVM_RANDOM); } - public static double nextDouble(Random rnd) { - return rnd.nextDouble(); + + /** + *

          Returns the next pseudorandom, uniformly distributed float value + * between 0.0 and 1.0 from the given Random + * sequence.

          + * + * @param random the Random sequence generator. + * @return the random double + */ + public static double nextDouble(Random random) { + return random.nextDouble(); } } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Range.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Range.java (.../Range.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/Range.java (.../Range.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,66 +1,31 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.math; +import org.apache.commons.lang.text.StrBuilder; + /** *

          Range represents a range of numbers of the same type.

          * *

          Specific subclasses hold the range values as different types. Each * subclass should be immutable and {@link java.io.Serializable Serializable} * if possible.

          * - * @author Stephen Colebourne + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ @@ -236,7 +201,7 @@ * range by long comparison */ public boolean containsLong(long value) { - return (value >= getMinimumLong() && value <= getMaximumLong()); + return value >= getMinimumLong() && value <= getMaximumLong(); } /** @@ -270,7 +235,7 @@ * range by int comparison */ public boolean containsInteger(int value) { - return (value >= getMinimumInteger() && value <= getMaximumInteger()); + return value >= getMinimumInteger() && value <= getMaximumInteger(); } /** @@ -306,7 +271,7 @@ public boolean containsDouble(double value) { int compareMin = NumberUtils.compare(getMinimumDouble(), value); int compareMax = NumberUtils.compare(getMaximumDouble(), value); - return (compareMin <= 0 && compareMax >= 0); + return compareMin <= 0 && compareMax >= 0; } /** @@ -342,7 +307,7 @@ public boolean containsFloat(float value) { int compareMin = NumberUtils.compare(getMinimumFloat(), value); int compareMax = NumberUtils.compare(getMaximumFloat(), value); - return (compareMin <= 0 && compareMax >= 0); + return compareMin <= 0 && compareMax >= 0; } // Range tests @@ -456,7 +421,7 @@ * @return the String representation of this range */ public String toString() { - StringBuffer buf = new StringBuffer(32); + StrBuilder buf = new StrBuilder(32); buf.append("Range["); buf.append(getMinimumNumber()); buf.append(','); Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/math/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/math/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/math/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,7 +1,25 @@ + -Extends java.math for business mathematical classes. This package is intended for business -mathematical classes, not scientific ones. +Extends {@link java.math} for business mathematical classes. This package is intended for business +mathematical use, not scientific use. See Commons Math +for a more complete set of mathematical classes. @since 2.0 +

          These classes are immutable, and therefore thread-safe.

          Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/Mutable.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/Mutable.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/Mutable.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang.mutable; + +/** + * Provides mutable access to a value. + *

          + * Mutable is used as a generic interface to the implementations in this package. + *

          + * A typical use case would be to enable a primitive or string to be passed to a method and allow that method to + * effectively change the value of the primitive/string. Another use case is to store a frequently changing primitive in + * a collection (for example a total in a map) without needing to create new Integer/Long wrapper objects. + * + * @author Apache Software Foundation + * @author Matthew Hawthorne + * @since 2.1 + * @version $Id$ + */ +public interface Mutable { + + /** + * Gets the value of this mutable. + * + * @return the stored value + */ + Object getValue(); + + /** + * Sets the value of this mutable. + * + * @param value + * the value to store + * @throws NullPointerException + * if the object is null and null is invalid + * @throws ClassCastException + * if the type is invalid + */ + void setValue(Object value); + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableBoolean.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableBoolean.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableBoolean.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang.mutable; + +import java.io.Serializable; + +import org.apache.commons.lang.BooleanUtils; + +/** + * A mutable boolean wrapper. + * + * @see Boolean + * @since 2.2 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableBoolean implements Mutable, Serializable, Comparable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -4830728138360036487L; + + /** The mutable value. */ + private boolean value; + + /** + * Constructs a new MutableBoolean with the default value of false. + */ + public MutableBoolean() { + super(); + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store + */ + public MutableBoolean(boolean value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableBoolean(Boolean value) { + super(); + this.value = value.booleanValue(); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Boolean instance. + * + * @return the value as a Boolean, never null + */ + public Object getValue() { + return BooleanUtils.toBooleanObject(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(boolean value) { + this.value = value; + } + + /** + * Sets the value from any Boolean instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Object value) { + setValue(((Boolean) value).booleanValue()); + } + + //----------------------------------------------------------------------- + /** + * Checks if the current value is true. + * + * @return true if the current value is true + * @since 2.5 + */ + public boolean isTrue() { + return value == true; + } + + /** + * Checks if the current value is false. + * + * @return true if the current value is false + * @since 2.5 + */ + public boolean isFalse() { + return value == false; + } + + //----------------------------------------------------------------------- + /** + * Returns the value of this MutableBoolean as a boolean. + * + * @return the boolean value represented by this object. + */ + public boolean booleanValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Boolean. + * + * @return a Boolean instance containing the value from this mutable, never null + * @since 2.5 + */ + public Boolean toBoolean() { + return BooleanUtils.toBooleanObject(this.value); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is an MutableBoolean object that contains the same + * boolean value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableBoolean) { + return value == ((MutableBoolean) obj).booleanValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return the hash code returned by Boolean.TRUE or Boolean.FALSE + */ + public int hashCode() { + return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * where false is less than true + */ + public int compareTo(Object obj) { + MutableBoolean other = (MutableBoolean) obj; + boolean anotherVal = other.value; + return value == anotherVal ? 0 : (value ? 1 : -1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableByte.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableByte.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableByte.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +/** + * A mutable byte wrapper. + * + * @see Byte + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableByte extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -1585823265L; + + /** The mutable value. */ + private byte value; + + /** + * Constructs a new MutableByte with the default value of zero. + */ + public MutableByte() { + super(); + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store + */ + public MutableByte(byte value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableByte(Number value) { + super(); + this.value = value.byteValue(); + } + + /** + * Constructs a new MutableByte parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a byte + * @since 2.5 + */ + public MutableByte(String value) throws NumberFormatException { + super(); + this.value = Byte.parseByte(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Byte instance. + * + * @return the value as a Byte, never null + */ + public Object getValue() { + return new Byte(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(byte value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).byteValue()); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(byte operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.byteValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(byte operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.byteValue(); + } + + //----------------------------------------------------------------------- + // shortValue relies on Number implementation + /** + * Returns the value of this MutableByte as a byte. + * + * @return the numeric value represented by this object after conversion to type byte. + */ + public byte byteValue() { + return value; + } + + /** + * Returns the value of this MutableByte as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Byte. + * + * @return a Byte instance containing the value from this mutable + */ + public Byte toByte() { + return new Byte(byteValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is a MutableByte object that contains the same byte value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableByte) { + return value == ((MutableByte) obj).byteValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * @throws ClassCastException if the argument is not a MutableByte + */ + public int compareTo(Object obj) { + MutableByte other = (MutableByte) obj; + byte anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableDouble.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableDouble.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableDouble.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +import org.apache.commons.lang.math.NumberUtils; + +/** + * A mutable double wrapper. + * + * @see Double + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableDouble extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1587163916L; + + /** The mutable value. */ + private double value; + + /** + * Constructs a new MutableDouble with the default value of zero. + */ + public MutableDouble() { + super(); + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store + */ + public MutableDouble(double value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableDouble(Number value) { + super(); + this.value = value.doubleValue(); + } + + /** + * Constructs a new MutableDouble parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a double + * @since 2.5 + */ + public MutableDouble(String value) throws NumberFormatException { + super(); + this.value = Double.parseDouble(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Double instance. + * + * @return the value as a Double, never null + */ + public Object getValue() { + return new Double(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(double value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).doubleValue()); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the double value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Double.isNaN(value); + } + + /** + * Checks whether the double value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Double.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add + * @since Commons Lang 2.2 + */ + public void add(double operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.doubleValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(double operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.doubleValue(); + } + + //----------------------------------------------------------------------- + // shortValue and bytValue rely on Number implementation + /** + * Returns the value of this MutableDouble as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableDouble as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableDouble as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return (float) value; + } + + /** + * Returns the value of this MutableDouble as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Double. + * + * @return a Double instance containing the value from this mutable, never null + */ + public Double toDouble() { + return new Double(doubleValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against the specified object. The result is true if and only if the argument + * is not null and is a Double object that represents a double that has the identical + * bit pattern to the bit pattern of the double represented by this object. For this purpose, two + * double values are considered to be the same if and only if the method + * {@link Double#doubleToLongBits(double)}returns the same long value when applied to each. + *

          + * Note that in most cases, for two instances of class Double,d1 and d2, + * the value of d1.equals(d2) is true if and only if

          + * + *
          +     *   d1.doubleValue() == d2.doubleValue()
          +     * 
          + * + *
          + *

          + * also has the value true. However, there are two exceptions: + *

            + *
          • If d1 and d2 both represent Double.NaN, then the + * equals method returns true, even though Double.NaN==Double.NaN has + * the value false. + *
          • If d1 represents +0.0 while d2 represents -0.0, + * or vice versa, the equal test has the value false, even though + * +0.0==-0.0 has the value true. This allows hashtables to operate properly. + *
          + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + return (obj instanceof MutableDouble) + && (Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value)); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + long bits = Double.doubleToLongBits(value); + return (int) (bits ^ (bits >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * @throws ClassCastException if the argument is not a MutableDouble + */ + public int compareTo(Object obj) { + MutableDouble other = (MutableDouble) obj; + double anotherVal = other.value; + return NumberUtils.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableFloat.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableFloat.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableFloat.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,309 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +import org.apache.commons.lang.math.NumberUtils; + +/** + * A mutable float wrapper. + * + * @see Float + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableFloat extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 5787169186L; + + /** The mutable value. */ + private float value; + + /** + * Constructs a new MutableFloat with the default value of zero. + */ + public MutableFloat() { + super(); + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store + */ + public MutableFloat(float value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableFloat(Number value) { + super(); + this.value = value.floatValue(); + } + + /** + * Constructs a new MutableFloat parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a float + * @since 2.5 + */ + public MutableFloat(String value) throws NumberFormatException { + super(); + this.value = Float.parseFloat(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Float instance. + * + * @return the value as a Float, never null + */ + public Object getValue() { + return new Float(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(float value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).floatValue()); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the float value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Float.isNaN(value); + } + + /** + * Checks whether the float value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Float.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(float operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.floatValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract + * @since Commons Lang 2.2 + */ + public void subtract(float operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.floatValue(); + } + + //----------------------------------------------------------------------- + // shortValue and bytValue rely on Number implementation + /** + * Returns the value of this MutableFloat as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableFloat as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableFloat as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableFloat as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Float. + * + * @return a Float instance containing the value from this mutable, never null + */ + public Float toFloat() { + return new Float(floatValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against some other object. The result is true if and only if the argument is + * not null and is a Float object that represents a float that has the + * identical bit pattern to the bit pattern of the float represented by this object. For this + * purpose, two float values are considered to be the same if and only if the method + * {@link Float#floatToIntBits(float)}returns the same int value when applied to each. + *

          + * Note that in most cases, for two instances of class Float,f1 and f2, + * the value of f1.equals(f2) is true if and only if

          + * + *
          +     *   f1.floatValue() == f2.floatValue()
          +     * 
          + * + *
          + *

          + * also has the value true. However, there are two exceptions: + *

            + *
          • If f1 and f2 both represent Float.NaN, then the + * equals method returns true, even though Float.NaN==Float.NaN has + * the value false. + *
          • If f1 represents +0.0f while f2 represents -0.0f, + * or vice versa, the equal test has the value false, even though + * 0.0f==-0.0f has the value true. + *
          + * This definition allows hashtables to operate properly. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + * @see java.lang.Float#floatToIntBits(float) + */ + public boolean equals(Object obj) { + return (obj instanceof MutableFloat) + && (Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value)); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + return Float.floatToIntBits(value); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(Object obj) { + MutableFloat other = (MutableFloat) obj; + float anotherVal = other.value; + return NumberUtils.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableInt.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableInt.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableInt.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +/** + * A mutable int wrapper. + * + * @see Integer + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableInt extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 512176391864L; + + /** The mutable value. */ + private int value; + + /** + * Constructs a new MutableInt with the default value of zero. + */ + public MutableInt() { + super(); + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store + */ + public MutableInt(int value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableInt(Number value) { + super(); + this.value = value.intValue(); + } + + /** + * Constructs a new MutableInt parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into an int + * @since 2.5 + */ + public MutableInt(String value) throws NumberFormatException { + super(); + this.value = Integer.parseInt(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Integer instance. + * + * @return the value as a Integer, never null + */ + public Object getValue() { + return new Integer(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(int value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).intValue()); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(int operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.intValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(int operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.intValue(); + } + + //----------------------------------------------------------------------- + // shortValue and bytValue rely on Number implementation + /** + * Returns the value of this MutableInt as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Integer. + * + * @return a Integer instance containing the value from this mutable, never null + */ + public Integer toInteger() { + return new Integer(intValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is a MutableInt object that contains the same int value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableInt) { + return value == ((MutableInt) obj).intValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * @throws ClassCastException if the argument is not a MutableInt + */ + public int compareTo(Object obj) { + MutableInt other = (MutableInt) obj; + int anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableLong.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableLong.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableLong.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +/** + * A mutable long wrapper. + * + * @see Long + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableLong extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 62986528375L; + + /** The mutable value. */ + private long value; + + /** + * Constructs a new MutableLong with the default value of zero. + */ + public MutableLong() { + super(); + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store + */ + public MutableLong(long value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableLong(Number value) { + super(); + this.value = value.longValue(); + } + + /** + * Constructs a new MutableLong parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a long + * @since 2.5 + */ + public MutableLong(String value) throws NumberFormatException { + super(); + this.value = Long.parseLong(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Long instance. + * + * @return the value as a Long, never null + */ + public Object getValue() { + return new Long(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(long value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).longValue()); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(long operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.longValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(long operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.longValue(); + } + + //----------------------------------------------------------------------- + // shortValue and bytValue rely on Number implementation + /** + * Returns the value of this MutableLong as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableLong as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Long. + * + * @return a Long instance containing the value from this mutable, never null + */ + public Long toLong() { + return new Long(longValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument + * is not null and is a MutableLong object that contains the same long + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableLong) { + return value == ((MutableLong) obj).longValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + return (int) (value ^ (value >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * @throws ClassCastException if the argument is not a MutableLong + */ + public int compareTo(Object obj) { + MutableLong other = (MutableLong) obj; + long anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableObject.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableObject.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableObject.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang.mutable; + +import java.io.Serializable; + +/** + * A mutable Object wrapper. + * + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableObject implements Mutable, Serializable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 86241875189L; + + /** The mutable value. */ + private Object value; + + /** + * Constructs a new MutableObject with the default value of null. + */ + public MutableObject() { + super(); + } + + /** + * Constructs a new MutableObject with the specified value. + * + * @param value the initial value to store + */ + public MutableObject(Object value) { + super(); + this.value = value; + } + + //----------------------------------------------------------------------- + /** + * Gets the value. + * + * @return the value, may be null + */ + public Object getValue() { + return this.value; + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(Object value) { + this.value = value; + } + + //----------------------------------------------------------------------- + /** + * Compares this object against the specified object. The result is true if and only if the argument + * is not null and is a MutableObject object that contains the same Object + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableObject) { + Object other = ((MutableObject) obj).value; + return value == other || (value != null && value.equals(other)); + } + return false; + } + + /** + * Returns the value's hash code or 0 if the value is null. + * + * @return the value's hash code or 0 if the value is null. + */ + public int hashCode() { + return value == null ? 0 : value.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return value == null ? "null" : value.toString(); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableShort.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableShort.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/MutableShort.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.mutable; + +/** + * A mutable short wrapper. + * + * @see Short + * @since 2.1 + * @author Apache Software Foundation + * @version $Id$ + */ +public class MutableShort extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -2135791679L; + + /** The mutable value. */ + private short value; + + /** + * Constructs a new MutableShort with the default value of zero. + */ + public MutableShort() { + super(); + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store + */ + public MutableShort(short value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableShort(Number value) { + super(); + this.value = value.shortValue(); + } + + /** + * Constructs a new MutableShort parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a short + * @since 2.5 + */ + public MutableShort(String value) throws NumberFormatException { + super(); + this.value = Short.parseShort(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Short instance. + * + * @return the value as a Short, never null + */ + public Object getValue() { + return new Short(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(short value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + * @throws ClassCastException if the type is not a {@link Number} + */ + public void setValue(Object value) { + setValue(((Number) value).shortValue()); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(short operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.shortValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(short operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.shortValue(); + } + + //----------------------------------------------------------------------- + // bytValue relies on Number implementation + /** + * Returns the value of this MutableShort as a short. + * + * @return the numeric value represented by this object after conversion to type short. + */ + public short shortValue() { + return value; + } + + /** + * Returns the value of this MutableShort as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Short. + * + * @return a Short instance containing the value from this mutable, never null + */ + public Short toShort() { + return new Short(shortValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument + * is not null and is a MutableShort object that contains the same short + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof MutableShort) { + return value == ((MutableShort) obj).shortValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param obj the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * @throws ClassCastException if the argument is not a MutableShort + */ + public int compareTo(Object obj) { + MutableShort other = (MutableShort) obj; + short anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + public String toString() { + return String.valueOf(value); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/package.html =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/package.html (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/mutable/package.html (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,29 @@ + + + + + + + +Provides typed mutable wrappers to primitive values and Object. +@since 2.1 +

          These classes are not thread-safe.

          + + Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/overview.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/overview.html (.../overview.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/overview.html (.../overview.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,7 +1,23 @@ +

          -This document is the API specification for the Apache Jakarta Commons Lang Library, version 2.0. +This document is the API specification for the Apache Commons Lang library.

          - \ No newline at end of file + Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,7 +1,25 @@ + Provides highly reusable static utility methods, chiefly concerned -with adding value to java.lang and other standard core classes. +with adding value to the {@link java.lang} classes. @since 1.0 +

          Most of these classes are immutable and thus thread-safe. +However Charset is not currently guaranteed thread-safe under all circumstances.

          Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/ConstructorUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/ConstructorUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/ConstructorUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.ClassUtils; + +/** + *

          Utility reflection methods focussed on constructors, modelled after + * {@link MethodUtils}.

          + * + *

          Known Limitations

          Accessing Public Constructors In A Default + * Access Superclass

          There is an issue when invoking public constructors + * contained in a default access superclass. Reflection locates these + * constructors fine and correctly assigns them as public. However, an + * IllegalAccessException is thrown if the constructors is + * invoked.

          + * + *

          ConstructorUtils contains a workaround for this situation. It + * will attempt to call setAccessible on this constructor. If this + * call succeeds, then the method can be invoked as normal. This call will only + * succeed when the application has sufficient security privilages. If this call + * fails then a warning will be logged and the method may fail.

          + * + * @author Apache Software Foundation + * @author Craig R. McClanahan + * @author Ralph Schaer + * @author Chris Audley + * @author Rey Francois + * @author Gregor Rayman + * @author Jan Sorensen + * @author Robert Burrell Donkin + * @author Rodney Waldhoff + * @since 2.5 + * @version $Id$ + */ +public class ConstructorUtils { + + /** + *

          ConstructorUtils instances should NOT be constructed in standard + * programming. Instead, the class should be used as + * ConstructorUtils.invokeConstructor(cls, args).

          + * + *

          This constructor is public to permit tools that require a JavaBean + * instance to operate.

          + */ + public ConstructorUtils() { + super(); + } + + /** + *

          Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

          + * + *

          The signatures should be assignment compatible.

          + * + * @param cls the class to be constructed. + * @param arg the actual argument + * @return new instance of klazz + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + */ + public static Object invokeConstructor(Class cls, Object arg) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException, InstantiationException { + return invokeConstructor(cls, new Object[] { arg }); + } + + /** + *

          Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

          + *

          The signatures should be assignment compatible.

          + * + * @param cls the class to be constructed. + * @param args actual argument array + * @return new instance of klazz + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the + * constructor + * @throws InvocationTargetException If an error occurs invoking the + * constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeConstructor(java.lang.Class, java.lang.Object[], + * java.lang.Class[]) + */ + public static Object invokeConstructor(Class cls, Object[] args) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (null == args) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Class parameterTypes[] = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeConstructor(cls, args, parameterTypes); + } + + /** + *

          Returns new instance of klazz created using constructor + * with signature parameterTypes and actual arguments + * args.

          + * + *

          The signatures should be assignment compatible.

          + * + * @param cls the class to be constructed. + * @param args actual argument array + * @param parameterTypes parameter types array + * @return new instance of klazz + * + * @throws NoSuchMethodException if matching constructor cannot be found + * @throws IllegalAccessException thrown on the constructor's invocation + * @throws InvocationTargetException thrown on the constructor's invocation + * @throws InstantiationException thrown on the constructor's invocation + * @see Constructor#newInstance + */ + public static Object invokeConstructor(Class cls, Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes); + if (null == ctor) { + throw new NoSuchMethodException("No such accessible constructor on object: " + + cls.getName()); + } + return ctor.newInstance(args); + } + + /** + *

          Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

          + * + *

          The signatures should match exactly.

          + * + * @param cls the class to be constructed. + * @param arg the actual argument + * @return new instance of klazz + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + */ + public static Object invokeExactConstructor(Class cls, Object arg) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException, InstantiationException { + return invokeExactConstructor(cls, new Object[] { arg }); + } + + /** + *

          Returns new instance of klazz created using the actual + * arguments args. The formal parameter types are inferred from + * the actual values of args. See + * {@link #invokeExactConstructor(Class, Object[], Class[])} for more + * details.

          + * + *

          The signatures should match exactly.

          + * + * @param cls the class to be constructed. + * @param args actual argument array + * @return new instance of klazz + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the + * constructor + * @throws InvocationTargetException If an error occurs invoking the + * constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], + * java.lang.Class[]) + */ + public static Object invokeExactConstructor(Class cls, Object[] args) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (null == args) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class parameterTypes[] = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactConstructor(cls, args, parameterTypes); + } + + /** + *

          Returns new instance of klazz created using constructor + * with signature parameterTypes and actual arguments + * args.

          + * + *

          The signatures should match exactly.

          + * + * @param cls the class to be constructed. + * @param args actual argument array + * @param parameterTypes parameter types array + * @return new instance of klazz + * + * @throws NoSuchMethodException if matching constructor cannot be found + * @throws IllegalAccessException thrown on the constructor's invocation + * @throws InvocationTargetException thrown on the constructor's invocation + * @throws InstantiationException thrown on the constructor's invocation + * @see Constructor#newInstance + */ + public static Object invokeExactConstructor(Class cls, Object[] args, + Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException, InstantiationException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Constructor ctor = getAccessibleConstructor(cls, parameterTypes); + if (null == ctor) { + throw new NoSuchMethodException("No such accessible constructor on object: " + + cls.getName()); + } + return ctor.newInstance(args); + } + + /** + * Returns a constructor with single argument. + * @param cls the class to be constructed + * @param parameterType The constructor parameter type + * @return null if matching accessible constructor can not be found. + * @see Class#getConstructor + * @see #getAccessibleConstructor(java.lang.reflect.Constructor) + */ + public static Constructor getAccessibleConstructor(Class cls, + Class parameterType) { + return getAccessibleConstructor(cls, new Class[] { parameterType }); + } + + /** + * Returns a constructor given a class and signature. + * @param cls the class to be constructed + * @param parameterTypes the parameter array + * @return null if matching accessible constructor can not be found + * @see Class#getConstructor + * @see #getAccessibleConstructor(java.lang.reflect.Constructor) + */ + public static Constructor getAccessibleConstructor(Class cls, + Class[] parameterTypes) { + try { + return getAccessibleConstructor(cls.getConstructor(parameterTypes)); + } catch (NoSuchMethodException e) { + return (null); + } + } + + /** + * Returns accessible version of the given constructor. + * @param ctor prototype constructor object. + * @return null if accessible constructor can not be found. + * @see java.lang.SecurityManager + */ + public static Constructor getAccessibleConstructor(Constructor ctor) { + return MemberUtils.isAccessible(ctor) + && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null; + } + + /** + *

          Find an accessible constructor with compatible parameters. Compatible + * parameters mean that every method parameter is assignable from the given + * parameters. In other words, it finds constructor that will take the + * parameters given.

          + * + *

          First it checks if there is constructor matching the exact signature. + * If no such, all the constructors of the class are tested if their + * signatures are assignment compatible with the parameter types. The first + * matching constructor is returned.

          + * + * @param cls find constructor for this class + * @param parameterTypes find method with compatible parameters + * @return a valid Constructor object. If there's no matching constructor, + * returns null. + */ + public static Constructor getMatchingAccessibleConstructor(Class cls, + Class[] parameterTypes) { + // see if we can find the constructor directly + // most of the time this works and it's much faster + try { + Constructor ctor = cls.getConstructor(parameterTypes); + MemberUtils.setAccessibleWorkaround(ctor); + return ctor; + } catch (NoSuchMethodException e) { /* SWALLOW */ + } + Constructor result = null; + // search through all constructors + Constructor[] ctors = cls.getConstructors(); + // return best match: + for (int i = 0; i < ctors.length; i++) { + + // compare parameters + if (ClassUtils.isAssignable(parameterTypes, ctors[i].getParameterTypes(), true)) { + // get accessible version of constructor + Constructor ctor = getAccessibleConstructor(ctors[i]); + if (ctor != null) { + MemberUtils.setAccessibleWorkaround(ctor); + if (result == null + || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result + .getParameterTypes(), parameterTypes) < 0) { + result = ctor; + } + } + } + } + return result; + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/FieldUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/FieldUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/FieldUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,599 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import org.apache.commons.lang.ClassUtils; + +/** + * Utilities for working with fields by reflection. Adapted and refactored + * from the dormant [reflect] Commons sandbox component. + *

          + * The ability is provided to break the scoping restrictions coded by the + * programmer. This can allow fields to be changed that shouldn't be. This + * facility should be used with care. + * + * @author Apache Software Foundation + * @author Matt Benson + * @since 2.5 + * @version $Id$ + */ +public class FieldUtils { + + /** + * FieldUtils instances should NOT be constructed in standard programming. + *

          + * This constructor is public to permit tools that require a JavaBean instance + * to operate. + */ + public FieldUtils() { + super(); + } + + /** + * Gets an accessible Field by name respecting scope. + * Superclasses/interfaces will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getField(Class cls, String fieldName) { + Field field = getField(cls, fieldName, false); + MemberUtils.setAccessibleWorkaround(field); + return field; + } + + /** + * Gets an accessible Field by name breaking scope + * if requested. Superclasses/interfaces will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getField(final Class cls, String fieldName, boolean forceAccess) { + if (cls == null) { + throw new IllegalArgumentException("The class must not be null"); + } + if (fieldName == null) { + throw new IllegalArgumentException("The field name must not be null"); + } + // Sun Java 1.3 has a bugged implementation of getField hence we write the + // code ourselves + + // getField() will return the Field object with the declaring class + // set correctly to the class that declares the field. Thus requesting the + // field on a subclass will return the field from the superclass. + // + // priority order for lookup: + // searchclass private/protected/package/public + // superclass protected/package/public + // private/different package blocks access to further superclasses + // implementedinterface public + + // check up the superclass hierarchy + for (Class acls = cls; acls != null; acls = acls.getSuperclass()) { + try { + Field field = acls.getDeclaredField(fieldName); + // getDeclaredField checks for non-public scopes as well + // and it returns accurate results + if (!Modifier.isPublic(field.getModifiers())) { + if (forceAccess) { + field.setAccessible(true); + } else { + continue; + } + } + return field; + } catch (NoSuchFieldException ex) { + // ignore + } + } + // check the public interface case. This must be manually searched for + // incase there is a public supersuperclass field hidden by a private/package + // superclass field. + Field match = null; + for (Iterator intf = ClassUtils.getAllInterfaces(cls).iterator(); intf + .hasNext();) { + try { + Field test = ((Class) intf.next()).getField(fieldName); + if (match != null) { + throw new IllegalArgumentException( + "Reference to field " + + fieldName + + " is ambiguous relative to " + + cls + + "; a matching field exists on two or more implemented interfaces."); + } + match = test; + } catch (NoSuchFieldException ex) { + // ignore + } + } + return match; + } + + /** + * Gets an accessible Field by name respecting scope. + * Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getDeclaredField(Class cls, String fieldName) { + return getDeclaredField(cls, fieldName, false); + } + + /** + * Gets an accessible Field by name breaking scope + * if requested. Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getDeclaredField(Class cls, String fieldName, boolean forceAccess) { + if (cls == null) { + throw new IllegalArgumentException("The class must not be null"); + } + if (fieldName == null) { + throw new IllegalArgumentException("The field name must not be null"); + } + try { + // only consider the specified class by using getDeclaredField() + Field field = cls.getDeclaredField(fieldName); + if (!MemberUtils.isAccessible(field)) { + if (forceAccess) { + field.setAccessible(true); + } else { + return null; + } + } + return field; + } catch (NoSuchFieldException e) { + } + return null; + } + + /** + * Read an accessible static Field. + * @param field to read + * @return the field value + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readStaticField(Field field) throws IllegalAccessException { + return readStaticField(field, false); + } + + /** + * Read a static Field. + * @param field to read + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. + * @return the field value + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (!Modifier.isStatic(field.getModifiers())) { + throw new IllegalArgumentException("The field '" + field.getName() + "' is not static"); + } + return readField(field, (Object) null, forceAccess); + } + + /** + * Read the named public static field. Superclasses will be considered. + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException { + return readStaticField(cls, fieldName, false); + } + + /** + * Read the named static field. Superclasses will be considered. + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readStaticField(Class cls, String fieldName, boolean forceAccess) throws IllegalAccessException { + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Gets a static Field value by name. The field must be public. + * Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readDeclaredStaticField(Class cls, String fieldName) throws IllegalAccessException { + return readDeclaredStaticField(cls, fieldName, false); + } + + /** + * Gets a static Field value by name. Only the specified class will + * be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readDeclaredStaticField(Class cls, String fieldName, boolean forceAccess) + throws IllegalAccessException { + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Read an accessible Field. + * @param field the field to use + * @param target the object to call on, may be null for static fields + * @return the field value + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readField(Field field, Object target) throws IllegalAccessException { + return readField(field, target, false); + } + + /** + * Read a Field. + * @param field the field to use + * @param target the object to call on, may be null for static fields + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. + * @return the field value + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + return field.get(target); + } + + /** + * Read the named public field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not public + */ + public static Object readField(Object target, String fieldName) throws IllegalAccessException { + return readField(target, fieldName, false); + } + + /** + * Read the named field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the field value + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not made accessible + */ + public static Object readField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + return readField(field, target); + } + + /** + * Read the named public field. Only the class of the specified object will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not public + */ + public static Object readDeclaredField(Object target, String fieldName) throws IllegalAccessException { + return readDeclaredField(target, fieldName, false); + } + + /** + * Gets a Field value by name. Only the class of the specified + * object will be considered. + * + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readDeclaredField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + return readField(field, target); + } + + /** + * Write a public static Field. + * @param field to write + * @param value to set + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeStaticField(Field field, Object value) throws IllegalAccessException { + writeStaticField(field, value, false); + } + + /** + * Write a static Field. + * @param field to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeStaticField(Field field, Object value, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (!Modifier.isStatic(field.getModifiers())) { + throw new IllegalArgumentException("The field '" + field.getName() + "' is not static"); + } + writeField(field, (Object) null, value, forceAccess); + } + + /** + * Write a named public static Field. Superclasses will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeStaticField(Class cls, String fieldName, Object value) throws IllegalAccessException { + writeStaticField(cls, fieldName, value, false); + } + + /** + * Write a named static Field. Superclasses will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeStaticField(Class cls, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + writeStaticField(field, value); + } + + /** + * Write a named public static Field. Only the specified class will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeDeclaredStaticField(Class cls, String fieldName, Object value) + throws IllegalAccessException { + writeDeclaredStaticField(cls, fieldName, value, false); + } + + /** + * Write a named static Field. Only the specified class will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeDeclaredStaticField(Class cls, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, (Object) null, value); + } + + /** + * Write an accessible field. + * @param field to write + * @param target the object to call on, may be null for static fields + * @param value to set + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not accessible or is final + */ + public static void writeField(Field field, Object target, Object value) throws IllegalAccessException { + writeField(field, target, value, false); + } + + /** + * Write a field. + * @param field to write + * @param target the object to call on, may be null for static fields + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeField(Field field, Object target, Object value, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + field.set(target, value); + } + + /** + * Write a public field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not accessible + */ + public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException { + writeField(target, fieldName, value, false); + } + + /** + * Write a field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeField(Object target, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, target, value); + } + + /** + * Write a public field. Only the specified class will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeDeclaredField(Object target, String fieldName, Object value) throws IllegalAccessException { + writeDeclaredField(target, fieldName, value, false); + } + + /** + * Write a public field. Only the specified class will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeDeclaredField(Object target, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, target, value); + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MemberUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MemberUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MemberUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.reflect; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.ClassUtils; +import org.apache.commons.lang.SystemUtils; + +/** + * Contains common code for working with Methods/Constructors, extracted and + * refactored from MethodUtils when it was imported from Commons + * BeanUtils. + * + * @author Apache Software Foundation + * @author Steve Cohen + * @author Matt Benson + * @since 2.5 + * @version $Id$ + */ +abstract class MemberUtils { + // TODO extract an interface to implement compareParameterSets(...)? + + private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; + + private static final Method IS_SYNTHETIC; + static { + Method isSynthetic = null; + if (SystemUtils.isJavaVersionAtLeast(1.5f)) { + // cannot call synthetic methods: + try { + isSynthetic = Member.class.getMethod("isSynthetic", + ArrayUtils.EMPTY_CLASS_ARRAY); + } catch (Exception e) { + } + } + IS_SYNTHETIC = isSynthetic; + } + + /** Array of primitive number types ordered by "promotability" */ + private static final Class[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE, Short.TYPE, + Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE }; + + /** + * XXX Default access superclass workaround + * + * When a public class has a default access superclass with public members, + * these members are accessible. Calling them from compiled code works fine. + * Unfortunately, on some JVMs, using reflection to invoke these members + * seems to (wrongly) to prevent access even when the modifer is public. + * Calling setAccessible(true) solves the problem but will only work from + * sufficiently privileged code. Better workarounds would be gratefully + * accepted. + * @param o the AccessibleObject to set as accessible + */ + static void setAccessibleWorkaround(AccessibleObject o) { + if (o == null || o.isAccessible()) { + return; + } + Member m = (Member) o; + if (Modifier.isPublic(m.getModifiers()) + && isPackageAccess(m.getDeclaringClass().getModifiers())) { + try { + o.setAccessible(true); + } catch (SecurityException e) { + // ignore in favor of subsequent IllegalAccessException + } + } + } + + /** + * Learn whether a given set of modifiers implies package access. + * @param modifiers to test + * @return true unless package/protected/private modifier detected + */ + static boolean isPackageAccess(int modifiers) { + return (modifiers & ACCESS_TEST) == 0; + } + + /** + * Check a Member for basic accessibility. + * @param m Member to check + * @return true if m is accessible + */ + static boolean isAccessible(Member m) { + return m != null && Modifier.isPublic(m.getModifiers()) && !isSynthetic(m); + } + + /** + * Try to learn whether a given member, on JDK >= 1.5, is synthetic. + * @param m Member to check + * @return true if m was introduced by the compiler. + */ + static boolean isSynthetic(Member m) { + if (IS_SYNTHETIC != null) { + try { + return ((Boolean) IS_SYNTHETIC.invoke(m, null)).booleanValue(); + } catch (Exception e) { + } + } + return false; + } + + /** + * Compare the relative fitness of two sets of parameter types in terms of + * matching a third set of runtime parameter types, such that a list ordered + * by the results of the comparison would return the best match first + * (least). + * + * @param left the "left" parameter set + * @param right the "right" parameter set + * @param actual the runtime parameter types to match against + * left/right + * @return int consistent with compare semantics + */ + static int compareParameterTypes(Class[] left, Class[] right, Class[] actual) { + float leftCost = getTotalTransformationCost(actual, left); + float rightCost = getTotalTransformationCost(actual, right); + return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0; + } + + /** + * Returns the sum of the object transformation cost for each class in the + * source argument list. + * @param srcArgs The source arguments + * @param destArgs The destination arguments + * @return The total transformation cost + */ + private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { + float totalCost = 0.0f; + for (int i = 0; i < srcArgs.length; i++) { + Class srcClass, destClass; + srcClass = srcArgs[i]; + destClass = destArgs[i]; + totalCost += getObjectTransformationCost(srcClass, destClass); + } + return totalCost; + } + + /** + * Gets the number of steps required needed to turn the source class into + * the destination class. This represents the number of steps in the object + * hierarchy graph. + * @param srcClass The source class + * @param destClass The destination class + * @return The cost of transforming an object + */ + private static float getObjectTransformationCost(Class srcClass, Class destClass) { + if (destClass.isPrimitive()) { + return getPrimitivePromotionCost(srcClass, destClass); + } + float cost = 0.0f; + while (srcClass != null && !destClass.equals(srcClass)) { + if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) { + // slight penalty for interface match. + // we still want an exact match to override an interface match, + // but + // an interface match should override anything where we have to + // get a superclass. + cost += 0.25f; + break; + } + cost++; + srcClass = srcClass.getSuperclass(); + } + /* + * If the destination class is null, we've travelled all the way up to + * an Object match. We'll penalize this by adding 1.5 to the cost. + */ + if (srcClass == null) { + cost += 1.5f; + } + return cost; + } + + /** + * Get the number of steps required to promote a primitive number to another + * type. + * @param srcClass the (primitive) source class + * @param destClass the (primitive) destination class + * @return The cost of promoting the primitive + */ + private static float getPrimitivePromotionCost(final Class srcClass, final Class destClass) { + float cost = 0.0f; + Class cls = srcClass; + if (!cls.isPrimitive()) { + // slight unwrapping penalty + cost += 0.1f; + cls = ClassUtils.wrapperToPrimitive(cls); + } + for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) { + if (cls == ORDERED_PRIMITIVE_TYPES[i]) { + cost += 0.1f; + if (i < ORDERED_PRIMITIVE_TYPES.length - 1) { + cls = ORDERED_PRIMITIVE_TYPES[i + 1]; + } + } + } + return cost; + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MethodUtils.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MethodUtils.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/MethodUtils.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,678 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.ClassUtils; + +/** + *

          Utility reflection methods focused on methods, originally from Commons BeanUtils. + * Differences from the BeanUtils version may be noted, especially where similar functionality + * already existed within Lang. + *

          + * + *

          Known Limitations

          + *

          Accessing Public Methods In A Default Access Superclass

          + *

          There is an issue when invoking public methods contained in a default access superclass on JREs prior to 1.4. + * Reflection locates these methods fine and correctly assigns them as public. + * However, an IllegalAccessException is thrown if the method is invoked.

          + * + *

          MethodUtils contains a workaround for this situation. + * It will attempt to call setAccessible on this method. + * If this call succeeds, then the method can be invoked as normal. + * This call will only succeed when the application has sufficient security privileges. + * If this call fails then the method may fail.

          + * + * @author Apache Software Foundation + * @author Craig R. McClanahan + * @author Ralph Schaer + * @author Chris Audley + * @author Rey François + * @author Gregor Raýman + * @author Jan Sorensen + * @author Robert Burrell Donkin + * @author Matt Benson + * @since 2.5 + * @version $Id$ + */ +public class MethodUtils { + + /** + *

          MethodUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as + * MethodUtils.getAccessibleMethod(method).

          + * + *

          This constructor is public to permit tools that require a JavaBean + * instance to operate.

          + */ + public MethodUtils() { + super(); + } + + /** + *

          Invoke a named method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean object + * would match a boolean primitive.

          + * + *

          This is a convenient wrapper for + * {@link #invokeMethod(Object object, String methodName, Object[] args)}. + *

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param arg use this argument + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the method invoked + * @throws IllegalAccessException if the requested method is not accessible via reflection + */ + public static Object invokeMethod(Object object, String methodName, + Object arg) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + return invokeMethod(object, methodName, new Object[] { arg }); + } + + /** + *

          Invoke a named method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean object + * would match a boolean primitive.

          + * + *

          This is a convenient wrapper for + * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}. + *

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the method invoked + * @throws IllegalAccessException if the requested method is not accessible via reflection + */ + public static Object invokeMethod(Object object, String methodName, + Object[] args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeMethod(object, methodName, args, parameterTypes); + } + + /** + *

          Invoke a named method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean object + * would match a boolean primitive.

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the method invoked + * @throws IllegalAccessException if the requested method is not accessible via reflection + */ + public static Object invokeMethod(Object object, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Method method = getMatchingAccessibleMethod(object.getClass(), + methodName, parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on object: " + + object.getClass().getName()); + } + return method.invoke(object, args); + } + + /** + *

          Invoke a method whose parameter type matches exactly the object + * type.

          + * + *

          This is a convenient wrapper for + * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. + *

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param arg use this argument + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactMethod(Object object, String methodName, + Object arg) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + return invokeExactMethod(object, methodName, new Object[] { arg }); + } + + /** + *

          Invoke a method whose parameter types match exactly the object + * types.

          + * + *

          This uses reflection to invoke the method obtained from a call to + * getAccessibleMethod().

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactMethod(Object object, String methodName, + Object[] args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactMethod(object, methodName, args, parameterTypes); + } + + /** + *

          Invoke a method whose parameter types match exactly the parameter + * types given.

          + * + *

          This uses reflection to invoke the method obtained from a call to + * getAccessibleMethod().

          + * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactMethod(Object object, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Method method = getAccessibleMethod(object.getClass(), methodName, + parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on object: " + + object.getClass().getName()); + } + return method.invoke(object, args); + } + + /** + *

          Invoke a static method whose parameter types match exactly the parameter + * types given.

          + * + *

          This uses reflection to invoke the method obtained from a call to + * {@link #getAccessibleMethod(Class, String, Class[])}.

          + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactStaticMethod(Class cls, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Method method = getAccessibleMethod(cls, methodName, parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on class: " + cls.getName()); + } + return method.invoke(null, args); + } + + /** + *

          Invoke a named static method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean class + * would match a boolean primitive.

          + * + *

          This is a convenient wrapper for + * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}. + *

          + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param arg use this argument + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeStaticMethod(Class cls, String methodName, + Object arg) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + return invokeStaticMethod(cls, methodName, new Object[] { arg }); + } + + /** + *

          Invoke a named static method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean class + * would match a boolean primitive.

          + * + *

          This is a convenient wrapper for + * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}. + *

          + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeStaticMethod(Class cls, String methodName, + Object[] args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeStaticMethod(cls, methodName, args, parameterTypes); + } + + /** + *

          Invoke a named static method whose parameter type matches the object type.

          + * + *

          This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

          + * + *

          This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean class + * would match a boolean primitive.

          + * + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeStaticMethod(Class cls, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Method method = getMatchingAccessibleMethod(cls, methodName, + parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on class: " + cls.getName()); + } + return method.invoke(null, args); + } + + /** + *

          Invoke a static method whose parameter type matches exactly the object + * type.

          + * + *

          This is a convenient wrapper for + * {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}. + *

          + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param arg use this argument + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactStaticMethod(Class cls, String methodName, + Object arg) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + return invokeExactStaticMethod(cls, methodName, new Object[] { arg }); + } + + /** + *

          Invoke a static method whose parameter types match exactly the object + * types.

          + * + *

          This uses reflection to invoke the method obtained from a call to + * {@link #getAccessibleMethod(Class, String, Class[])}.

          + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactStaticMethod(Class cls, String methodName, + Object[] args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactStaticMethod(cls, methodName, args, parameterTypes); + } + + /** + *

          Return an accessible method (that is, one that can be invoked via + * reflection) with given name and a single parameter. If no such method + * can be found, return null. + * Basically, a convenience wrapper that constructs a Class + * array for you.

          + * + * @param cls get method from this class + * @param methodName get method with this name + * @param parameterType taking this type of parameter + * @return The accessible method + */ + public static Method getAccessibleMethod(Class cls, String methodName, + Class parameterType) { + return getAccessibleMethod(cls, methodName, + new Class[] { parameterType }); + } + + /** + *

          Return an accessible method (that is, one that can be invoked via + * reflection) with given name and parameters. If no such method + * can be found, return null. + * This is just a convenient wrapper for + * {@link #getAccessibleMethod(Method method)}.

          + * + * @param cls get method from this class + * @param methodName get method with this name + * @param parameterTypes with these parameters types + * @return The accessible method + */ + public static Method getAccessibleMethod(Class cls, String methodName, + Class[] parameterTypes) { + try { + return getAccessibleMethod(cls.getMethod(methodName, + parameterTypes)); + } catch (NoSuchMethodException e) { + return (null); + } + } + + /** + *

          Return an accessible method (that is, one that can be invoked via + * reflection) that implements the specified Method. If no such method + * can be found, return null.

          + * + * @param method The method that we wish to call + * @return The accessible method + */ + public static Method getAccessibleMethod(Method method) { + if (!MemberUtils.isAccessible(method)) { + return null; + } + // If the declaring class is public, we are done + Class cls = method.getDeclaringClass(); + if (Modifier.isPublic(cls.getModifiers())) { + return method; + } + String methodName = method.getName(); + Class[] parameterTypes = method.getParameterTypes(); + + // Check the implemented interfaces and subinterfaces + method = getAccessibleMethodFromInterfaceNest(cls, methodName, + parameterTypes); + + // Check the superclass chain + if (method == null) { + method = getAccessibleMethodFromSuperclass(cls, methodName, + parameterTypes); + } + return method; + } + + /** + *

          Return an accessible method (that is, one that can be invoked via + * reflection) by scanning through the superclasses. If no such method + * can be found, return null.

          + * + * @param cls Class to be checked + * @param methodName Method name of the method we wish to call + * @param parameterTypes The parameter type signatures + * @return the accessible method or null if not found + */ + private static Method getAccessibleMethodFromSuperclass(Class cls, + String methodName, Class[] parameterTypes) { + Class parentClass = cls.getSuperclass(); + while (parentClass != null) { + if (Modifier.isPublic(parentClass.getModifiers())) { + try { + return parentClass.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) { + return null; + } + } + parentClass = parentClass.getSuperclass(); + } + return null; + } + + /** + *

          Return an accessible method (that is, one that can be invoked via + * reflection) that implements the specified method, by scanning through + * all implemented interfaces and subinterfaces. If no such method + * can be found, return null.

          + * + *

          There isn't any good reason why this method must be private. + * It is because there doesn't seem any reason why other classes should + * call this rather than the higher level methods.

          + * + * @param cls Parent class for the interfaces to be checked + * @param methodName Method name of the method we wish to call + * @param parameterTypes The parameter type signatures + * @return the accessible method or null if not found + */ + private static Method getAccessibleMethodFromInterfaceNest(Class cls, + String methodName, Class[] parameterTypes) { + Method method = null; + + // Search up the superclass chain + for (; cls != null; cls = cls.getSuperclass()) { + + // Check the implemented interfaces of the parent class + Class[] interfaces = cls.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + // Is this interface public? + if (!Modifier.isPublic(interfaces[i].getModifiers())) { + continue; + } + // Does the method exist on this interface? + try { + method = interfaces[i].getDeclaredMethod(methodName, + parameterTypes); + } catch (NoSuchMethodException e) { + /* + * Swallow, if no method is found after the loop then this + * method returns null. + */ + } + if (method != null) { + break; + } + // Recursively check our parent interfaces + method = getAccessibleMethodFromInterfaceNest(interfaces[i], + methodName, parameterTypes); + if (method != null) { + break; + } + } + } + return method; + } + + /** + *

          Find an accessible method that matches the given name and has compatible parameters. + * Compatible parameters mean that every method parameter is assignable from + * the given parameters. + * In other words, it finds a method with the given name + * that will take the parameters given.

          + * + *

          This method is used by + * {@link + * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. + * + *

          This method can match primitive parameter by passing in wrapper classes. + * For example, a Boolean will match a primitive boolean + * parameter. + * + * @param cls find method in this class + * @param methodName find method with this name + * @param parameterTypes find method with most compatible parameters + * @return The accessible method + */ + public static Method getMatchingAccessibleMethod(Class cls, + String methodName, Class[] parameterTypes) { + try { + Method method = cls.getMethod(methodName, parameterTypes); + MemberUtils.setAccessibleWorkaround(method); + return method; + } catch (NoSuchMethodException e) { /* SWALLOW */ + } + // search through all methods + Method bestMatch = null; + Method[] methods = cls.getMethods(); + for (int i = 0, size = methods.length; i < size; i++) { + if (methods[i].getName().equals(methodName)) { + // compare parameters + if (ClassUtils.isAssignable(parameterTypes, methods[i] + .getParameterTypes(), true)) { + // get accessible version of method + Method accessibleMethod = getAccessibleMethod(methods[i]); + if (accessibleMethod != null) { + if (bestMatch == null + || MemberUtils.compareParameterTypes( + accessibleMethod.getParameterTypes(), + bestMatch.getParameterTypes(), + parameterTypes) < 0) { + bestMatch = accessibleMethod; + } + } + } + } + } + if (bestMatch != null) { + MemberUtils.setAccessibleWorkaround(bestMatch); + } + return bestMatch; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/package.html =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/package.html (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/reflect/package.html (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,29 @@ + + + + + + + +Accumulates common high-level uses of the java.lang.reflect APIs. +@since 2.5 +

          These classes are immutable, and therefore thread-safe.

          + + Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/CompositeFormat.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/CompositeFormat.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/CompositeFormat.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParseException; +import java.text.ParsePosition; + +/** + * Formats using one formatter and parses using a different formatter. An + * example of use for this would be a webapp where data is taken in one way and + * stored in a database another way. + * + * @author Apache Software Foundation + * @author Archimedes Trajano + * @version $Id$ + */ +public class CompositeFormat extends Format { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -4329119827877627683L; + + /** The parser to use. */ + private final Format parser; + /** The formatter to use. */ + private final Format formatter; + + /** + * Create a format that points its parseObject method to one implementation + * and its format method to another. + * + * @param parser implementation + * @param formatter implementation + */ + public CompositeFormat(Format parser, Format formatter) { + this.parser = parser; + this.formatter = formatter; + } + + /** + * Uses the formatter Format instance. + * + * @param obj the object to format + * @param toAppendTo the {@link StringBuffer} to append to + * @param pos the FieldPosition to use (or ignore). + * @return toAppendTo + * @see Format#format(Object, StringBuffer, FieldPosition) + */ + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + return formatter.format(obj, toAppendTo, pos); + } + + /** + * Uses the parser Format instance. + * + * @param source the String source + * @param pos the ParsePosition containing the position to parse from, will + * be updated according to parsing success (index) or failure + * (error index) + * @return the parsed Object + * @see Format#parseObject(String, ParsePosition) + */ + public Object parseObject(String source, ParsePosition pos) { + return parser.parseObject(source, pos); + } + + /** + * Provides access to the parser Format implementation. + * + * @return parser Format implementation + */ + public Format getParser() { + return this.parser; + } + + /** + * Provides access to the parser Format implementation. + * + * @return formatter Format implementation + */ + public Format getFormatter() { + return this.formatter; + } + + /** + * Utility method to parse and then reformat a String. + * + * @param input String to reformat + * @return A reformatted String + * @throws ParseException thrown by parseObject(String) call + */ + public String reformat(String input) throws ParseException { + return format(parseObject(input)); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/ExtendedMessageFormat.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/ExtendedMessageFormat.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/ExtendedMessageFormat.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.text.Format; +import java.text.MessageFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.Validate; + +/** + * Extends java.text.MessageFormat to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of FormatFactory instances associated with String + * format names. This registry will be consulted when the format elements are + * parsed from the message pattern. In this way custom patterns can be specified, + * and the formats supported by java.text.MessageFormat can be overridden + * at the format and/or format style level (see MessageFormat). A "format element" + * embedded in the message pattern is specified (()? signifies optionality):
          + * {argument-number(,format-name(,format-style)?)?} + * + *

          + * format-name and format-style values are trimmed of surrounding whitespace + * in the manner of java.text.MessageFormat. If format-name denotes + * FormatFactory formatFactoryInstance in registry, a Format + * matching format-name and format-style is requested from + * formatFactoryInstance. If this is successful, the Format + * found is used for this format element. + *

          + * + *

          NOTICE:: The various subformat mutator methods are considered unnecessary; they exist on the parent + * class to allow the type of customization which it is the job of this class to provide in + * a configurable fashion. These methods have thus been disabled and will throw + * UnsupportedOperationException if called. + *

          + * + *

          Limitations inherited from java.text.MessageFormat: + *

            + *
          • When using "choice" subformats, support for nested formatting instructions is limited + * to that provided by the base class.
          • + *
          • Thread-safety of Formats, including MessageFormat and thus + * ExtendedMessageFormat, is not guaranteed.
          • + *
          + *

          + * + * @author Apache Software Foundation + * @author Matt Benson + * @since 2.4 + * @version $Id$ + */ +public class ExtendedMessageFormat extends MessageFormat { + private static final long serialVersionUID = -2362048321261811743L; + private static final int HASH_SEED = 31; + + private static final String DUMMY_PATTERN = ""; + private static final String ESCAPED_QUOTE = "''"; + private static final char START_FMT = ','; + private static final char END_FE = '}'; + private static final char START_FE = '{'; + private static final char QUOTE = '\''; + + private String toPattern; + private final Map registry; + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Locale locale) { + this(pattern, locale, null); + } + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Map registry) { + this(pattern, Locale.getDefault(), registry); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Locale locale, Map registry) { + super(DUMMY_PATTERN); + setLocale(locale); + this.registry = registry; + applyPattern(pattern); + } + + /** + * {@inheritDoc} + */ + public String toPattern() { + return toPattern; + } + + /** + * Apply the specified pattern. + * + * @param pattern String + */ + public final void applyPattern(String pattern) { + if (registry == null) { + super.applyPattern(pattern); + toPattern = super.toPattern(); + return; + } + ArrayList foundFormats = new ArrayList(); + ArrayList foundDescriptions = new ArrayList(); + StrBuilder stripCustom = new StrBuilder(pattern.length()); + + ParsePosition pos = new ParsePosition(0); + char[] c = pattern.toCharArray(); + int fmtCount = 0; + while (pos.getIndex() < pattern.length()) { + switch (c[pos.getIndex()]) { + case QUOTE: + appendQuotedString(pattern, pos, stripCustom, true); + break; + case START_FE: + fmtCount++; + seekNonWs(pattern, pos); + int start = pos.getIndex(); + int index = readArgumentIndex(pattern, next(pos)); + stripCustom.append(START_FE).append(index); + seekNonWs(pattern, pos); + Format format = null; + String formatDescription = null; + if (c[pos.getIndex()] == START_FMT) { + formatDescription = parseFormatDescription(pattern, + next(pos)); + format = getFormat(formatDescription); + if (format == null) { + stripCustom.append(START_FMT).append(formatDescription); + } + } + foundFormats.add(format); + foundDescriptions.add(format == null ? null : formatDescription); + Validate.isTrue(foundFormats.size() == fmtCount); + Validate.isTrue(foundDescriptions.size() == fmtCount); + if (c[pos.getIndex()] != END_FE) { + throw new IllegalArgumentException( + "Unreadable format element at position " + start); + } + //$FALL-THROUGH$ + default: + stripCustom.append(c[pos.getIndex()]); + next(pos); + } + } + super.applyPattern(stripCustom.toString()); + toPattern = insertFormats(super.toPattern(), foundDescriptions); + if (containsElements(foundFormats)) { + Format[] origFormats = getFormats(); + // only loop over what we know we have, as MessageFormat on Java 1.3 + // seems to provide an extra format element: + int i = 0; + for (Iterator it = foundFormats.iterator(); it.hasNext(); i++) { + Format f = (Format) it.next(); + if (f != null) { + origFormats[i] = f; + } + } + super.setFormats(origFormats); + } + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param formatElementIndex format element index + * @param newFormat the new format + * @throws UnsupportedOperationException + */ + public void setFormat(int formatElementIndex, Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param argumentIndex argument index + * @param newFormat the new format + * @throws UnsupportedOperationException + */ + public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException + */ + public void setFormats(Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException + */ + public void setFormatsByArgumentIndex(Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Check if this extended message format is equal to another object. + * + * @param obj the object to compare to + * @return true if this object equals the other, otherwise false + * @since 2.6 + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!super.equals(obj)) { + return false; + } + if (ObjectUtils.notEqual(getClass(), obj.getClass())) { + return false; + } + ExtendedMessageFormat rhs = (ExtendedMessageFormat)obj; + if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { + return false; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * Return the hashcode. + * + * @return the hashcode + * @since 2.6 + */ + public int hashCode() { + int result = super.hashCode(); + result = HASH_SEED * result + ObjectUtils.hashCode(registry); + result = HASH_SEED * result + ObjectUtils.hashCode(toPattern); + return result; + } + + /** + * Get a custom format from a format description. + * + * @param desc String + * @return Format + */ + private Format getFormat(String desc) { + if (registry != null) { + String name = desc; + String args = null; + int i = desc.indexOf(START_FMT); + if (i > 0) { + name = desc.substring(0, i).trim(); + args = desc.substring(i + 1).trim(); + } + FormatFactory factory = (FormatFactory) registry.get(name); + if (factory != null) { + return factory.getFormat(name, args, getLocale()); + } + } + return null; + } + + /** + * Read the argument index from the current format element + * + * @param pattern pattern to parse + * @param pos current parse position + * @return argument index + */ + private int readArgumentIndex(String pattern, ParsePosition pos) { + int start = pos.getIndex(); + seekNonWs(pattern, pos); + StrBuilder result = new StrBuilder(); + boolean error = false; + for (; !error && pos.getIndex() < pattern.length(); next(pos)) { + char c = pattern.charAt(pos.getIndex()); + if (Character.isWhitespace(c)) { + seekNonWs(pattern, pos); + c = pattern.charAt(pos.getIndex()); + if (c != START_FMT && c != END_FE) { + error = true; + continue; + } + } + if ((c == START_FMT || c == END_FE) && result.length() > 0) { + try { + return Integer.parseInt(result.toString()); + } catch (NumberFormatException e) { + // we've already ensured only digits, so unless something + // outlandishly large was specified we should be okay. + } + } + error = !Character.isDigit(c); + result.append(c); + } + if (error) { + throw new IllegalArgumentException( + "Invalid format argument index at position " + start + ": " + + pattern.substring(start, pos.getIndex())); + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Parse the format component of a format element. + * + * @param pattern string to parse + * @param pos current parse position + * @return Format description String + */ + private String parseFormatDescription(String pattern, ParsePosition pos) { + int start = pos.getIndex(); + seekNonWs(pattern, pos); + int text = pos.getIndex(); + int depth = 1; + for (; pos.getIndex() < pattern.length(); next(pos)) { + switch (pattern.charAt(pos.getIndex())) { + case START_FE: + depth++; + break; + case END_FE: + depth--; + if (depth == 0) { + return pattern.substring(text, pos.getIndex()); + } + break; + case QUOTE: + getQuotedString(pattern, pos, false); + break; + } + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern source + * @param customPatterns The custom patterns to re-insert, if any + * @return full pattern + */ + private String insertFormats(String pattern, ArrayList customPatterns) { + if (!containsElements(customPatterns)) { + return pattern; + } + StrBuilder sb = new StrBuilder(pattern.length() * 2); + ParsePosition pos = new ParsePosition(0); + int fe = -1; + int depth = 0; + while (pos.getIndex() < pattern.length()) { + char c = pattern.charAt(pos.getIndex()); + switch (c) { + case QUOTE: + appendQuotedString(pattern, pos, sb, false); + break; + case START_FE: + depth++; + if (depth == 1) { + fe++; + sb.append(START_FE).append( + readArgumentIndex(pattern, next(pos))); + String customPattern = (String) customPatterns.get(fe); + if (customPattern != null) { + sb.append(START_FMT).append(customPattern); + } + } + break; + case END_FE: + depth--; + //$FALL-THROUGH$ + default: + sb.append(c); + next(pos); + } + } + return sb.toString(); + } + + /** + * Consume whitespace from the current parse position. + * + * @param pattern String to read + * @param pos current position + */ + private void seekNonWs(String pattern, ParsePosition pos) { + int len = 0; + char[] buffer = pattern.toCharArray(); + do { + len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); + pos.setIndex(pos.getIndex() + len); + } while (len > 0 && pos.getIndex() < pattern.length()); + } + + /** + * Convenience method to advance parse position by 1 + * + * @param pos ParsePosition + * @return pos + */ + private ParsePosition next(ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to appendTo if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuffer to append + * @param escapingOn whether to process escaped quotes + * @return appendTo + */ + private StrBuilder appendQuotedString(String pattern, ParsePosition pos, + StrBuilder appendTo, boolean escapingOn) { + int start = pos.getIndex(); + char[] c = pattern.toCharArray(); + if (escapingOn && c[start] == QUOTE) { + next(pos); + return appendTo == null ? null : appendTo.append(QUOTE); + } + int lastHold = start; + for (int i = pos.getIndex(); i < pattern.length(); i++) { + if (escapingOn && pattern.substring(i).startsWith(ESCAPED_QUOTE)) { + appendTo.append(c, lastHold, pos.getIndex() - lastHold).append( + QUOTE); + pos.setIndex(i + ESCAPED_QUOTE.length()); + lastHold = pos.getIndex(); + continue; + } + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + next(pos); + } + } + throw new IllegalArgumentException( + "Unterminated quoted string at position " + start); + } + + /** + * Consume quoted string only + * + * @param pattern pattern to parse + * @param pos current parse position + * @param escapingOn whether to process escaped quotes + */ + private void getQuotedString(String pattern, ParsePosition pos, + boolean escapingOn) { + appendQuotedString(pattern, pos, null, escapingOn); + } + + /** + * Learn whether the specified Collection contains non-null elements. + * @param coll to check + * @return true if some Object was found, false otherwise. + */ + private boolean containsElements(Collection coll) { + if (coll == null || coll.size() == 0) { + return false; + } + for (Iterator iter = coll.iterator(); iter.hasNext();) { + if (iter.next() != null) { + return true; + } + } + return false; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/FormatFactory.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/FormatFactory.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/FormatFactory.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.text.Format; +import java.util.Locale; + +/** + * Format factory. + * + * @author Apache Software Foundation + * @since 2.4 + * @version $Id$ + */ +public interface FormatFactory { + + /** + * Create or retrieve a format instance. + * + * @param name The format type name + * @param arguments Arguments used to create the format instance. This allows the + * FormatFactory to implement the "format style" + * concept from java.text.MessageFormat. + * @param locale The locale, may be null + * @return The format instance + */ + Format getFormat(String name, String arguments, Locale locale); + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrBuilder.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrBuilder.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrBuilder.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,2794 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.SystemUtils; + +/** + * Builds a string from constituent parts providing a more flexible and powerful API + * than StringBuffer. + *

          + * The main differences from StringBuffer/StringBuilder are: + *

            + *
          • Not synchronized
          • + *
          • Not final
          • + *
          • Subclasses have direct access to character array
          • + *
          • Additional methods + *
              + *
            • appendWithSeparators - adds an array of values, with a separator
            • + *
            • appendPadding - adds a length padding characters
            • + *
            • appendFixedLength - adds a fixed width field to the builder
            • + *
            • toCharArray/getChars - simpler ways to get a range of the character array
            • + *
            • delete - delete char or string
            • + *
            • replace - search and replace for a char or string
            • + *
            • leftString/rightString/midString - substring without exceptions
            • + *
            • contains - whether the builder contains a char or string
            • + *
            • size/clear/isEmpty - collections style API methods
            • + *
            + *
          • + *
          + *
        • Views + *
            + *
          • asTokenizer - uses the internal buffer as the source of a StrTokenizer
          • + *
          • asReader - uses the internal buffer as the source of a Reader
          • + *
          • asWriter - allows a Writer to write directly to the internal buffer
          • + *
          + *
        • + *
        + *

        + * The aim has been to provide an API that mimics very closely what StringBuffer + * provides, but with additional methods. It should be noted that some edge cases, + * with invalid indices or null input, have been altered - see individual methods. + * The biggest of these changes is that by default, null will not output the text + * 'null'. This can be controlled by a property, {@link #setNullText(String)}. + *

        + * Prior to 3.0, this class implemented Cloneable but did not implement the + * clone method so could not be used. From 3.0 onwards it no longer implements + * the interface. + * + * @author Apache Software Foundation + * @since 2.2 + * @version $Id$ + */ +public class StrBuilder implements Cloneable { + + /** + * The extra capacity for new builders. + */ + static final int CAPACITY = 32; + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7628716375283629643L; + + /** Internal data storage. */ + protected char[] buffer; // TODO make private? + /** Current size of the buffer. */ + protected int size; // TODO make private? + /** The new line. */ + private String newLine; + /** The null text. */ + private String nullText; + + //----------------------------------------------------------------------- + /** + * Constructor that creates an empty builder initial capacity 32 characters. + */ + public StrBuilder() { + this(CAPACITY); + } + + /** + * Constructor that creates an empty builder the specified initial capacity. + * + * @param initialCapacity the initial capacity, zero or less will be converted to 32 + */ + public StrBuilder(int initialCapacity) { + super(); + if (initialCapacity <= 0) { + initialCapacity = CAPACITY; + } + buffer = new char[initialCapacity]; + } + + /** + * Constructor that creates a builder from the string, allocating + * 32 extra characters for growth. + * + * @param str the string to copy, null treated as blank string + */ + public StrBuilder(String str) { + super(); + if (str == null) { + buffer = new char[CAPACITY]; + } else { + buffer = new char[str.length() + CAPACITY]; + append(str); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the text to be appended when a new line is added. + * + * @return the new line text, null means use system default + */ + public String getNewLineText() { + return newLine; + } + + /** + * Sets the text to be appended when a new line is added. + * + * @param newLine the new line text, null means use system default + * @return this, to enable chaining + */ + public StrBuilder setNewLineText(String newLine) { + this.newLine = newLine; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the text to be appended when null is added. + * + * @return the null text, null means no append + */ + public String getNullText() { + return nullText; + } + + /** + * Sets the text to be appended when null is added. + * + * @param nullText the null text, null means no append + * @return this, to enable chaining + */ + public StrBuilder setNullText(String nullText) { + if (nullText != null && nullText.length() == 0) { + nullText = null; + } + this.nullText = nullText; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the length of the string builder. + * + * @return the length + */ + public int length() { + return size; + } + + /** + * Updates the length of the builder by either dropping the last characters + * or adding filler of unicode zero. + * + * @param length the length to set to, must be zero or positive + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the length is negative + */ + public StrBuilder setLength(int length) { + if (length < 0) { + throw new StringIndexOutOfBoundsException(length); + } + if (length < size) { + size = length; + } else if (length > size) { + ensureCapacity(length); + int oldEnd = size; + int newEnd = length; + size = length; + for (int i = oldEnd; i < newEnd; i++) { + buffer[i] = '\0'; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the current size of the internal character array buffer. + * + * @return the capacity + */ + public int capacity() { + return buffer.length; + } + + /** + * Checks the capacity and ensures that it is at least the size specified. + * + * @param capacity the capacity to ensure + * @return this, to enable chaining + */ + public StrBuilder ensureCapacity(int capacity) { + if (capacity > buffer.length) { + char[] old = buffer; + buffer = new char[capacity * 2]; + System.arraycopy(old, 0, buffer, 0, size); + } + return this; + } + + /** + * Minimizes the capacity to the actual length of the string. + * + * @return this, to enable chaining + */ + public StrBuilder minimizeCapacity() { + if (buffer.length > length()) { + char[] old = buffer; + buffer = new char[length()]; + System.arraycopy(old, 0, buffer, 0, size); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the length of the string builder. + *

        + * This method is the same as {@link #length()} and is provided to match the + * API of Collections. + * + * @return the length + */ + public int size() { + return size; + } + + /** + * Checks is the string builder is empty (convenience Collections API style method). + *

        + * This method is the same as checking {@link #length()} and is provided to match the + * API of Collections. + * + * @return true if the size is 0. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Clears the string builder (convenience Collections API style method). + *

        + * This method does not reduce the size of the internal character buffer. + * To do that, call clear() followed by {@link #minimizeCapacity()}. + *

        + * This method is the same as {@link #setLength(int)} called with zero + * and is provided to match the API of Collections. + * + * @return this, to enable chaining + */ + public StrBuilder clear() { + size = 0; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the character at the specified index. + * + * @see #setCharAt(int, char) + * @see #deleteCharAt(int) + * @param index the index to retrieve, must be valid + * @return the character at the index + * @throws IndexOutOfBoundsException if the index is invalid + */ + public char charAt(int index) { + if (index < 0 || index >= length()) { + throw new StringIndexOutOfBoundsException(index); + } + return buffer[index]; + } + + /** + * Sets the character at the specified index. + * + * @see #charAt(int) + * @see #deleteCharAt(int) + * @param index the index to set + * @param ch the new character + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder setCharAt(int index, char ch) { + if (index < 0 || index >= length()) { + throw new StringIndexOutOfBoundsException(index); + } + buffer[index] = ch; + return this; + } + + /** + * Deletes the character at the specified index. + * + * @see #charAt(int) + * @see #setCharAt(int, char) + * @param index the index to delete + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder deleteCharAt(int index) { + if (index < 0 || index >= size) { + throw new StringIndexOutOfBoundsException(index); + } + deleteImpl(index, index + 1, 1); + return this; + } + + //----------------------------------------------------------------------- + /** + * Copies the builder's character array into a new character array. + * + * @return a new array that represents the contents of the builder + */ + public char[] toCharArray() { + if (size == 0) { + return ArrayUtils.EMPTY_CHAR_ARRAY; + } + char chars[] = new char[size]; + System.arraycopy(buffer, 0, chars, 0, size); + return chars; + } + + /** + * Copies part of the builder's character array into a new character array. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except that + * if too large it is treated as end of string + * @return a new array that holds part of the contents of the builder + * @throws IndexOutOfBoundsException if startIndex is invalid, + * or if endIndex is invalid (but endIndex greater than size is valid) + */ + public char[] toCharArray(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + int len = endIndex - startIndex; + if (len == 0) { + return ArrayUtils.EMPTY_CHAR_ARRAY; + } + char chars[] = new char[len]; + System.arraycopy(buffer, startIndex, chars, 0, len); + return chars; + } + + /** + * Copies the character array into the specified array. + * + * @param destination the destination array, null will cause an array to be created + * @return the input array, unless that was null or too small + */ + public char[] getChars(char[] destination) { + int len = length(); + if (destination == null || destination.length < len) { + destination = new char[len]; + } + System.arraycopy(buffer, 0, destination, 0, len); + return destination; + } + + /** + * Copies the character array into the specified array. + * + * @param startIndex first index to copy, inclusive, must be valid + * @param endIndex last index, exclusive, must be valid + * @param destination the destination array, must not be null or too small + * @param destinationIndex the index to start copying in destination + * @throws NullPointerException if the array is null + * @throws IndexOutOfBoundsException if any index is invalid + */ + public void getChars(int startIndex, int endIndex, char destination[], int destinationIndex) { + if (startIndex < 0) { + throw new StringIndexOutOfBoundsException(startIndex); + } + if (endIndex < 0 || endIndex > length()) { + throw new StringIndexOutOfBoundsException(endIndex); + } + if (startIndex > endIndex) { + throw new StringIndexOutOfBoundsException("end < start"); + } + System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex); + } + + //----------------------------------------------------------------------- + /** + * Appends the new line string to this string builder. + *

        + * The new line string can be altered using {@link #setNewLineText(String)}. + * This might be used to force the output to always use Unix line endings + * even when on Windows. + * + * @return this, to enable chaining + */ + public StrBuilder appendNewLine() { + if (newLine == null) { + append(SystemUtils.LINE_SEPARATOR); + return this; + } + return append(newLine); + } + + /** + * Appends the text representing null to this string builder. + * + * @return this, to enable chaining + */ + public StrBuilder appendNull() { + if (nullText == null) { + return this; + } + return append(nullText); + } + + /** + * Appends an object to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param obj the object to append + * @return this, to enable chaining + */ + public StrBuilder append(Object obj) { + if (obj == null) { + return appendNull(); + } + return append(obj.toString()); + } + + /** + * Appends a string to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @return this, to enable chaining + */ + public StrBuilder append(String str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + str.getChars(0, strLen, buffer, len); + size += strLen; + } + return this; + } + + /** + * Appends part of a string to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(String str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends a string buffer to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string buffer to append + * @return this, to enable chaining + */ + public StrBuilder append(StringBuffer str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + str.getChars(0, strLen, buffer, len); + size += strLen; + } + return this; + } + + /** + * Appends part of a string buffer to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(StringBuffer str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends another string builder to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string builder to append + * @return this, to enable chaining + */ + public StrBuilder append(StrBuilder str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + System.arraycopy(str.buffer, 0, buffer, len, strLen); + size += strLen; + } + return this; + } + + /** + * Appends part of a string builder to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(StrBuilder str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends a char array to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @return this, to enable chaining + */ + public StrBuilder append(char[] chars) { + if (chars == null) { + return appendNull(); + } + int strLen = chars.length; + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + System.arraycopy(chars, 0, buffer, len, strLen); + size += strLen; + } + return this; + } + + /** + * Appends a char array to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(char[] chars, int startIndex, int length) { + if (chars == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid startIndex: " + length); + } + if (length < 0 || (startIndex + length) > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid length: " + length); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + System.arraycopy(chars, startIndex, buffer, len, length); + size += length; + } + return this; + } + + /** + * Appends a boolean value to the string builder. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(boolean value) { + if (value) { + ensureCapacity(size + 4); + buffer[size++] = 't'; + buffer[size++] = 'r'; + buffer[size++] = 'u'; + buffer[size++] = 'e'; + } else { + ensureCapacity(size + 5); + buffer[size++] = 'f'; + buffer[size++] = 'a'; + buffer[size++] = 'l'; + buffer[size++] = 's'; + buffer[size++] = 'e'; + } + return this; + } + + /** + * Appends a char value to the string builder. + * + * @param ch the value to append + * @return this, to enable chaining + */ + public StrBuilder append(char ch) { + int len = length(); + ensureCapacity(len + 1); + buffer[size++] = ch; + return this; + } + + /** + * Appends an int value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(int value) { + return append(String.valueOf(value)); + } + + /** + * Appends a long value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(long value) { + return append(String.valueOf(value)); + } + + /** + * Appends a float value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(float value) { + return append(String.valueOf(value)); + } + + /** + * Appends a double value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(double value) { + return append(String.valueOf(value)); + } + + //----------------------------------------------------------------------- + /** + * Appends an object followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param obj the object to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(Object obj) { + return append(obj).appendNewLine(); + } + + /** + * Appends a string followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(String str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(String str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends a string buffer followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string buffer to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StringBuffer str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string buffer followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StringBuffer str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends another string builder followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string builder to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StrBuilder str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string builder followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StrBuilder str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends a char array followed by a new line to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char[] chars) { + return append(chars).appendNewLine(); + } + + /** + * Appends a char array followed by a new line to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char[] chars, int startIndex, int length) { + return append(chars, startIndex, length).appendNewLine(); + } + + /** + * Appends a boolean value followed by a new line to the string builder. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(boolean value) { + return append(value).appendNewLine(); + } + + /** + * Appends a char value followed by a new line to the string builder. + * + * @param ch the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char ch) { + return append(ch).appendNewLine(); + } + + /** + * Appends an int value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(int value) { + return append(value).appendNewLine(); + } + + /** + * Appends a long value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(long value) { + return append(value).appendNewLine(); + } + + /** + * Appends a float value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(float value) { + return append(value).appendNewLine(); + } + + /** + * Appends a double value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(double value) { + return append(value).appendNewLine(); + } + + //----------------------------------------------------------------------- + /** + * Appends each item in an array to the builder without any separators. + * Appending a null array will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param array the array to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Object[] array) { + if (array != null && array.length > 0) { + for (int i = 0; i < array.length; i++) { + append(array[i]); + } + } + return this; + } + + /** + * Appends each item in a collection to the builder without any separators. + * Appending a null collection will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param coll the collection to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Collection coll) { + if (coll != null && coll.size() > 0) { + Iterator it = coll.iterator(); + while (it.hasNext()) { + append(it.next()); + } + } + return this; + } + + /** + * Appends each item in an iterator to the builder without any separators. + * Appending a null iterator will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param it the iterator to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Iterator it) { + if (it != null) { + while (it.hasNext()) { + append(it.next()); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends an array placing separators between each value, but + * not before the first or after the last. + * Appending a null array will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param array the array to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Object[] array, String separator) { + if (array != null && array.length > 0) { + separator = (separator == null ? "" : separator); + append(array[0]); + for (int i = 1; i < array.length; i++) { + append(separator); + append(array[i]); + } + } + return this; + } + + /** + * Appends a collection placing separators between each value, but + * not before the first or after the last. + * Appending a null collection will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param coll the collection to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Collection coll, String separator) { + if (coll != null && coll.size() > 0) { + separator = (separator == null ? "" : separator); + Iterator it = coll.iterator(); + while (it.hasNext()) { + append(it.next()); + if (it.hasNext()) { + append(separator); + } + } + } + return this; + } + + /** + * Appends an iterator placing separators between each value, but + * not before the first or after the last. + * Appending a null iterator will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param it the iterator to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Iterator it, String separator) { + if (it != null) { + separator = (separator == null ? "" : separator); + while (it.hasNext()) { + append(it.next()); + if (it.hasNext()) { + append(separator); + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends a separator if the builder is currently non-empty. + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

        + * This method is useful for adding a separator each time around the + * loop except the first. + *

        +     * for (Iterator it = list.iterator(); it.hasNext(); ) {
        +     *   appendSeparator(",");
        +     *   append(it.next());
        +     * }
        +     * 
        + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Collection, String)}. + * + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(String separator) { + return appendSeparator(separator, null); + } + + /** + * Appends one of both separators to the StrBuilder. + * If the builder is currently empty it will append the defaultIfEmpty-separator + * Otherwise it will append the standard-separator + * + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

        + * This method is for example useful for constructing queries + *

        +     * StrBuilder whereClause = new StrBuilder();
        +     * if(searchCommand.getPriority() != null) {
        +     *  whereClause.appendSeparator(" and", " where");
        +     *  whereClause.append(" priority = ?")
        +     * }
        +     * if(searchCommand.getComponent() != null) {
        +     *  whereClause.appendSeparator(" and", " where");
        +     *  whereClause.append(" component = ?")
        +     * }
        +     * selectClause.append(whereClause)
        +     * 
        + * + * @param standard the separator if builder is not empty, null means no separator + * @param defaultIfEmpty the separator if builder is empty, null means no separator + * @return this, to enable chaining + * @since 2.5 + */ + public StrBuilder appendSeparator(String standard, String defaultIfEmpty) { + String str = isEmpty() ? defaultIfEmpty : standard; + if (str != null) { + append(str); + } + return this; + } + + /** + * Appends a separator if the builder is currently non-empty. + * The separator is appended using {@link #append(char)}. + *

        + * This method is useful for adding a separator each time around the + * loop except the first. + *

        +     * for (Iterator it = list.iterator(); it.hasNext(); ) {
        +     *   appendSeparator(',');
        +     *   append(it.next());
        +     * }
        +     * 
        + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Collection, String)}. + * + * @param separator the separator to use + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(char separator) { + if (size() > 0) { + append(separator); + } + return this; + } + + /** + * Append one of both separators to the builder + * If the builder is currently empty it will append the defaultIfEmpty-separator + * Otherwise it will append the standard-separator + * + * The separator is appended using {@link #append(char)}. + * @param standard the separator if builder is not empty + * @param defaultIfEmpty the separator if builder is empty + * @return this, to enable chaining + * @since 2.5 + */ + public StrBuilder appendSeparator(char standard, char defaultIfEmpty) { + if (size() > 0) { + append(standard); + } + else { + append(defaultIfEmpty); + } + return this; + } + /** + * Appends a separator to the builder if the loop index is greater than zero. + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

        + * This method is useful for adding a separator each time around the + * loop except the first. + *

        +     * for (int i = 0; i < list.size(); i++) {
        +     *   appendSeparator(",", i);
        +     *   append(list.get(i));
        +     * }
        +     * 
        + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Collection, String)}. + * + * @param separator the separator to use, null means no separator + * @param loopIndex the loop index + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(String separator, int loopIndex) { + if (separator != null && loopIndex > 0) { + append(separator); + } + return this; + } + + /** + * Appends a separator to the builder if the loop index is greater than zero. + * The separator is appended using {@link #append(char)}. + *

        + * This method is useful for adding a separator each time around the + * loop except the first. + *

        +     * for (int i = 0; i < list.size(); i++) {
        +     *   appendSeparator(",", i);
        +     *   append(list.get(i));
        +     * }
        +     * 
        + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Collection, String)}. + * + * @param separator the separator to use + * @param loopIndex the loop index + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(char separator, int loopIndex) { + if (loopIndex > 0) { + append(separator); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends the pad character to the builder the specified number of times. + * + * @param length the length to append, negative means no append + * @param padChar the character to append + * @return this, to enable chaining + */ + public StrBuilder appendPadding(int length, char padChar) { + if (length >= 0) { + ensureCapacity(size + length); + for (int i = 0; i < length; i++) { + buffer[size++] = padChar; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends an object to the builder padding on the left to a fixed width. + * The toString of the object is used. + * If the object is larger than the length, the left hand side is lost. + * If the object is null, the null text value is used. + * + * @param obj the object to append, null uses null text + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadLeft(Object obj, int width, char padChar) { + if (width > 0) { + ensureCapacity(size + width); + String str = (obj == null ? getNullText() : obj.toString()); + if (str == null) { + str = ""; + } + int strLen = str.length(); + if (strLen >= width) { + str.getChars(strLen - width, strLen, buffer, size); + } else { + int padLen = width - strLen; + for (int i = 0; i < padLen; i++) { + buffer[size + i] = padChar; + } + str.getChars(0, strLen, buffer, size + padLen); + } + size += width; + } + return this; + } + + /** + * Appends an object to the builder padding on the left to a fixed width. + * The String.valueOf of the int value is used. + * If the formatted value is larger than the length, the left hand side is lost. + * + * @param value the value to append + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadLeft(int value, int width, char padChar) { + return appendFixedWidthPadLeft(String.valueOf(value), width, padChar); + } + + /** + * Appends an object to the builder padding on the right to a fixed length. + * The toString of the object is used. + * If the object is larger than the length, the right hand side is lost. + * If the object is null, null text value is used. + * + * @param obj the object to append, null uses null text + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadRight(Object obj, int width, char padChar) { + if (width > 0) { + ensureCapacity(size + width); + String str = (obj == null ? getNullText() : obj.toString()); + if (str == null) { + str = ""; + } + int strLen = str.length(); + if (strLen >= width) { + str.getChars(0, width, buffer, size); + } else { + int padLen = width - strLen; + str.getChars(0, strLen, buffer, size); + for (int i = 0; i < padLen; i++) { + buffer[size + strLen + i] = padChar; + } + } + size += width; + } + return this; + } + + /** + * Appends an object to the builder padding on the right to a fixed length. + * The String.valueOf of the int value is used. + * If the object is larger than the length, the right hand side is lost. + * + * @param value the value to append + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadRight(int value, int width, char padChar) { + return appendFixedWidthPadRight(String.valueOf(value), width, padChar); + } + + //----------------------------------------------------------------------- + /** + * Inserts the string representation of an object into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param obj the object to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, Object obj) { + if (obj == null) { + return insert(index, nullText); + } + return insert(index, obj.toString()); + } + + /** + * Inserts the string into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param str the string to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, String str) { + validateIndex(index); + if (str == null) { + str = nullText; + } + int strLen = (str == null ? 0 : str.length()); + if (strLen > 0) { + int newSize = size + strLen; + ensureCapacity(newSize); + System.arraycopy(buffer, index, buffer, index + strLen, size - index); + size = newSize; + str.getChars(0, strLen, buffer, index); // str cannot be null here + } + return this; + } + + /** + * Inserts the character array into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param chars the char array to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, char chars[]) { + validateIndex(index); + if (chars == null) { + return insert(index, nullText); + } + int len = chars.length; + if (len > 0) { + ensureCapacity(size + len); + System.arraycopy(buffer, index, buffer, index + len, size - index); + System.arraycopy(chars, 0, buffer, index, len); + size += len; + } + return this; + } + + /** + * Inserts part of the character array into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param chars the char array to insert + * @param offset the offset into the character array to start at, must be valid + * @param length the length of the character array part to copy, must be positive + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if any index is invalid + */ + public StrBuilder insert(int index, char chars[], int offset, int length) { + validateIndex(index); + if (chars == null) { + return insert(index, nullText); + } + if (offset < 0 || offset > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid offset: " + offset); + } + if (length < 0 || offset + length > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid length: " + length); + } + if (length > 0) { + ensureCapacity(size + length); + System.arraycopy(buffer, index, buffer, index + length, size - index); + System.arraycopy(chars, offset, buffer, index, length); + size += length; + } + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, boolean value) { + validateIndex(index); + if (value) { + ensureCapacity(size + 4); + System.arraycopy(buffer, index, buffer, index + 4, size - index); + buffer[index++] = 't'; + buffer[index++] = 'r'; + buffer[index++] = 'u'; + buffer[index] = 'e'; + size += 4; + } else { + ensureCapacity(size + 5); + System.arraycopy(buffer, index, buffer, index + 5, size - index); + buffer[index++] = 'f'; + buffer[index++] = 'a'; + buffer[index++] = 'l'; + buffer[index++] = 's'; + buffer[index] = 'e'; + size += 5; + } + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, char value) { + validateIndex(index); + ensureCapacity(size + 1); + System.arraycopy(buffer, index, buffer, index + 1, size - index); + buffer[index] = value; + size++; + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, int value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, long value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, float value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, double value) { + return insert(index, String.valueOf(value)); + } + + //----------------------------------------------------------------------- + /** + * Internal method to delete a range without validation. + * + * @param startIndex the start index, must be valid + * @param endIndex the end index (exclusive), must be valid + * @param len the length, must be valid + * @throws IndexOutOfBoundsException if any index is invalid + */ + private void deleteImpl(int startIndex, int endIndex, int len) { + System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex); + size -= len; + } + + /** + * Deletes the characters between the two specified indices. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder delete(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + int len = endIndex - startIndex; + if (len > 0) { + deleteImpl(startIndex, endIndex, len); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes the character wherever it occurs in the builder. + * + * @param ch the character to delete + * @return this, to enable chaining + */ + public StrBuilder deleteAll(char ch) { + for (int i = 0; i < size; i++) { + if (buffer[i] == ch) { + int start = i; + while (++i < size) { + if (buffer[i] != ch) { + break; + } + } + int len = i - start; + deleteImpl(start, i, len); + i -= len; + } + } + return this; + } + + /** + * Deletes the character wherever it occurs in the builder. + * + * @param ch the character to delete + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(char ch) { + for (int i = 0; i < size; i++) { + if (buffer[i] == ch) { + deleteImpl(i, i + 1, 1); + break; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes the string wherever it occurs in the builder. + * + * @param str the string to delete, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteAll(String str) { + int len = (str == null ? 0 : str.length()); + if (len > 0) { + int index = indexOf(str, 0); + while (index >= 0) { + deleteImpl(index, index + len, len); + index = indexOf(str, index); + } + } + return this; + } + + /** + * Deletes the string wherever it occurs in the builder. + * + * @param str the string to delete, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(String str) { + int len = (str == null ? 0 : str.length()); + if (len > 0) { + int index = indexOf(str, 0); + if (index >= 0) { + deleteImpl(index, index + len, len); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes all parts of the builder that the matcher matches. + *

        + * Matchers can be used to perform advanced deletion behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteAll(StrMatcher matcher) { + return replace(matcher, null, 0, size, -1); + } + + /** + * Deletes the first match within the builder using the specified matcher. + *

        + * Matchers can be used to perform advanced deletion behaviour. + * For example you could write a matcher to delete + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(StrMatcher matcher) { + return replace(matcher, null, 0, size, 1); + } + + //----------------------------------------------------------------------- + /** + * Internal method to delete a range without validation. + * + * @param startIndex the start index, must be valid + * @param endIndex the end index (exclusive), must be valid + * @param removeLen the length to remove (endIndex - startIndex), must be valid + * @param insertStr the string to replace with, null means delete range + * @param insertLen the length of the insert string, must be valid + * @throws IndexOutOfBoundsException if any index is invalid + */ + private void replaceImpl(int startIndex, int endIndex, int removeLen, String insertStr, int insertLen) { + int newSize = size - removeLen + insertLen; + if (insertLen != removeLen) { + ensureCapacity(newSize); + System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex); + size = newSize; + } + if (insertLen > 0) { + insertStr.getChars(0, insertLen, buffer, startIndex); + } + } + + /** + * Replaces a portion of the string builder with another string. + * The length of the inserted string does not have to match the removed length. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @param replaceStr the string to replace with, null means delete range + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder replace(int startIndex, int endIndex, String replaceStr) { + endIndex = validateRange(startIndex, endIndex); + int insertLen = (replaceStr == null ? 0 : replaceStr.length()); + replaceImpl(startIndex, endIndex, endIndex - startIndex, replaceStr, insertLen); + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces the search character with the replace character + * throughout the builder. + * + * @param search the search character + * @param replace the replace character + * @return this, to enable chaining + */ + public StrBuilder replaceAll(char search, char replace) { + if (search != replace) { + for (int i = 0; i < size; i++) { + if (buffer[i] == search) { + buffer[i] = replace; + } + } + } + return this; + } + + /** + * Replaces the first instance of the search character with the + * replace character in the builder. + * + * @param search the search character + * @param replace the replace character + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(char search, char replace) { + if (search != replace) { + for (int i = 0; i < size; i++) { + if (buffer[i] == search) { + buffer[i] = replace; + break; + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces the search string with the replace string throughout the builder. + * + * @param searchStr the search string, null causes no action to occur + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceAll(String searchStr, String replaceStr) { + int searchLen = (searchStr == null ? 0 : searchStr.length()); + if (searchLen > 0) { + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + int index = indexOf(searchStr, 0); + while (index >= 0) { + replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); + index = indexOf(searchStr, index + replaceLen); + } + } + return this; + } + + /** + * Replaces the first instance of the search string with the replace string. + * + * @param searchStr the search string, null causes no action to occur + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(String searchStr, String replaceStr) { + int searchLen = (searchStr == null ? 0 : searchStr.length()); + if (searchLen > 0) { + int index = indexOf(searchStr, 0); + if (index >= 0) { + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces all matches within the builder with the replace string. + *

        + * Matchers can be used to perform advanced replace behaviour. + * For example you could write a matcher to replace all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceAll(StrMatcher matcher, String replaceStr) { + return replace(matcher, replaceStr, 0, size, -1); + } + + /** + * Replaces the first match within the builder with the replace string. + *

        + * Matchers can be used to perform advanced replace behaviour. + * For example you could write a matcher to replace + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(StrMatcher matcher, String replaceStr) { + return replace(matcher, replaceStr, 0, size, 1); + } + + // ----------------------------------------------------------------------- + /** + * Advanced search and replaces within the builder using a matcher. + *

        + * Matchers can be used to perform advanced behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the string to replace the match with, null is a delete + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @param replaceCount the number of times to replace, -1 for replace all + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if start index is invalid + */ + public StrBuilder replace( + StrMatcher matcher, String replaceStr, + int startIndex, int endIndex, int replaceCount) { + endIndex = validateRange(startIndex, endIndex); + return replaceImpl(matcher, replaceStr, startIndex, endIndex, replaceCount); + } + + /** + * Replaces within the builder using a matcher. + *

        + * Matchers can be used to perform advanced behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the string to replace the match with, null is a delete + * @param from the start index, must be valid + * @param to the end index (exclusive), must be valid + * @param replaceCount the number of times to replace, -1 for replace all + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if any index is invalid + */ + private StrBuilder replaceImpl( + StrMatcher matcher, String replaceStr, + int from, int to, int replaceCount) { + if (matcher == null || size == 0) { + return this; + } + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + char[] buf = buffer; + for (int i = from; i < to && replaceCount != 0; i++) { + int removeLen = matcher.isMatch(buf, i, from, to); + if (removeLen > 0) { + replaceImpl(i, i + removeLen, removeLen, replaceStr, replaceLen); + to = to - removeLen + replaceLen; + i = i + replaceLen - 1; + if (replaceCount > 0) { + replaceCount--; + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Reverses the string builder placing each character in the opposite index. + * + * @return this, to enable chaining + */ + public StrBuilder reverse() { + if (size == 0) { + return this; + } + + int half = size / 2; + char[] buf = buffer; + for (int leftIdx = 0, rightIdx = size - 1; leftIdx < half; leftIdx++,rightIdx--) { + char swap = buf[leftIdx]; + buf[leftIdx] = buf[rightIdx]; + buf[rightIdx] = swap; + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Trims the builder by removing characters less than or equal to a space + * from the beginning and end. + * + * @return this, to enable chaining + */ + public StrBuilder trim() { + if (size == 0) { + return this; + } + int len = size; + char[] buf = buffer; + int pos = 0; + while (pos < len && buf[pos] <= ' ') { + pos++; + } + while (pos < len && buf[len - 1] <= ' ') { + len--; + } + if (len < size) { + delete(len, size); + } + if (pos > 0) { + delete(0, pos); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Checks whether this builder starts with the specified string. + *

        + * Note that this method handles null input quietly, unlike String. + * + * @param str the string to search for, null returns false + * @return true if the builder starts with the string + */ + public boolean startsWith(String str) { + if (str == null) { + return false; + } + int len = str.length(); + if (len == 0) { + return true; + } + if (len > size) { + return false; + } + for (int i = 0; i < len; i++) { + if (buffer[i] != str.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Checks whether this builder ends with the specified string. + *

        + * Note that this method handles null input quietly, unlike String. + * + * @param str the string to search for, null returns false + * @return true if the builder ends with the string + */ + public boolean endsWith(String str) { + if (str == null) { + return false; + } + int len = str.length(); + if (len == 0) { + return true; + } + if (len > size) { + return false; + } + int pos = size - len; + for (int i = 0; i < len; i++,pos++) { + if (buffer[pos] != str.charAt(i)) { + return false; + } + } + return true; + } + + //----------------------------------------------------------------------- + /** + * Extracts a portion of this string builder as a string. + * + * @param start the start index, inclusive, must be valid + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + public String substring(int start) { + return substring(start, size); + } + + /** + * Extracts a portion of this string builder as a string. + *

        + * Note: This method treats an endIndex greater than the length of the + * builder as equal to the length of the builder, and continues + * without error, unlike StringBuffer or String. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + public String substring(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + return new String(buffer, startIndex, endIndex - startIndex); + } + + /** + * Extracts the leftmost characters from the string builder without + * throwing an exception. + *

        + * This method extracts the left length characters from + * the builder. If this many characters are not available, the whole + * builder is returned. Thus the returned string may be shorter than the + * length requested. + * + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String leftString(int length) { + if (length <= 0) { + return ""; + } else if (length >= size) { + return new String(buffer, 0, size); + } else { + return new String(buffer, 0, length); + } + } + + /** + * Extracts the rightmost characters from the string builder without + * throwing an exception. + *

        + * This method extracts the right length characters from + * the builder. If this many characters are not available, the whole + * builder is returned. Thus the returned string may be shorter than the + * length requested. + * + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String rightString(int length) { + if (length <= 0) { + return ""; + } else if (length >= size) { + return new String(buffer, 0, size); + } else { + return new String(buffer, size - length, length); + } + } + + /** + * Extracts some characters from the middle of the string builder without + * throwing an exception. + *

        + * This method extracts length characters from the builder + * at the specified index. + * If the index is negative it is treated as zero. + * If the index is greater than the builder size, it is treated as the builder size. + * If the length is negative, the empty string is returned. + * If insufficient characters are available in the builder, as much as possible is returned. + * Thus the returned string may be shorter than the length requested. + * + * @param index the index to start at, negative means zero + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String midString(int index, int length) { + if (index < 0) { + index = 0; + } + if (length <= 0 || index >= size) { + return ""; + } + if (size <= index + length) { + return new String(buffer, index, size - index); + } else { + return new String(buffer, index, length); + } + } + + //----------------------------------------------------------------------- + /** + * Checks if the string builder contains the specified char. + * + * @param ch the character to find + * @return true if the builder contains the character + */ + public boolean contains(char ch) { + char[] thisBuf = buffer; + for (int i = 0; i < this.size; i++) { + if (thisBuf[i] == ch) { + return true; + } + } + return false; + } + + /** + * Checks if the string builder contains the specified string. + * + * @param str the string to find + * @return true if the builder contains the string + */ + public boolean contains(String str) { + return indexOf(str, 0) >= 0; + } + + /** + * Checks if the string builder contains a string matched using the + * specified matcher. + *

        + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to search for the character + * 'a' followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return true if the matcher finds a match in the builder + */ + public boolean contains(StrMatcher matcher) { + return indexOf(matcher, 0) >= 0; + } + + //----------------------------------------------------------------------- + /** + * Searches the string builder to find the first reference to the specified char. + * + * @param ch the character to find + * @return the first index of the character, or -1 if not found + */ + public int indexOf(char ch) { + return indexOf(ch, 0); + } + + /** + * Searches the string builder to find the first reference to the specified char. + * + * @param ch the character to find + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index of the character, or -1 if not found + */ + public int indexOf(char ch, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (startIndex >= size) { + return -1; + } + char[] thisBuf = buffer; + for (int i = startIndex; i < size; i++) { + if (thisBuf[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Searches the string builder to find the first reference to the specified string. + *

        + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @return the first index of the string, or -1 if not found + */ + public int indexOf(String str) { + return indexOf(str, 0); + } + + /** + * Searches the string builder to find the first reference to the specified + * string starting searching from the given index. + *

        + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index of the string, or -1 if not found + */ + public int indexOf(String str, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (str == null || startIndex >= size) { + return -1; + } + int strLen = str.length(); + if (strLen == 1) { + return indexOf(str.charAt(0), startIndex); + } + if (strLen == 0) { + return startIndex; + } + if (strLen > size) { + return -1; + } + char[] thisBuf = buffer; + int len = size - strLen + 1; + outer: + for (int i = startIndex; i < len; i++) { + for (int j = 0; j < strLen; j++) { + if (str.charAt(j) != thisBuf[i + j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Searches the string builder using the matcher to find the first match. + *

        + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return the first index matched, or -1 if not found + */ + public int indexOf(StrMatcher matcher) { + return indexOf(matcher, 0); + } + + /** + * Searches the string builder using the matcher to find the first + * match searching from the given index. + *

        + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index matched, or -1 if not found + */ + public int indexOf(StrMatcher matcher, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (matcher == null || startIndex >= size) { + return -1; + } + int len = size; + char[] buf = buffer; + for (int i = startIndex; i < len; i++) { + if (matcher.isMatch(buf, i, startIndex, len) > 0) { + return i; + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + * Searches the string builder to find the last reference to the specified char. + * + * @param ch the character to find + * @return the last index of the character, or -1 if not found + */ + public int lastIndexOf(char ch) { + return lastIndexOf(ch, size - 1); + } + + /** + * Searches the string builder to find the last reference to the specified char. + * + * @param ch the character to find + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index of the character, or -1 if not found + */ + public int lastIndexOf(char ch, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (startIndex < 0) { + return -1; + } + for (int i = startIndex; i >= 0; i--) { + if (buffer[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Searches the string builder to find the last reference to the specified string. + *

        + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @return the last index of the string, or -1 if not found + */ + public int lastIndexOf(String str) { + return lastIndexOf(str, size - 1); + } + + /** + * Searches the string builder to find the last reference to the specified + * string starting searching from the given index. + *

        + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index of the string, or -1 if not found + */ + public int lastIndexOf(String str, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (str == null || startIndex < 0) { + return -1; + } + int strLen = str.length(); + if (strLen > 0 && strLen <= size) { + if (strLen == 1) { + return lastIndexOf(str.charAt(0), startIndex); + } + + outer: + for (int i = startIndex - strLen + 1; i >= 0; i--) { + for (int j = 0; j < strLen; j++) { + if (str.charAt(j) != buffer[i + j]) { + continue outer; + } + } + return i; + } + + } else if (strLen == 0) { + return startIndex; + } + return -1; + } + + /** + * Searches the string builder using the matcher to find the last match. + *

        + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return the last index matched, or -1 if not found + */ + public int lastIndexOf(StrMatcher matcher) { + return lastIndexOf(matcher, size); + } + + /** + * Searches the string builder using the matcher to find the last + * match searching from the given index. + *

        + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index matched, or -1 if not found + */ + public int lastIndexOf(StrMatcher matcher, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (matcher == null || startIndex < 0) { + return -1; + } + char[] buf = buffer; + int endIndex = startIndex + 1; + for (int i = startIndex; i >= 0; i--) { + if (matcher.isMatch(buf, i, 0, endIndex) > 0) { + return i; + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + * Creates a tokenizer that can tokenize the contents of this builder. + *

        + * This method allows the contents of this builder to be tokenized. + * The tokenizer will be setup by default to tokenize on space, tab, + * newline and formfeed (as per StringTokenizer). These values can be + * changed on the tokenizer class, before retrieving the tokens. + *

        + * The returned tokenizer is linked to this builder. You may intermix + * calls to the buider and tokenizer within certain limits, however + * there is no synchronization. Once the tokenizer has been used once, + * it must be {@link StrTokenizer#reset() reset} to pickup the latest + * changes in the builder. For example: + *

        +     * StrBuilder b = new StrBuilder();
        +     * b.append("a b ");
        +     * StrTokenizer t = b.asTokenizer();
        +     * String[] tokens1 = t.getTokenArray();  // returns a,b
        +     * b.append("c d ");
        +     * String[] tokens2 = t.getTokenArray();  // returns a,b (c and d ignored)
        +     * t.reset();              // reset causes builder changes to be picked up
        +     * String[] tokens3 = t.getTokenArray();  // returns a,b,c,d
        +     * 
        + * In addition to simply intermixing appends and tokenization, you can also + * call the set methods on the tokenizer to alter how it tokenizes. Just + * remember to call reset when you want to pickup builder changes. + *

        + * Calling {@link StrTokenizer#reset(String)} or {@link StrTokenizer#reset(char[])} + * with a non-null value will break the link with the builder. + * + * @return a tokenizer that is linked to this builder + */ + public StrTokenizer asTokenizer() { + return new StrBuilderTokenizer(); + } + + //----------------------------------------------------------------------- + /** + * Gets the contents of this builder as a Reader. + *

        + * This method allows the contents of the builder to be read + * using any standard method that expects a Reader. + *

        + * To use, simply create a StrBuilder, populate it with + * data, call asReader, and then read away. + *

        + * The internal character array is shared between the builder and the reader. + * This allows you to append to the builder after creating the reader, + * and the changes will be picked up. + * Note however, that no synchronization occurs, so you must perform + * all operations with the builder and the reader in one thread. + *

        + * The returned reader supports marking, and ignores the flush method. + * + * @return a reader that reads from this builder + */ + public Reader asReader() { + return new StrBuilderReader(); + } + + //----------------------------------------------------------------------- + /** + * Gets this builder as a Writer that can be written to. + *

        + * This method allows you to populate the contents of the builder + * using any standard method that takes a Writer. + *

        + * To use, simply create a StrBuilder, + * call asWriter, and populate away. The data is available + * at any time using the methods of the StrBuilder. + *

        + * The internal character array is shared between the builder and the writer. + * This allows you to intermix calls that append to the builder and + * write using the writer and the changes will be occur correctly. + * Note however, that no synchronization occurs, so you must perform + * all operations with the builder and the writer in one thread. + *

        + * The returned writer ignores the close and flush methods. + * + * @return a writer that populates this builder + */ + public Writer asWriter() { + return new StrBuilderWriter(); + } + + //----------------------------------------------------------------------- +// /** +// * Gets a String version of the string builder by calling the internal +// * constructor of String by reflection. +// *

        +// * WARNING: You must not use the StrBuilder after calling this method +// * as the buffer is now shared with the String object. To ensure this, +// * the internal character array is set to null, so you will get +// * NullPointerExceptions on all method calls. +// * +// * @return the builder as a String +// */ +// public String toSharedString() { +// try { +// Constructor con = String.class.getDeclaredConstructor( +// new Class[] {int.class, int.class, char[].class}); +// con.setAccessible(true); +// char[] buffer = buf; +// buf = null; +// size = -1; +// nullText = null; +// return (String) con.newInstance( +// new Object[] {new Integer(0), new Integer(size), buffer}); +// +// } catch (Exception ex) { +// ex.printStackTrace(); +// throw new UnsupportedOperationException("StrBuilder.toSharedString is unsupported: " + ex.getMessage()); +// } +// } + + //----------------------------------------------------------------------- + /** + * Checks the contents of this builder against another to see if they + * contain the same character content ignoring case. + * + * @param other the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + public boolean equalsIgnoreCase(StrBuilder other) { + if (this == other) { + return true; + } + if (this.size != other.size) { + return false; + } + char thisBuf[] = this.buffer; + char otherBuf[] = other.buffer; + for (int i = size - 1; i >= 0; i--) { + char c1 = thisBuf[i]; + char c2 = otherBuf[i]; + if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) { + return false; + } + } + return true; + } + + /** + * Checks the contents of this builder against another to see if they + * contain the same character content. + * + * @param other the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + public boolean equals(StrBuilder other) { + if (this == other) { + return true; + } + if (this.size != other.size) { + return false; + } + char thisBuf[] = this.buffer; + char otherBuf[] = other.buffer; + for (int i = size - 1; i >= 0; i--) { + if (thisBuf[i] != otherBuf[i]) { + return false; + } + } + return true; + } + + /** + * Checks the contents of this builder against another to see if they + * contain the same character content. + * + * @param obj the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + public boolean equals(Object obj) { + if (obj instanceof StrBuilder) { + return equals((StrBuilder) obj); + } + return false; + } + + /** + * Gets a suitable hash code for this builder. + * + * @return a hash code + */ + public int hashCode() { + char buf[] = buffer; + int hash = 0; + for (int i = size - 1; i >= 0; i--) { + hash = 31 * hash + buf[i]; + } + return hash; + } + + //----------------------------------------------------------------------- + /** + * Gets a String version of the string builder, creating a new instance + * each time the method is called. + *

        + * Note that unlike StringBuffer, the string version returned is + * independent of the string builder. + * + * @return the builder as a String + */ + public String toString() { + return new String(buffer, 0, size); + } + + /** + * Gets a StringBuffer version of the string builder, creating a + * new instance each time the method is called. + * + * @return the builder as a StringBuffer + */ + public StringBuffer toStringBuffer() { + return new StringBuffer(size).append(buffer, 0, size); + } + + /** + * Clone this object. + * + * @return a clone of this object + * @throws CloneNotSupportedException if clone is not supported + * @since 2.6 + */ + public Object clone() throws CloneNotSupportedException { + StrBuilder clone = (StrBuilder)super.clone(); + clone.buffer = new char[buffer.length]; + System.arraycopy(buffer, 0, clone.buffer, 0, buffer.length); + return clone; + } + + //----------------------------------------------------------------------- + /** + * Validates parameters defining a range of the builder. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + protected int validateRange(int startIndex, int endIndex) { + if (startIndex < 0) { + throw new StringIndexOutOfBoundsException(startIndex); + } + if (endIndex > size) { + endIndex = size; + } + if (startIndex > endIndex) { + throw new StringIndexOutOfBoundsException("end < start"); + } + return endIndex; + } + + /** + * Validates parameters defining a single index in the builder. + * + * @param index the index, must be valid + * @throws IndexOutOfBoundsException if the index is invalid + */ + protected void validateIndex(int index) { + if (index < 0 || index > size) { + throw new StringIndexOutOfBoundsException(index); + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a tokenizer. + */ + class StrBuilderTokenizer extends StrTokenizer { + + /** + * Default constructor. + */ + StrBuilderTokenizer() { + super(); + } + + /** {@inheritDoc} */ + protected List tokenize(char[] chars, int offset, int count) { + if (chars == null) { + return super.tokenize(StrBuilder.this.buffer, 0, StrBuilder.this.size()); + } else { + return super.tokenize(chars, offset, count); + } + } + + /** {@inheritDoc} */ + public String getContent() { + String str = super.getContent(); + if (str == null) { + return StrBuilder.this.toString(); + } else { + return str; + } + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a writer. + */ + class StrBuilderReader extends Reader { + /** The current stream position. */ + private int pos; + /** The last mark position. */ + private int mark; + + /** + * Default constructor. + */ + StrBuilderReader() { + super(); + } + + /** {@inheritDoc} */ + public void close() { + // do nothing + } + + /** {@inheritDoc} */ + public int read() { + if (ready() == false) { + return -1; + } + return StrBuilder.this.charAt(pos++); + } + + /** {@inheritDoc} */ + public int read(char b[], int off, int len) { + if (off < 0 || len < 0 || off > b.length || + (off + len) > b.length || (off + len) < 0) { + throw new IndexOutOfBoundsException(); + } + if (len == 0) { + return 0; + } + if (pos >= StrBuilder.this.size()) { + return -1; + } + if (pos + len > size()) { + len = StrBuilder.this.size() - pos; + } + StrBuilder.this.getChars(pos, pos + len, b, off); + pos += len; + return len; + } + + /** {@inheritDoc} */ + public long skip(long n) { + if (pos + n > StrBuilder.this.size()) { + n = StrBuilder.this.size() - pos; + } + if (n < 0) { + return 0; + } + pos += n; + return n; + } + + /** {@inheritDoc} */ + public boolean ready() { + return pos < StrBuilder.this.size(); + } + + /** {@inheritDoc} */ + public boolean markSupported() { + return true; + } + + /** {@inheritDoc} */ + public void mark(int readAheadLimit) { + mark = pos; + } + + /** {@inheritDoc} */ + public void reset() { + pos = mark; + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a writer. + */ + class StrBuilderWriter extends Writer { + + /** + * Default constructor. + */ + StrBuilderWriter() { + super(); + } + + /** {@inheritDoc} */ + public void close() { + // do nothing + } + + /** {@inheritDoc} */ + public void flush() { + // do nothing + } + + /** {@inheritDoc} */ + public void write(int c) { + StrBuilder.this.append((char) c); + } + + /** {@inheritDoc} */ + public void write(char[] cbuf) { + StrBuilder.this.append(cbuf); + } + + /** {@inheritDoc} */ + public void write(char[] cbuf, int off, int len) { + StrBuilder.this.append(cbuf, off, len); + } + + /** {@inheritDoc} */ + public void write(String str) { + StrBuilder.this.append(str); + } + + /** {@inheritDoc} */ + public void write(String str, int off, int len) { + StrBuilder.this.append(str, off, len); + } + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrLookup.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrLookup.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrLookup.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.util.Map; + +/** + * Lookup a String key to a String value. + *

        + * This class represents the simplest form of a string to string map. + * It has a benefit over a map in that it can create the result on + * demand based on the key. + *

        + * This class comes complete with various factory methods. + * If these do not suffice, you can subclass and implement your own matcher. + *

        + * For example, it would be possible to implement a lookup that used the + * key as a primary key, and looked up the value on demand from the database + * + * @author Apache Software Foundation + * @since 2.2 + * @version $Id$ + */ +public abstract class StrLookup { + + /** + * Lookup that always returns null. + */ + private static final StrLookup NONE_LOOKUP; + /** + * Lookup that uses System properties. + */ + private static final StrLookup SYSTEM_PROPERTIES_LOOKUP; + static { + NONE_LOOKUP = new MapStrLookup(null); + StrLookup lookup = null; + try { + lookup = new MapStrLookup(System.getProperties()); + } catch (SecurityException ex) { + lookup = NONE_LOOKUP; + } + SYSTEM_PROPERTIES_LOOKUP = lookup; + } + + //----------------------------------------------------------------------- + /** + * Returns a lookup which always returns null. + * + * @return a lookup that always returns null, not null + */ + public static StrLookup noneLookup() { + return NONE_LOOKUP; + } + + /** + * Returns a lookup which uses {@link System#getProperties() System properties} + * to lookup the key to value. + *

        + * If a security manager blocked access to system properties, then null will + * be returned from every lookup. + *

        + * If a null key is used, this lookup will throw a NullPointerException. + * + * @return a lookup using system properties, not null + */ + public static StrLookup systemPropertiesLookup() { + return SYSTEM_PROPERTIES_LOOKUP; + } + + /** + * Returns a lookup which looks up values using a map. + *

        + * If the map is null, then null will be returned from every lookup. + * The map result object is converted to a string using toString(). + * + * @param map the map of keys to values, may be null + * @return a lookup using the map, not null + */ + public static StrLookup mapLookup(Map map) { + return new MapStrLookup(map); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + */ + protected StrLookup() { + super(); + } + + /** + * Looks up a String key to a String value. + *

        + * The internal implementation may use any mechanism to return the value. + * The simplest implementation is to use a Map. However, virtually any + * implementation is possible. + *

        + * For example, it would be possible to implement a lookup that used the + * key as a primary key, and looked up the value on demand from the database + * Or, a numeric based implementation could be created that treats the key + * as an integer, increments the value and return the result as a string - + * converting 1 to 2, 15 to 16 etc. + *

        + * The {@link #lookup(String)} method always returns a String, regardless of + * the underlying data, by converting it as necessary. For example: + *

        +     * Map map = new HashMap();
        +     * map.put("number", new Integer(2));
        +     * assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
        +     * 
        + * @param key the key to be looked up, may be null + * @return the matching value, null if no match + */ + public abstract String lookup(String key); + + //----------------------------------------------------------------------- + /** + * Lookup implementation that uses a Map. + */ + static class MapStrLookup extends StrLookup { + + /** Map keys are variable names and value. */ + private final Map map; + + /** + * Creates a new instance backed by a Map. + * + * @param map the map of keys to values, may be null + */ + MapStrLookup(Map map) { + this.map = map; + } + + /** + * Looks up a String key to a String value using the map. + *

        + * If the map is null, then null is returned. + * The map result object is converted to a string using toString(). + * + * @param key the key to be looked up, may be null + * @return the matching value, null if no match + */ + public String lookup(String key) { + if (map == null) { + return null; + } + Object obj = map.get(key); + if (obj == null) { + return null; + } + return obj.toString(); + } + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrMatcher.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrMatcher.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrMatcher.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,430 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.util.Arrays; + +/** + * A matcher class that can be queried to determine if a character array + * portion matches. + *

        + * This class comes complete with various factory methods. + * If these do not suffice, you can subclass and implement your own matcher. + * + * @author Apache Software Foundation + * @since 2.2 + * @version $Id$ + */ +public abstract class StrMatcher { + + /** + * Matches the comma character. + */ + private static final StrMatcher COMMA_MATCHER = new CharMatcher(','); + /** + * Matches the tab character. + */ + private static final StrMatcher TAB_MATCHER = new CharMatcher('\t'); + /** + * Matches the space character. + */ + private static final StrMatcher SPACE_MATCHER = new CharMatcher(' '); + /** + * Matches the same characters as StringTokenizer, + * namely space, tab, newline, formfeed. + */ + private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray()); + /** + * Matches the String trim() whitespace characters. + */ + private static final StrMatcher TRIM_MATCHER = new TrimMatcher(); + /** + * Matches the double quote character. + */ + private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\''); + /** + * Matches the double quote character. + */ + private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"'); + /** + * Matches the single or double quote character. + */ + private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray()); + /** + * Matches no characters. + */ + private static final StrMatcher NONE_MATCHER = new NoMatcher(); + + // ----------------------------------------------------------------------- + + /** + * Returns a matcher which matches the comma character. + * + * @return a matcher for a comma + */ + public static StrMatcher commaMatcher() { + return COMMA_MATCHER; + } + + /** + * Returns a matcher which matches the tab character. + * + * @return a matcher for a tab + */ + public static StrMatcher tabMatcher() { + return TAB_MATCHER; + } + + /** + * Returns a matcher which matches the space character. + * + * @return a matcher for a space + */ + public static StrMatcher spaceMatcher() { + return SPACE_MATCHER; + } + + /** + * Matches the same characters as StringTokenizer, + * namely space, tab, newline and formfeed. + * + * @return the split matcher + */ + public static StrMatcher splitMatcher() { + return SPLIT_MATCHER; + } + + /** + * Matches the String trim() whitespace characters. + * + * @return the trim matcher + */ + public static StrMatcher trimMatcher() { + return TRIM_MATCHER; + } + + /** + * Returns a matcher which matches the single quote character. + * + * @return a matcher for a single quote + */ + public static StrMatcher singleQuoteMatcher() { + return SINGLE_QUOTE_MATCHER; + } + + /** + * Returns a matcher which matches the double quote character. + * + * @return a matcher for a double quote + */ + public static StrMatcher doubleQuoteMatcher() { + return DOUBLE_QUOTE_MATCHER; + } + + /** + * Returns a matcher which matches the single or double quote character. + * + * @return a matcher for a single or double quote + */ + public static StrMatcher quoteMatcher() { + return QUOTE_MATCHER; + } + + /** + * Matches no characters. + * + * @return a matcher that matches nothing + */ + public static StrMatcher noneMatcher() { + return NONE_MATCHER; + } + + /** + * Constructor that creates a matcher from a character. + * + * @param ch the character to match, must not be null + * @return a new Matcher for the given char + */ + public static StrMatcher charMatcher(char ch) { + return new CharMatcher(ch); + } + + /** + * Constructor that creates a matcher from a set of characters. + * + * @param chars the characters to match, null or empty matches nothing + * @return a new matcher for the given char[] + */ + public static StrMatcher charSetMatcher(char[] chars) { + if (chars == null || chars.length == 0) { + return NONE_MATCHER; + } + if (chars.length == 1) { + return new CharMatcher(chars[0]); + } + return new CharSetMatcher(chars); + } + + /** + * Constructor that creates a matcher from a string representing a set of characters. + * + * @param chars the characters to match, null or empty matches nothing + * @return a new Matcher for the given characters + */ + public static StrMatcher charSetMatcher(String chars) { + if (chars == null || chars.length() == 0) { + return NONE_MATCHER; + } + if (chars.length() == 1) { + return new CharMatcher(chars.charAt(0)); + } + return new CharSetMatcher(chars.toCharArray()); + } + + /** + * Constructor that creates a matcher from a string. + * + * @param str the string to match, null or empty matches nothing + * @return a new Matcher for the given String + */ + public static StrMatcher stringMatcher(String str) { + if (str == null || str.length() == 0) { + return NONE_MATCHER; + } + return new StringMatcher(str); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + */ + protected StrMatcher() { + super(); + } + + /** + * Returns the number of matching characters, zero for no match. + *

        + * This method is called to check for a match. + * The parameter pos represents the current position to be + * checked in the string buffer (a character array which must + * not be changed). + * The API guarantees that pos is a valid index for buffer. + *

        + * The character array may be larger than the active area to be matched. + * Only values in the buffer between the specifed indices may be accessed. + *

        + * The matching code may check one character or many. + * It may check characters preceeding pos as well as those + * after, so long as no checks exceed the bounds specified. + *

        + * It must return zero for no match, or a positive number if a match was found. + * The number indicates the number of characters that matched. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd); + + /** + * Returns the number of matching characters, zero for no match. + *

        + * This method is called to check for a match. + * The parameter pos represents the current position to be + * checked in the string buffer (a character array which must + * not be changed). + * The API guarantees that pos is a valid index for buffer. + *

        + * The matching code may check one character or many. + * It may check characters preceeding pos as well as those after. + *

        + * It must return zero for no match, or a positive number if a match was found. + * The number indicates the number of characters that matched. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @return the number of matching characters, zero for no match + * @since 2.4 + */ + public int isMatch(char[] buffer, int pos) { + return isMatch(buffer, pos, 0, buffer.length); + } + + //----------------------------------------------------------------------- + /** + * Class used to define a set of characters for matching purposes. + */ + static final class CharSetMatcher extends StrMatcher { + /** The set of characters to match. */ + private final char[] chars; + + /** + * Constructor that creates a matcher from a character array. + * + * @param chars the characters to match, must not be null + */ + CharSetMatcher(char chars[]) { + super(); + this.chars = (char[]) chars.clone(); + Arrays.sort(this.chars); + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to define a character for matching purposes. + */ + static final class CharMatcher extends StrMatcher { + /** The character to match. */ + private final char ch; + + /** + * Constructor that creates a matcher that matches a single character. + * + * @param ch the character to match + */ + CharMatcher(char ch) { + super(); + this.ch = ch; + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return ch == buffer[pos] ? 1 : 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to define a set of characters for matching purposes. + */ + static final class StringMatcher extends StrMatcher { + /** The string to match, as a character array. */ + private final char[] chars; + + /** + * Constructor that creates a matcher from a String. + * + * @param str the string to match, must not be null + */ + StringMatcher(String str) { + super(); + chars = str.toCharArray(); + } + + /** + * Returns whether or not the given text matches the stored string. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + int len = chars.length; + if (pos + len > bufferEnd) { + return 0; + } + for (int i = 0; i < chars.length; i++, pos++) { + if (chars[i] != buffer[pos]) { + return 0; + } + } + return len; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to match no characters. + */ + static final class NoMatcher extends StrMatcher { + + /** + * Constructs a new instance of NoMatcher. + */ + NoMatcher() { + super(); + } + + /** + * Always returns false. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to match whitespace as per trim(). + */ + static final class TrimMatcher extends StrMatcher { + + /** + * Constructs a new instance of TrimMatcher. + */ + TrimMatcher() { + super(); + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return buffer[pos] <= 32 ? 1 : 0; + } + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrSubstitutor.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrSubstitutor.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrSubstitutor.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,926 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Substitutes variables within a string by values. + *

        + * This class takes a piece of text and substitutes all the variables within it. + * The default definition of a variable is ${variableName}. + * The prefix and suffix can be changed via constructors and set methods. + *

        + * Variable values are typically resolved from a map, but could also be resolved + * from system properties, or by supplying a custom variable resolver. + *

        + * The simplest example is to use this class to replace Java System properties. For example: + *

        + * StrSubstitutor.replaceSystemProperties(
        + *      "You are running with java.version = ${java.version} and os.name = ${os.name}.");
        + * 
        + *

        + * Typical usage of this class follows the following pattern: First an instance is created + * and initialized with the map that contains the values for the available variables. + * If a prefix and/or suffix for variables should be used other than the default ones, + * the appropriate settings can be performed. After that the replace() + * method can be called passing in the source text for interpolation. In the returned + * text all variable references (as long as their values are known) will be resolved. + * The following example demonstrates this: + *

        + * Map valuesMap = HashMap();
        + * valuesMap.put("animal", "quick brown fox");
        + * valuesMap.put("target", "lazy dog");
        + * String templateString = "The ${animal} jumped over the ${target}.";
        + * StrSubstitutor sub = new StrSubstitutor(valuesMap);
        + * String resolvedString = sub.replace(templateString);
        + * 
        + * yielding: + *
        + *      The quick brown fox jumped over the lazy dog.
        + * 
        + *

        + * In addition to this usage pattern there are some static convenience methods that + * cover the most common use cases. These methods can be used without the need of + * manually creating an instance. However if multiple replace operations are to be + * performed, creating and reusing an instance of this class will be more efficient. + *

        + * Variable replacement works in a recursive way. Thus, if a variable value contains + * a variable then that variable will also be replaced. Cyclic replacements are + * detected and will cause an exception to be thrown. + *

        + * Sometimes the interpolation's result must contain a variable prefix. As an example + * take the following source text: + *

        + *   The variable ${${name}} must be used.
        + * 
        + * Here only the variable's name referred to in the text should be replaced resulting + * in the text (assuming that the value of the name variable is x): + *
        + *   The variable ${x} must be used.
        + * 
        + * To achieve this effect there are two possibilities: Either set a different prefix + * and suffix for variables which do not conflict with the result text you want to + * produce. The other possibility is to use the escape character, by default '$'. + * If this character is placed before a variable reference, this reference is ignored + * and won't be replaced. For example: + *
        + *   The variable $${${name}} must be used.
        + * 
        + *

        + * In some complex scenarios you might even want to perform substitution in the + * names of variables, for instance + *

        + * ${jre-${java.specification.version}}
        + * 
        + * StrSubstitutor supports this recursive substitution in variable + * names, but it has to be enabled explicitly by setting the + * {@link #setEnableSubstitutionInVariables(boolean) enableSubstitutionInVariables} + * property to true. + * + * @author Apache Software Foundation + * @author Oliver Heger + * @version $Id$ + * @since 2.2 + */ +public class StrSubstitutor { + + /** + * Constant for the default escape character. + */ + public static final char DEFAULT_ESCAPE = '$'; + /** + * Constant for the default variable prefix. + */ + public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher("${"); + /** + * Constant for the default variable suffix. + */ + public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}"); + + /** + * Stores the escape character. + */ + private char escapeChar; + /** + * Stores the variable prefix. + */ + private StrMatcher prefixMatcher; + /** + * Stores the variable suffix. + */ + private StrMatcher suffixMatcher; + /** + * Variable resolution is delegated to an implementor of VariableResolver. + */ + private StrLookup variableResolver; + /** + * The flag whether substitution in variable names is enabled. + */ + private boolean enableSubstitutionInVariables; + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the map. + * + * @param source the source text containing the variables to substitute, null returns null + * @param valueMap the map with the values, may be null + * @return the result of the replace operation + */ + public static String replace(Object source, Map valueMap) { + return new StrSubstitutor(valueMap).replace(source); + } + + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the map. This method allows to specifiy a + * custom variable prefix and suffix + * + * @param source the source text containing the variables to substitute, null returns null + * @param valueMap the map with the values, may be null + * @param prefix the prefix of variables, not null + * @param suffix the suffix of variables, not null + * @return the result of the replace operation + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public static String replace(Object source, Map valueMap, String prefix, String suffix) { + return new StrSubstitutor(valueMap, prefix, suffix).replace(source); + } + + /** + * Replaces all the occurrences of variables in the given source object with their matching + * values from the properties. + * + * @param source the source text containing the variables to substitute, null returns null + * @param valueProperties the properties with values, may be null + * @return the result of the replace operation + * @since 2.6 + */ + public static String replace(Object source, Properties valueProperties) + { + if (valueProperties == null) { + return source.toString(); + } + Map valueMap = new HashMap(); + Enumeration propNames = valueProperties.propertyNames(); + while (propNames.hasMoreElements()) + { + String propName = (String)propNames.nextElement(); + String propValue = valueProperties.getProperty(propName); + valueMap.put(propName, propValue); + } + return StrSubstitutor.replace(source, valueMap); + } + + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the system properties. + * + * @param source the source text containing the variables to substitute, null returns null + * @return the result of the replace operation + */ + public static String replaceSystemProperties(Object source) { + return new StrSubstitutor(StrLookup.systemPropertiesLookup()).replace(source); + } + + //----------------------------------------------------------------------- + /** + * Creates a new instance with defaults for variable prefix and suffix + * and the escaping character. + */ + public StrSubstitutor() { + this((StrLookup) null, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. Uses defaults for variable + * prefix and suffix and the escaping character. + * + * @param valueMap the map with the variables' values, may be null + */ + public StrSubstitutor(Map valueMap) { + this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. Uses a default escaping character. + * + * @param valueMap the map with the variables' values, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(Map valueMap, String prefix, String suffix) { + this(StrLookup.mapLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. + * + * @param valueMap the map with the variables' values, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(Map valueMap, String prefix, String suffix, char escape) { + this(StrLookup.mapLookup(valueMap), prefix, suffix, escape); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + */ + public StrSubstitutor(StrLookup variableResolver) { + this(variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(StrLookup variableResolver, String prefix, String suffix, char escape) { + this.setVariableResolver(variableResolver); + this.setVariablePrefix(prefix); + this.setVariableSuffix(suffix); + this.setEscapeChar(escape); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + * @param prefixMatcher the prefix for variables, not null + * @param suffixMatcher the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor( + StrLookup variableResolver, StrMatcher prefixMatcher, StrMatcher suffixMatcher, char escape) { + this.setVariableResolver(variableResolver); + this.setVariablePrefixMatcher(prefixMatcher); + this.setVariableSuffixMatcher(suffixMatcher); + this.setEscapeChar(escape); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source string as a template. + * + * @param source the string to replace in, null returns null + * @return the result of the replace operation + */ + public String replace(String source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source); + if (substitute(buf, 0, source.length()) == false) { + return source; + } + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source string as a template. + *

        + * Only the specified portion of the string will be processed. + * The rest of the string is not processed, and is not returned. + * + * @param source the string to replace in, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(String source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + if (substitute(buf, 0, length) == false) { + return source.substring(offset, offset + length); + } + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source array as a template. + * The array is not altered by this method. + * + * @param source the character array to replace in, not altered, null returns null + * @return the result of the replace operation + */ + public String replace(char[] source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length).append(source); + substitute(buf, 0, source.length); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source array as a template. + * The array is not altered by this method. + *

        + * Only the specified portion of the array will be processed. + * The rest of the array is not processed, and is not returned. + * + * @param source the character array to replace in, not altered, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(char[] source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source buffer as a template. + * The buffer is not altered by this method. + * + * @param source the buffer to use as a template, not changed, null returns null + * @return the result of the replace operation + */ + public String replace(StringBuffer source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length()).append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source buffer as a template. + * The buffer is not altered by this method. + *

        + * Only the specified portion of the buffer will be processed. + * The rest of the buffer is not processed, and is not returned. + * + * @param source the buffer to use as a template, not changed, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(StringBuffer source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source builder as a template. + * The builder is not altered by this method. + * + * @param source the builder to use as a template, not changed, null returns null + * @return the result of the replace operation + */ + public String replace(StrBuilder source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length()).append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source builder as a template. + * The builder is not altered by this method. + *

        + * Only the specified portion of the builder will be processed. + * The rest of the builder is not processed, and is not returned. + * + * @param source the builder to use as a template, not changed, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(StrBuilder source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the resolver. The input source object is + * converted to a string using toString and is not altered. + * + * @param source the source to replace in, null returns null + * @return the result of the replace operation + */ + public String replace(Object source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder().append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables within the given source buffer + * with their matching values from the resolver. + * The buffer is updated with the result. + * + * @param source the buffer to replace in, updated, null returns zero + * @return true if altered + */ + public boolean replaceIn(StringBuffer source) { + if (source == null) { + return false; + } + return replaceIn(source, 0, source.length()); + } + + /** + * Replaces all the occurrences of variables within the given source buffer + * with their matching values from the resolver. + * The buffer is updated with the result. + *

        + * Only the specified portion of the buffer will be processed. + * The rest of the buffer is not processed, but it is not deleted. + * + * @param source the buffer to replace in, updated, null returns zero + * @param offset the start offset within the array, must be valid + * @param length the length within the buffer to be processed, must be valid + * @return true if altered + */ + public boolean replaceIn(StringBuffer source, int offset, int length) { + if (source == null) { + return false; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + if (substitute(buf, 0, length) == false) { + return false; + } + source.replace(offset, offset + length, buf.toString()); + return true; + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables within the given source + * builder with their matching values from the resolver. + * + * @param source the builder to replace in, updated, null returns zero + * @return true if altered + */ + public boolean replaceIn(StrBuilder source) { + if (source == null) { + return false; + } + return substitute(source, 0, source.length()); + } + + /** + * Replaces all the occurrences of variables within the given source + * builder with their matching values from the resolver. + *

        + * Only the specified portion of the builder will be processed. + * The rest of the builder is not processed, but it is not deleted. + * + * @param source the builder to replace in, null returns zero + * @param offset the start offset within the array, must be valid + * @param length the length within the builder to be processed, must be valid + * @return true if altered + */ + public boolean replaceIn(StrBuilder source, int offset, int length) { + if (source == null) { + return false; + } + return substitute(source, offset, length); + } + + //----------------------------------------------------------------------- + /** + * Internal method that substitutes the variables. + *

        + * Most users of this class do not need to call this method. This method will + * be called automatically by another (public) method. + *

        + * Writers of subclasses can override this method if they need access to + * the substitution process at the start or end. + * + * @param buf the string builder to substitute into, not null + * @param offset the start offset within the builder, must be valid + * @param length the length within the builder to be processed, must be valid + * @return true if altered + */ + protected boolean substitute(StrBuilder buf, int offset, int length) { + return substitute(buf, offset, length, null) > 0; + } + + /** + * Recursive handler for multiple levels of interpolation. This is the main + * interpolation method, which resolves the values of all variable references + * contained in the passed in text. + * + * @param buf the string builder to substitute into, not null + * @param offset the start offset within the builder, must be valid + * @param length the length within the builder to be processed, must be valid + * @param priorVariables the stack keeping track of the replaced variables, may be null + * @return the length change that occurs, unless priorVariables is null when the int + * represents a boolean flag as to whether any change occurred. + */ + private int substitute(StrBuilder buf, int offset, int length, List priorVariables) { + StrMatcher prefixMatcher = getVariablePrefixMatcher(); + StrMatcher suffixMatcher = getVariableSuffixMatcher(); + char escape = getEscapeChar(); + + boolean top = (priorVariables == null); + boolean altered = false; + int lengthChange = 0; + char[] chars = buf.buffer; + int bufEnd = offset + length; + int pos = offset; + while (pos < bufEnd) { + int startMatchLen = prefixMatcher.isMatch(chars, pos, offset, + bufEnd); + if (startMatchLen == 0) { + pos++; + } else { + // found variable start marker + if (pos > offset && chars[pos - 1] == escape) { + // escaped + buf.deleteCharAt(pos - 1); + chars = buf.buffer; // in case buffer was altered + lengthChange--; + altered = true; + bufEnd--; + } else { + // find suffix + int startPos = pos; + pos += startMatchLen; + int endMatchLen = 0; + int nestedVarCount = 0; + while (pos < bufEnd) { + if (isEnableSubstitutionInVariables() + && (endMatchLen = prefixMatcher.isMatch(chars, + pos, offset, bufEnd)) != 0) { + // found a nested variable start + nestedVarCount++; + pos += endMatchLen; + continue; + } + + endMatchLen = suffixMatcher.isMatch(chars, pos, offset, + bufEnd); + if (endMatchLen == 0) { + pos++; + } else { + // found variable end marker + if (nestedVarCount == 0) { + String varName = new String(chars, startPos + + startMatchLen, pos - startPos + - startMatchLen); + if (isEnableSubstitutionInVariables()) { + StrBuilder bufName = new StrBuilder(varName); + substitute(bufName, 0, bufName.length()); + varName = bufName.toString(); + } + pos += endMatchLen; + int endPos = pos; + + // on the first call initialize priorVariables + if (priorVariables == null) { + priorVariables = new ArrayList(); + priorVariables.add(new String(chars, + offset, length)); + } + + // handle cyclic substitution + checkCyclicSubstitution(varName, priorVariables); + priorVariables.add(varName); + + // resolve the variable + String varValue = resolveVariable(varName, buf, + startPos, endPos); + if (varValue != null) { + // recursive replace + int varLen = varValue.length(); + buf.replace(startPos, endPos, varValue); + altered = true; + int change = substitute(buf, startPos, + varLen, priorVariables); + change = change + + (varLen - (endPos - startPos)); + pos += change; + bufEnd += change; + lengthChange += change; + chars = buf.buffer; // in case buffer was + // altered + } + + // remove variable from the cyclic stack + priorVariables + .remove(priorVariables.size() - 1); + break; + } else { + nestedVarCount--; + pos += endMatchLen; + } + } + } + } + } + } + if (top) { + return (altered ? 1 : 0); + } + return lengthChange; + } + + /** + * Checks if the specified variable is already in the stack (list) of variables. + * + * @param varName the variable name to check + * @param priorVariables the list of prior variables + */ + private void checkCyclicSubstitution(String varName, List priorVariables) { + if (priorVariables.contains(varName) == false) { + return; + } + StrBuilder buf = new StrBuilder(256); + buf.append("Infinite loop in property interpolation of "); + buf.append(priorVariables.remove(0)); + buf.append(": "); + buf.appendWithSeparators(priorVariables, "->"); + throw new IllegalStateException(buf.toString()); + } + + /** + * Internal method that resolves the value of a variable. + *

        + * Most users of this class do not need to call this method. This method is + * called automatically by the substitution process. + *

        + * Writers of subclasses can override this method if they need to alter + * how each substitution occurs. The method is passed the variable's name + * and must return the corresponding value. This implementation uses the + * {@link #getVariableResolver()} with the variable's name as the key. + * + * @param variableName the name of the variable, not null + * @param buf the buffer where the substitution is occurring, not null + * @param startPos the start position of the variable including the prefix, valid + * @param endPos the end position of the variable including the suffix, valid + * @return the variable's value or null if the variable is unknown + */ + protected String resolveVariable(String variableName, StrBuilder buf, int startPos, int endPos) { + StrLookup resolver = getVariableResolver(); + if (resolver == null) { + return null; + } + return resolver.lookup(variableName); + } + + // Escape + //----------------------------------------------------------------------- + /** + * Returns the escape character. + * + * @return the character used for escaping variable references + */ + public char getEscapeChar() { + return this.escapeChar; + } + + /** + * Sets the escape character. + * If this character is placed before a variable reference in the source + * text, this variable will be ignored. + * + * @param escapeCharacter the escape character (0 for disabling escaping) + */ + public void setEscapeChar(char escapeCharacter) { + this.escapeChar = escapeCharacter; + } + + // Prefix + //----------------------------------------------------------------------- + /** + * Gets the variable prefix matcher currently in use. + *

        + * The variable prefix is the characer or characters that identify the + * start of a variable. This prefix is expressed in terms of a matcher + * allowing advanced prefix matches. + * + * @return the prefix matcher in use + */ + public StrMatcher getVariablePrefixMatcher() { + return prefixMatcher; + } + + /** + * Sets the variable prefix matcher currently in use. + *

        + * The variable prefix is the characer or characters that identify the + * start of a variable. This prefix is expressed in terms of a matcher + * allowing advanced prefix matches. + * + * @param prefixMatcher the prefix matcher to use, null ignored + * @return this, to enable chaining + * @throws IllegalArgumentException if the prefix matcher is null + */ + public StrSubstitutor setVariablePrefixMatcher(StrMatcher prefixMatcher) { + if (prefixMatcher == null) { + throw new IllegalArgumentException("Variable prefix matcher must not be null!"); + } + this.prefixMatcher = prefixMatcher; + return this; + } + + /** + * Sets the variable prefix to use. + *

        + * The variable prefix is the character or characters that identify the + * start of a variable. This method allows a single character prefix to + * be easily set. + * + * @param prefix the prefix character to use + * @return this, to enable chaining + */ + public StrSubstitutor setVariablePrefix(char prefix) { + return setVariablePrefixMatcher(StrMatcher.charMatcher(prefix)); + } + + /** + * Sets the variable prefix to use. + *

        + * The variable prefix is the characer or characters that identify the + * start of a variable. This method allows a string prefix to be easily set. + * + * @param prefix the prefix for variables, not null + * @return this, to enable chaining + * @throws IllegalArgumentException if the prefix is null + */ + public StrSubstitutor setVariablePrefix(String prefix) { + if (prefix == null) { + throw new IllegalArgumentException("Variable prefix must not be null!"); + } + return setVariablePrefixMatcher(StrMatcher.stringMatcher(prefix)); + } + + // Suffix + //----------------------------------------------------------------------- + /** + * Gets the variable suffix matcher currently in use. + *

        + * The variable suffix is the characer or characters that identify the + * end of a variable. This suffix is expressed in terms of a matcher + * allowing advanced suffix matches. + * + * @return the suffix matcher in use + */ + public StrMatcher getVariableSuffixMatcher() { + return suffixMatcher; + } + + /** + * Sets the variable suffix matcher currently in use. + *

        + * The variable suffix is the characer or characters that identify the + * end of a variable. This suffix is expressed in terms of a matcher + * allowing advanced suffix matches. + * + * @param suffixMatcher the suffix matcher to use, null ignored + * @return this, to enable chaining + * @throws IllegalArgumentException if the suffix matcher is null + */ + public StrSubstitutor setVariableSuffixMatcher(StrMatcher suffixMatcher) { + if (suffixMatcher == null) { + throw new IllegalArgumentException("Variable suffix matcher must not be null!"); + } + this.suffixMatcher = suffixMatcher; + return this; + } + + /** + * Sets the variable suffix to use. + *

        + * The variable suffix is the characer or characters that identify the + * end of a variable. This method allows a single character suffix to + * be easily set. + * + * @param suffix the suffix character to use + * @return this, to enable chaining + */ + public StrSubstitutor setVariableSuffix(char suffix) { + return setVariableSuffixMatcher(StrMatcher.charMatcher(suffix)); + } + + /** + * Sets the variable suffix to use. + *

        + * The variable suffix is the character or characters that identify the + * end of a variable. This method allows a string suffix to be easily set. + * + * @param suffix the suffix for variables, not null + * @return this, to enable chaining + * @throws IllegalArgumentException if the suffix is null + */ + public StrSubstitutor setVariableSuffix(String suffix) { + if (suffix == null) { + throw new IllegalArgumentException("Variable suffix must not be null!"); + } + return setVariableSuffixMatcher(StrMatcher.stringMatcher(suffix)); + } + + // Resolver + //----------------------------------------------------------------------- + /** + * Gets the VariableResolver that is used to lookup variables. + * + * @return the VariableResolver + */ + public StrLookup getVariableResolver() { + return this.variableResolver; + } + + /** + * Sets the VariableResolver that is used to lookup variables. + * + * @param variableResolver the VariableResolver + */ + public void setVariableResolver(StrLookup variableResolver) { + this.variableResolver = variableResolver; + } + + // Substitution support in variable names + //----------------------------------------------------------------------- + /** + * Returns a flag whether substitution is done in variable names. + * + * @return the substitution in variable names flag + * @since 2.6 + */ + public boolean isEnableSubstitutionInVariables() { + return enableSubstitutionInVariables; + } + + /** + * Sets a flag whether substitution is done in variable names. If set to + * true, the names of variables can contain other variables which are + * processed first before the original variable is evaluated, e.g. + * ${jre-${java.version}}. The default value is false. + * + * @param enableSubstitutionInVariables the new value of the flag + * @since 2.6 + */ + public void setEnableSubstitutionInVariables( + boolean enableSubstitutionInVariables) { + this.enableSubstitutionInVariables = enableSubstitutionInVariables; + } +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrTokenizer.java =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrTokenizer.java (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/StrTokenizer.java (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,1127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang.text; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * Tokenizes a string based based on delimiters (separators) + * and supporting quoting and ignored character concepts. + *

        + * This class can split a String into many smaller strings. It aims + * to do a similar job to {@link java.util.StringTokenizer StringTokenizer}, + * however it offers much more control and flexibility including implementing + * the ListIterator interface. By default, it is set up + * like StringTokenizer. + *

        + * The input String is split into a number of tokens. + * Each token is separated from the next String by a delimiter. + * One or more delimiter characters must be specified. + *

        + * Each token may be surrounded by quotes. + * The quote matcher specifies the quote character(s). + * A quote may be escaped within a quoted section by duplicating itself. + *

        + * Between each token and the delimiter are potentially characters that need trimming. + * The trimmer matcher specifies these characters. + * One usage might be to trim whitespace characters. + *

        + * At any point outside the quotes there might potentially be invalid characters. + * The ignored matcher specifies these characters to be removed. + * One usage might be to remove new line characters. + *

        + * Empty tokens may be removed or returned as null. + *

        + * "a,b,c"         - Three tokens "a","b","c"   (comma delimiter)
        + * " a, b , c "    - Three tokens "a","b","c"   (default CSV processing trims whitespace)
        + * "a, ", b ,", c" - Three tokens "a, " , " b ", ", c" (quoted text untouched)
        + * 
        + *

        + * + * This tokenizer has the following properties and options: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
        PropertyTypeDefault
        delimCharSetMatcher{ \t\n\r\f}
        quoteNoneMatcher{}
        ignoreNoneMatcher{}
        emptyTokenAsNullbooleanfalse
        ignoreEmptyTokensbooleantrue
        + * + * @author Apache Software Foundation + * @author Matthew Inger + * @author Gary D. Gregory + * @since 2.2 + * @version $Id$ + */ +public class StrTokenizer implements ListIterator, Cloneable { + + private static final StrTokenizer CSV_TOKENIZER_PROTOTYPE; + private static final StrTokenizer TSV_TOKENIZER_PROTOTYPE; + static { + CSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); + CSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.commaMatcher()); + CSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + CSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); + CSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); + CSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); + CSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); + + TSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); + TSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.tabMatcher()); + TSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + TSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); + TSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); + TSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); + TSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); + } + + /** The text to work on. */ + private char chars[]; + /** The parsed tokens */ + private String tokens[]; + /** The current iteration position */ + private int tokenPos; + + /** The delimiter matcher */ + private StrMatcher delimMatcher = StrMatcher.splitMatcher(); + /** The quote matcher */ + private StrMatcher quoteMatcher = StrMatcher.noneMatcher(); + /** The ignored matcher */ + private StrMatcher ignoredMatcher = StrMatcher.noneMatcher(); + /** The trimmer matcher */ + private StrMatcher trimmerMatcher = StrMatcher.noneMatcher(); + + /** Whether to return empty tokens as null */ + private boolean emptyAsNull = false; + /** Whether to ignore empty tokens */ + private boolean ignoreEmptyTokens = true; + + //----------------------------------------------------------------------- + + /** + * Returns a clone of CSV_TOKENIZER_PROTOTYPE. + * + * @return a clone of CSV_TOKENIZER_PROTOTYPE. + */ + private static StrTokenizer getCSVClone() { + return (StrTokenizer) CSV_TOKENIZER_PROTOTYPE.clone(); + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + *

        + * You must call a "reset" method to set the string which you want to parse. + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance() { + return getCSVClone(); + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + * + * @param input the text to parse + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance(String input) { + StrTokenizer tok = getCSVClone(); + tok.reset(input); + return tok; + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + * + * @param input the text to parse + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance(char[] input) { + StrTokenizer tok = getCSVClone(); + tok.reset(input); + return tok; + } + + /** + * Returns a clone of TSV_TOKENIZER_PROTOTYPE. + * + * @return a clone of TSV_TOKENIZER_PROTOTYPE. + */ + private static StrTokenizer getTSVClone() { + return (StrTokenizer) TSV_TOKENIZER_PROTOTYPE.clone(); + } + + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + *

        + * You must call a "reset" method to set the string which you want to parse. + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance() { + return getTSVClone(); + } + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + * @param input the string to parse + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance(String input) { + StrTokenizer tok = getTSVClone(); + tok.reset(input); + return tok; + } + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + * @param input the string to parse + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance(char[] input) { + StrTokenizer tok = getTSVClone(); + tok.reset(input); + return tok; + } + + //----------------------------------------------------------------------- + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer, but with no text to tokenize. + *

        + * This constructor is normally used with {@link #reset(String)}. + */ + public StrTokenizer() { + super(); + this.chars = null; + } + + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer. + * + * @param input the string which is to be parsed + */ + public StrTokenizer(String input) { + super(); + if (input != null) { + chars = input.toCharArray(); + } else { + chars = null; + } + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character. + * + * @param input the string which is to be parsed + * @param delim the field delimiter character + */ + public StrTokenizer(String input, char delim) { + this(input); + setDelimiterChar(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter string. + * + * @param input the string which is to be parsed + * @param delim the field delimiter string + */ + public StrTokenizer(String input, String delim) { + this(input); + setDelimiterString(delim); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher. + * + * @param input the string which is to be parsed + * @param delim the field delimiter matcher + */ + public StrTokenizer(String input, StrMatcher delim) { + this(input); + setDelimiterMatcher(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character + * and handling quotes using the specified quote character. + * + * @param input the string which is to be parsed + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(String input, char delim, char quote) { + this(input, delim); + setQuoteChar(quote); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher + * and handling quotes using the specified quote matcher. + * + * @param input the string which is to be parsed + * @param delim the field delimiter matcher + * @param quote the field quoted string matcher + */ + public StrTokenizer(String input, StrMatcher delim, StrMatcher quote) { + this(input, delim); + setQuoteMatcher(quote); + } + + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + */ + public StrTokenizer(char[] input) { + super(); + this.chars = input; + } + + /** + * Constructs a tokenizer splitting on the specified character. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + */ + public StrTokenizer(char[] input, char delim) { + this(input); + setDelimiterChar(delim); + } + + /** + * Constructs a tokenizer splitting on the specified string. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter string + */ + public StrTokenizer(char[] input, String delim) { + this(input); + setDelimiterString(delim); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter matcher + */ + public StrTokenizer(char[] input, StrMatcher delim) { + this(input); + setDelimiterMatcher(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character + * and handling quotes using the specified quote character. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(char[] input, char delim, char quote) { + this(input, delim); + setQuoteChar(quote); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher + * and handling quotes using the specified quote matcher. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(char[] input, StrMatcher delim, StrMatcher quote) { + this(input, delim); + setQuoteMatcher(quote); + } + + // API + //----------------------------------------------------------------------- + /** + * Gets the number of tokens found in the String. + * + * @return the number of matched tokens + */ + public int size() { + checkTokenized(); + return tokens.length; + } + + /** + * Gets the next token from the String. + * Equivalent to {@link #next()} except it returns null rather than + * throwing {@link NoSuchElementException} when no tokens remain. + * + * @return the next sequential token, or null when no more tokens are found + */ + public String nextToken() { + if (hasNext()) { + return tokens[tokenPos++]; + } + return null; + } + + /** + * Gets the previous token from the String. + * + * @return the previous sequential token, or null when no more tokens are found + */ + public String previousToken() { + if (hasPrevious()) { + return tokens[--tokenPos]; + } + return null; + } + + /** + * Gets a copy of the full token list as an independent modifiable array. + * + * @return the tokens as a String array + */ + public String[] getTokenArray() { + checkTokenized(); + return (String[]) tokens.clone(); + } + + /** + * Gets a copy of the full token list as an independent modifiable list. + * + * @return the tokens as a String array + */ + public List getTokenList() { + checkTokenized(); + List list = new ArrayList(tokens.length); + for (int i = 0; i < tokens.length; i++) { + list.add(tokens[i]); + } + return list; + } + + /** + * Resets this tokenizer, forgetting all parsing and iteration already completed. + *

        + * This method allows the same tokenizer to be reused for the same String. + * + * @return this, to enable chaining + */ + public StrTokenizer reset() { + tokenPos = 0; + tokens = null; + return this; + } + + /** + * Reset this tokenizer, giving it a new input string to parse. + * In this manner you can re-use a tokenizer with the same settings + * on multiple input lines. + * + * @param input the new string to tokenize, null sets no text to parse + * @return this, to enable chaining + */ + public StrTokenizer reset(String input) { + reset(); + if (input != null) { + this.chars = input.toCharArray(); + } else { + this.chars = null; + } + return this; + } + + /** + * Reset this tokenizer, giving it a new input string to parse. + * In this manner you can re-use a tokenizer with the same settings + * on multiple input lines. + *

        + * The input character array is not cloned, and must not be altered after + * passing in to this method. + * + * @param input the new character array to tokenize, not cloned, null sets no text to parse + * @return this, to enable chaining + */ + public StrTokenizer reset(char[] input) { + reset(); + this.chars = input; + return this; + } + + // ListIterator + //----------------------------------------------------------------------- + /** + * Checks whether there are any more tokens. + * + * @return true if there are more tokens + */ + public boolean hasNext() { + checkTokenized(); + return tokenPos < tokens.length; + } + + /** + * Gets the next token. + * + * @return the next String token + * @throws NoSuchElementException if there are no more elements + */ + public Object next() { + if (hasNext()) { + return tokens[tokenPos++]; + } + throw new NoSuchElementException(); + } + + /** + * Gets the index of the next token to return. + * + * @return the next token index + */ + public int nextIndex() { + return tokenPos; + } + + /** + * Checks whether there are any previous tokens that can be iterated to. + * + * @return true if there are previous tokens + */ + public boolean hasPrevious() { + checkTokenized(); + return tokenPos > 0; + } + + /** + * Gets the token previous to the last returned token. + * + * @return the previous token + */ + public Object previous() { + if (hasPrevious()) { + return tokens[--tokenPos]; + } + throw new NoSuchElementException(); + } + + /** + * Gets the index of the previous token. + * + * @return the previous token index + */ + public int previousIndex() { + return tokenPos - 1; + } + + /** + * Unsupported ListIterator operation. + * + * @throws UnsupportedOperationException always + */ + public void remove() { + throw new UnsupportedOperationException("remove() is unsupported"); + } + + /** + * Unsupported ListIterator operation. + * @param obj this parameter ignored. + * @throws UnsupportedOperationException always + */ + public void set(Object obj) { + throw new UnsupportedOperationException("set() is unsupported"); + } + + /** + * Unsupported ListIterator operation. + * @param obj this parameter ignored. + * @throws UnsupportedOperationException always + */ + public void add(Object obj) { + throw new UnsupportedOperationException("add() is unsupported"); + } + + // Implementation + //----------------------------------------------------------------------- + /** + * Checks if tokenization has been done, and if not then do it. + */ + private void checkTokenized() { + if (tokens == null) { + if (chars == null) { + // still call tokenize as subclass may do some work + List split = tokenize(null, 0, 0); + tokens = (String[]) split.toArray(new String[split.size()]); + } else { + List split = tokenize(chars, 0, chars.length); + tokens = (String[]) split.toArray(new String[split.size()]); + } + } + } + + /** + * Internal method to performs the tokenization. + *

        + * Most users of this class do not need to call this method. This method + * will be called automatically by other (public) methods when required. + *

        + * This method exists to allow subclasses to add code before or after the + * tokenization. For example, a subclass could alter the character array, + * offset or count to be parsed, or call the tokenizer multiple times on + * multiple strings. It is also be possible to filter the results. + *

        + * StrTokenizer will always pass a zero offset and a count + * equal to the length of the array to this method, however a subclass + * may pass other values, or even an entirely different array. + * + * @param chars the character array being tokenized, may be null + * @param offset the start position within the character array, must be valid + * @param count the number of characters to tokenize, must be valid + * @return the modifiable list of String tokens, unmodifiable if null array or zero count + */ + protected List tokenize(char[] chars, int offset, int count) { + if (chars == null || count == 0) { + return Collections.EMPTY_LIST; + } + StrBuilder buf = new StrBuilder(); + List tokens = new ArrayList(); + int pos = offset; + + // loop around the entire buffer + while (pos >= 0 && pos < count) { + // find next token + pos = readNextToken(chars, pos, count, buf, tokens); + + // handle case where end of string is a delimiter + if (pos >= count) { + addToken(tokens, ""); + } + } + return tokens; + } + + /** + * Adds a token to a list, paying attention to the parameters we've set. + * + * @param list the list to add to + * @param tok the token to add + */ + private void addToken(List list, String tok) { + if (tok == null || tok.length() == 0) { + if (isIgnoreEmptyTokens()) { + return; + } + if (isEmptyTokenAsNull()) { + tok = null; + } + } + list.add(tok); + } + + /** + * Reads character by character through the String to get the next token. + * + * @param chars the character array being tokenized + * @param start the first character of field + * @param len the length of the character array being tokenized + * @param workArea a temporary work area + * @param tokens the list of parsed tokens + * @return the starting position of the next field (the character + * immediately after the delimiter), or -1 if end of string found + */ + private int readNextToken(char[] chars, int start, int len, StrBuilder workArea, List tokens) { + // skip all leading whitespace, unless it is the + // field delimiter or the quote character + while (start < len) { + int removeLen = Math.max( + getIgnoredMatcher().isMatch(chars, start, start, len), + getTrimmerMatcher().isMatch(chars, start, start, len)); + if (removeLen == 0 || + getDelimiterMatcher().isMatch(chars, start, start, len) > 0 || + getQuoteMatcher().isMatch(chars, start, start, len) > 0) { + break; + } + start += removeLen; + } + + // handle reaching end + if (start >= len) { + addToken(tokens, ""); + return -1; + } + + // handle empty token + int delimLen = getDelimiterMatcher().isMatch(chars, start, start, len); + if (delimLen > 0) { + addToken(tokens, ""); + return start + delimLen; + } + + // handle found token + int quoteLen = getQuoteMatcher().isMatch(chars, start, start, len); + if (quoteLen > 0) { + return readWithQuotes(chars, start + quoteLen, len, workArea, tokens, start, quoteLen); + } + return readWithQuotes(chars, start, len, workArea, tokens, 0, 0); + } + + /** + * Reads a possibly quoted string token. + * + * @param chars the character array being tokenized + * @param start the first character of field + * @param len the length of the character array being tokenized + * @param workArea a temporary work area + * @param tokens the list of parsed tokens + * @param quoteStart the start position of the matched quote, 0 if no quoting + * @param quoteLen the length of the matched quote, 0 if no quoting + * @return the starting position of the next field (the character + * immediately after the delimiter, or if end of string found, + * then the length of string + */ + private int readWithQuotes(char[] chars, int start, int len, StrBuilder workArea, + List tokens, int quoteStart, int quoteLen) + { + // Loop until we've found the end of the quoted + // string or the end of the input + workArea.clear(); + int pos = start; + boolean quoting = (quoteLen > 0); + int trimStart = 0; + + while (pos < len) { + // quoting mode can occur several times throughout a string + // we must switch between quoting and non-quoting until we + // encounter a non-quoted delimiter, or end of string + if (quoting) { + // In quoting mode + + // If we've found a quote character, see if it's + // followed by a second quote. If so, then we need + // to actually put the quote character into the token + // rather than end the token. + if (isQuote(chars, pos, len, quoteStart, quoteLen)) { + if (isQuote(chars, pos + quoteLen, len, quoteStart, quoteLen)) { + // matched pair of quotes, thus an escaped quote + workArea.append(chars, pos, quoteLen); + pos += (quoteLen * 2); + trimStart = workArea.size(); + continue; + } + + // end of quoting + quoting = false; + pos += quoteLen; + continue; + } + + // copy regular character from inside quotes + workArea.append(chars[pos++]); + trimStart = workArea.size(); + + } else { + // Not in quoting mode + + // check for delimiter, and thus end of token + int delimLen = getDelimiterMatcher().isMatch(chars, pos, start, len); + if (delimLen > 0) { + // return condition when end of token found + addToken(tokens, workArea.substring(0, trimStart)); + return pos + delimLen; + } + + // check for quote, and thus back into quoting mode + if (quoteLen > 0) { + if (isQuote(chars, pos, len, quoteStart, quoteLen)) { + quoting = true; + pos += quoteLen; + continue; + } + } + + // check for ignored (outside quotes), and ignore + int ignoredLen = getIgnoredMatcher().isMatch(chars, pos, start, len); + if (ignoredLen > 0) { + pos += ignoredLen; + continue; + } + + // check for trimmed character + // don't yet know if its at the end, so copy to workArea + // use trimStart to keep track of trim at the end + int trimmedLen = getTrimmerMatcher().isMatch(chars, pos, start, len); + if (trimmedLen > 0) { + workArea.append(chars, pos, trimmedLen); + pos += trimmedLen; + continue; + } + + // copy regular character from outside quotes + workArea.append(chars[pos++]); + trimStart = workArea.size(); + } + } + + // return condition when end of string found + addToken(tokens, workArea.substring(0, trimStart)); + return -1; + } + + /** + * Checks if the characters at the index specified match the quote + * already matched in readNextToken(). + * + * @param chars the character array being tokenized + * @param pos the position to check for a quote + * @param len the length of the character array being tokenized + * @param quoteStart the start position of the matched quote, 0 if no quoting + * @param quoteLen the length of the matched quote, 0 if no quoting + * @return true if a quote is matched + */ + private boolean isQuote(char[] chars, int pos, int len, int quoteStart, int quoteLen) { + for (int i = 0; i < quoteLen; i++) { + if ((pos + i) >= len || chars[pos + i] != chars[quoteStart + i]) { + return false; + } + } + return true; + } + + // Delimiter + //----------------------------------------------------------------------- + /** + * Gets the field delimiter matcher. + * + * @return the delimiter matcher in use + */ + public StrMatcher getDelimiterMatcher() { + return this.delimMatcher; + } + + /** + * Sets the field delimiter matcher. + *

        + * The delimitier is used to separate one token from another. + * + * @param delim the delimiter matcher to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterMatcher(StrMatcher delim) { + if (delim == null) { + this.delimMatcher = StrMatcher.noneMatcher(); + } else { + this.delimMatcher = delim; + } + return this; + } + + /** + * Sets the field delimiter character. + * + * @param delim the delimiter character to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterChar(char delim) { + return setDelimiterMatcher(StrMatcher.charMatcher(delim)); + } + + /** + * Sets the field delimiter string. + * + * @param delim the delimiter string to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterString(String delim) { + return setDelimiterMatcher(StrMatcher.stringMatcher(delim)); + } + + // Quote + //----------------------------------------------------------------------- + /** + * Gets the quote matcher currently in use. + *

        + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * The default value is '"' (double quote). + * + * @return the quote matcher in use + */ + public StrMatcher getQuoteMatcher() { + return quoteMatcher; + } + + /** + * Set the quote matcher to use. + *

        + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * + * @param quote the quote matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setQuoteMatcher(StrMatcher quote) { + if (quote != null) { + this.quoteMatcher = quote; + } + return this; + } + + /** + * Sets the quote character to use. + *

        + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * + * @param quote the quote character to use + * @return this, to enable chaining + */ + public StrTokenizer setQuoteChar(char quote) { + return setQuoteMatcher(StrMatcher.charMatcher(quote)); + } + + // Ignored + //----------------------------------------------------------------------- + /** + * Gets the ignored character matcher. + *

        + * These characters are ignored when parsing the String, unless they are + * within a quoted region. + * The default value is not to ignore anything. + * + * @return the ignored matcher in use + */ + public StrMatcher getIgnoredMatcher() { + return ignoredMatcher; + } + + /** + * Set the matcher for characters to ignore. + *

        + * These characters are ignored when parsing the String, unless they are + * within a quoted region. + * + * @param ignored the ignored matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setIgnoredMatcher(StrMatcher ignored) { + if (ignored != null) { + this.ignoredMatcher = ignored; + } + return this; + } + + /** + * Set the character to ignore. + *

        + * This character is ignored when parsing the String, unless it is + * within a quoted region. + * + * @param ignored the ignored character to use + * @return this, to enable chaining + */ + public StrTokenizer setIgnoredChar(char ignored) { + return setIgnoredMatcher(StrMatcher.charMatcher(ignored)); + } + + // Trimmer + //----------------------------------------------------------------------- + /** + * Gets the trimmer character matcher. + *

        + * These characters are trimmed off on each side of the delimiter + * until the token or quote is found. + * The default value is not to trim anything. + * + * @return the trimmer matcher in use + */ + public StrMatcher getTrimmerMatcher() { + return trimmerMatcher; + } + + /** + * Sets the matcher for characters to trim. + *

        + * These characters are trimmed off on each side of the delimiter + * until the token or quote is found. + * + * @param trimmer the trimmer matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setTrimmerMatcher(StrMatcher trimmer) { + if (trimmer != null) { + this.trimmerMatcher = trimmer; + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets whether the tokenizer currently returns empty tokens as null. + * The default for this property is false. + * + * @return true if empty tokens are returned as null + */ + public boolean isEmptyTokenAsNull() { + return this.emptyAsNull; + } + + /** + * Sets whether the tokenizer should return empty tokens as null. + * The default for this property is false. + * + * @param emptyAsNull whether empty tokens are returned as null + * @return this, to enable chaining + */ + public StrTokenizer setEmptyTokenAsNull(boolean emptyAsNull) { + this.emptyAsNull = emptyAsNull; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets whether the tokenizer currently ignores empty tokens. + * The default for this property is true. + * + * @return true if empty tokens are not returned + */ + public boolean isIgnoreEmptyTokens() { + return ignoreEmptyTokens; + } + + /** + * Sets whether the tokenizer should ignore and not return empty tokens. + * The default for this property is true. + * + * @param ignoreEmptyTokens whether empty tokens are not returned + * @return this, to enable chaining + */ + public StrTokenizer setIgnoreEmptyTokens(boolean ignoreEmptyTokens) { + this.ignoreEmptyTokens = ignoreEmptyTokens; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the String content that the tokenizer is parsing. + * + * @return the string content being parsed + */ + public String getContent() { + if (chars == null) { + return null; + } + return new String(chars); + } + + //----------------------------------------------------------------------- + /** + * Creates a new instance of this Tokenizer. The new instance is reset so + * that it will be at the start of the token list. + * If a {@link CloneNotSupportedException} is caught, return null. + * + * @return a new instance of this Tokenizer which has been reset. + */ + public Object clone() { + try { + return cloneReset(); + } catch (CloneNotSupportedException ex) { + return null; + } + } + + /** + * Creates a new instance of this Tokenizer. The new instance is reset so that + * it will be at the start of the token list. + * + * @return a new instance of this Tokenizer which has been reset. + * @throws CloneNotSupportedException if there is a problem cloning + */ + Object cloneReset() throws CloneNotSupportedException { + // this method exists to enable 100% test coverage + StrTokenizer cloned = (StrTokenizer) super.clone(); + if (cloned.chars != null) { + cloned.chars = (char[]) cloned.chars.clone(); + } + cloned.reset(); + return cloned; + } + + //----------------------------------------------------------------------- + /** + * Gets the String content that the tokenizer is parsing. + * + * @return the string content being parsed + */ + public String toString() { + if (tokens == null) { + return "StrTokenizer[not tokenized yet]"; + } + return "StrTokenizer" + getTokenList(); + } + +} Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/text/package.html =================================================================== diff -u --- 3rdParty_sources/commons-lang/org/apache/commons/lang/text/package.html (revision 0) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/text/package.html (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -0,0 +1,26 @@ + + + +

        +Provides classes for handling and manipulating text, partly as an extension to {@link java.text}. +The classes in this package are, for the most part, intended to be instantiated. +(ie. they are not utility classes with lots of static methods) +

        +@since 2.1 + + Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateFormatUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateFormatUtils.java (.../DateFormatUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateFormatUtils.java (.../DateFormatUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,80 +1,44 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.time; +import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; /** - *

        Date and time formatting utilites and constants.

        + *

        Date and time formatting utilities and constants.

        * *

        Formatting is performed using the * {@link org.apache.commons.lang.time.FastDateFormat} class.

        * + * @author Apache Software Foundation * @author Apache Ant - DateUtils * @author Stephane Bailliez * @author Stefan Bodewig - * @author Stephen Colebourne * @author Gary Gregory * @since 2.0 * @version $Id$ */ public class DateFormatUtils { /** - * ISO8601 formatter for date-time witout time zone. + * ISO8601 formatter for date-time without time zone. * The format used is yyyy-MM-dd'T'HH:mm:ss. */ public static final FastDateFormat ISO_DATETIME_FORMAT @@ -150,10 +114,11 @@ * to operate.

        */ public DateFormatUtils() { + super(); } /** - *

        Format a date/time into a specific pattern using the UTC time zone.

        + *

        Formats a date/time into a specific pattern using the UTC time zone.

        * * @param millis the date to format expressed in milliseconds * @param pattern the pattern to use to format the date @@ -164,7 +129,7 @@ } /** - *

        Format a date/time into a specific pattern using the UTC time zone.

        + *

        Formats a date/time into a specific pattern using the UTC time zone.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -175,7 +140,7 @@ } /** - *

        Format a date/time into a specific pattern using the UTC time zone.

        + *

        Formats a date/time into a specific pattern using the UTC time zone.

        * * @param millis the date to format expressed in milliseconds * @param pattern the pattern to use to format the date @@ -187,7 +152,7 @@ } /** - *

        Format a date/time into a specific pattern using the UTC time zone.

        + *

        Formats a date/time into a specific pattern using the UTC time zone.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -199,7 +164,7 @@ } /** - *

        Format a date/time into a specific pattern.

        + *

        Formats a date/time into a specific pattern.

        * * @param millis the date to format expressed in milliseconds * @param pattern the pattern to use to format the date @@ -210,7 +175,7 @@ } /** - *

        Format a date/time into a specific pattern.

        + *

        Formats a date/time into a specific pattern.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -219,9 +184,22 @@ public static String format(Date date, String pattern) { return format(date, pattern, null, null); } + + /** + *

        Formats a calendar into a specific pattern.

        + * + * @param calendar the calendar to format + * @param pattern the pattern to use to format the calendar + * @return the formatted calendar + * @see FastDateFormat#format(Calendar) + * @since 2.4 + */ + public static String format(Calendar calendar, String pattern) { + return format(calendar, pattern, null, null); + } /** - *

        Format a date/time into a specific pattern in a time zone.

        + *

        Formats a date/time into a specific pattern in a time zone.

        * * @param millis the time expressed in milliseconds * @param pattern the pattern to use to format the date @@ -233,7 +211,7 @@ } /** - *

        Format a date/time into a specific pattern in a time zone.

        + *

        Formats a date/time into a specific pattern in a time zone.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -245,8 +223,22 @@ } /** - *

        Format a date/time into a specific pattern in a locale.

        + *

        Formats a calendar into a specific pattern in a time zone.

        * + * @param calendar the calendar to format + * @param pattern the pattern to use to format the calendar + * @param timeZone the time zone to use, may be null + * @return the formatted calendar + * @see FastDateFormat#format(Calendar) + * @since 2.4 + */ + public static String format(Calendar calendar, String pattern, TimeZone timeZone) { + return format(calendar, pattern, timeZone, null); + } + + /** + *

        Formats a date/time into a specific pattern in a locale.

        + * * @param millis the date to format expressed in milliseconds * @param pattern the pattern to use to format the date * @param locale the locale to use, may be null @@ -257,7 +249,7 @@ } /** - *

        Format a date/time into a specific pattern in a locale.

        + *

        Formats a date/time into a specific pattern in a locale.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -269,8 +261,22 @@ } /** - *

        Format a date/time into a specific pattern in a time zone and locale.

        + *

        Formats a calendar into a specific pattern in a locale.

        * + * @param calendar the calendar to format + * @param pattern the pattern to use to format the calendar + * @param locale the locale to use, may be null + * @return the formatted calendar + * @see FastDateFormat#format(Calendar) + * @since 2.4 + */ + public static String format(Calendar calendar, String pattern, Locale locale) { + return format(calendar, pattern, null, locale); + } + + /** + *

        Formats a date/time into a specific pattern in a time zone and locale.

        + * * @param millis the date to format expressed in milliseconds * @param pattern the pattern to use to format the date * @param timeZone the time zone to use, may be null @@ -282,7 +288,7 @@ } /** - *

        Format a date/time into a specific pattern in a time zone and locale.

        + *

        Formats a date/time into a specific pattern in a time zone and locale.

        * * @param date the date to format * @param pattern the pattern to use to format the date @@ -295,4 +301,20 @@ return df.format(date); } + /** + *

        Formats a calendar into a specific pattern in a time zone and locale.

        + * + * @param calendar the calendar to format + * @param pattern the pattern to use to format the calendar + * @param timeZone the time zone to use, may be null + * @param locale the locale to use, may be null + * @return the formatted calendar + * @see FastDateFormat#format(Calendar) + * @since 2.4 + */ + public static String format(Calendar calendar, String pattern, TimeZone timeZone, Locale locale) { + FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone, locale); + return df.format(calendar); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateUtils.java (.../DateUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DateUtils.java (.../DateUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,73 +1,55 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.time; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import java.util.GregorianCalendar; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.TimeZone; +import org.apache.commons.lang.StringUtils; + /** *

        A suite of utilities surrounding the use of the * {@link java.util.Calendar} and {@link java.util.Date} object.

        + * + *

        DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

        + * + * * + * @author Apache Software Foundation * @author Serge Knystautas - * @author Stephen Colebourne * @author Janek Bogucki * @author Gary Gregory + * @author Phil Steitz + * @author Robert Scholte * @since 2.0 * @version $Id$ */ @@ -79,20 +61,24 @@ public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("GMT"); /** * Number of milliseconds in a standard second. + * @since 2.1 */ - public static final int MILLIS_IN_SECOND = 1000; + public static final long MILLIS_PER_SECOND = 1000; /** * Number of milliseconds in a standard minute. + * @since 2.1 */ - public static final int MILLIS_IN_MINUTE = 60 * 1000; + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; /** * Number of milliseconds in a standard hour. + * @since 2.1 */ - public static final int MILLIS_IN_HOUR = 60 * 60 * 1000; + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; /** * Number of milliseconds in a standard day. + * @since 2.1 */ - public static final int MILLIS_IN_DAY = 24 * 60 * 60 * 1000; + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; /** * This is half a month, so this represents whether a date is in the top @@ -105,7 +91,9 @@ {Calendar.SECOND}, {Calendar.MINUTE}, {Calendar.HOUR_OF_DAY, Calendar.HOUR}, - {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, {Calendar.MONTH, DateUtils.SEMI_MONTH}, {Calendar.YEAR}, {Calendar.ERA}}; @@ -141,6 +129,21 @@ public final static int RANGE_MONTH_MONDAY = 6; /** + * Constant marker for truncating + */ + private final static int MODIFY_TRUNCATE = 0; + + /** + * Constant marker for rounding + */ + private final static int MODIFY_ROUND = 1; + + /** + * Constant marker for ceiling + */ + private final static int MODIFY_CEILING= 2; + + /** *

        DateUtils instances should NOT be constructed in * standard programming. Instead, the class should be used as * DateUtils.parse(str);.

        @@ -149,10 +152,531 @@ * instance to operate.

        */ public DateUtils() { + super(); } //----------------------------------------------------------------------- /** + *

        Checks if two date objects are on the same day ignoring time.

        + * + *

        28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

        + * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(Date date1, Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

        Checks if two calendar objects are on the same day ignoring time.

        + * + *

        28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

        + * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(Calendar cal1, Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)); + } + + //----------------------------------------------------------------------- + /** + *

        Checks if two date objects represent the same instant in time.

        + * + *

        This method compares the long millisecond time of the two objects.

        + * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(Date date1, Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

        Checks if two calendar objects represent the same instant in time.

        + * + *

        This method compares the long millisecond time of the two objects.

        + * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(Calendar cal1, Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

        Checks if two calendar objects represent the same local time.

        + * + *

        This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

        + * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(Calendar cal1, Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass()); + } + + //----------------------------------------------------------------------- + /** + *

        Parses a string representing a date by trying a variety of different parsers.

        + * + *

        The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

        + * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(String str, String[] parsePatterns) throws ParseException { + return parseDateWithLeniency(str, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

        Parses a string representing a date by trying a variety of different parsers.

        + * + *

        The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

        + * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(String str, String[] parsePatterns) throws ParseException { + return parseDateWithLeniency(str, parsePatterns, false); + } + + /** + *

        Parses a string representing a date by trying a variety of different parsers.

        + * + *

        The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

        + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calender#isLenient() + */ + private static Date parseDateWithLeniency(String str, String[] parsePatterns, + boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + SimpleDateFormat parser = new SimpleDateFormat(); + parser.setLenient(lenient); + ParsePosition pos = new ParsePosition(0); + for (int i = 0; i < parsePatterns.length; i++) { + + String pattern = parsePatterns[i]; + + // LANG-530 - need to make sure 'ZZ' output doesn't get passed to SimpleDateFormat + if (parsePatterns[i].endsWith("ZZ")) { + pattern = pattern.substring(0, pattern.length() - 1); + } + + parser.applyPattern(pattern); + pos.setIndex(0); + + String str2 = str; + // LANG-530 - need to make sure 'ZZ' output doesn't hit SimpleDateFormat as it will ParseException + if (parsePatterns[i].endsWith("ZZ")) { + int signIdx = indexOfSignChars(str2, 0); + while (signIdx >=0) { + str2 = reformatTimezone(str2, signIdx); + signIdx = indexOfSignChars(str2, ++signIdx); + } + } + + Date date = parser.parse(str2, pos); + if (date != null && pos.getIndex() == str2.length()) { + return date; + } + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + /** + * Index of sign charaters (i.e. '+' or '-'). + * + * @param str The string to search + * @param startPos The start position + * @return the index of the first sign character or -1 if not found + */ + private static int indexOfSignChars(String str, int startPos) { + int idx = StringUtils.indexOf(str, '+', startPos); + if (idx < 0) { + idx = StringUtils.indexOf(str, '-', startPos); + } + return idx; + } + + /** + * Reformat the timezone in a date string. + * + * @param str The input string + * @param signIdx The index position of the sign characters + * @return The reformatted string + */ + private static String reformatTimezone(String str, int signIdx) { + String str2 = str; + if (signIdx >= 0 && + signIdx + 5 < str.length() && + Character.isDigit(str.charAt(signIdx + 1)) && + Character.isDigit(str.charAt(signIdx + 2)) && + str.charAt(signIdx + 3) == ':' && + Character.isDigit(str.charAt(signIdx + 4)) && + Character.isDigit(str.charAt(signIdx + 5))) { + str2 = str.substring(0, signIdx + 3) + str.substring(signIdx + 4); + } + return str2; + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(Date date, int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(Date date, int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(Date date, int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(Date date, int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(Date date, int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(Date date, int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(Date date, int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(Date date, int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new date object with the amount added + * @throws IllegalArgumentException if the date is null + * @deprecated Will become privately scoped in 3.0 + */ + public static Date add(Date date, int calendarField, int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(Date date, int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(Date date, int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(Date date, int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(Date date, int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(Date date, int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(Date date, int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the miliseconds field to a date returning a new object. + * The original date object is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(Date date, int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original date object is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to set the amount to + * @param amount the amount to set + * @return a new Date object set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(Date date, int calendarField, int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Convert a Date into a Calendar object. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 2.6 + */ + public static Calendar toCalendar(Date date) { + Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** *

        Round this date, leaving the field specified as the most * significant field.

        * @@ -161,19 +685,32 @@ * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it * would return 1 April 2002 0:00:00.000.

        * + *

        For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

          + *
        • March 30, 2003 01:10 rounds to March 30, 2003 01:00
        • + *
        • March 30, 2003 01:40 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:10 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:40 rounds to March 30, 2003 04:00
        • + *
        + *

        + * * @param date the date to work with * @param field the field from Calendar * or SEMI_MONTH * @return the rounded date * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million */ public static Date round(Date date, int field) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } - GregorianCalendar gval = new GregorianCalendar(); + Calendar gval = Calendar.getInstance(); gval.setTime(date); - modify(gval, field, true); + modify(gval, field, MODIFY_ROUND); return gval.getTime(); } @@ -186,18 +723,31 @@ * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it * would return 1 April 2002 0:00:00.000.

        * + *

        For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

          + *
        • March 30, 2003 01:10 rounds to March 30, 2003 01:00
        • + *
        • March 30, 2003 01:40 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:10 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:40 rounds to March 30, 2003 04:00
        • + *
        + *

        + * * @param date the date to work with * @param field the field from Calendar * or SEMI_MONTH * @return the rounded date (a different object) * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million */ public static Calendar round(Calendar date, int field) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } Calendar rounded = (Calendar) date.clone(); - modify(rounded, field, true); + modify(rounded, field, MODIFY_ROUND); return rounded; } @@ -210,13 +760,26 @@ * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it * would return 1 April 2002 0:00:00.000.

        * + *

        For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

          + *
        • March 30, 2003 01:10 rounds to March 30, 2003 01:00
        • + *
        • March 30, 2003 01:40 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:10 rounds to March 30, 2003 03:00
        • + *
        • March 30, 2003 02:40 rounds to March 30, 2003 04:00
        • + *
        + *

        + * * @param date the date to work with, either Date or Calendar * @param field the field from Calendar * or SEMI_MONTH * @return the rounded date * @throws IllegalArgumentException if the date is null * @throws ClassCastException if the object type is not a Date * or Calendar + * @throws ArithmeticException if the year is over 280 million */ public static Date round(Object date, int field) { if (date == null) { @@ -246,14 +809,15 @@ * or SEMI_MONTH * @return the rounded date * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million */ public static Date truncate(Date date, int field) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } - GregorianCalendar gval = new GregorianCalendar(); + Calendar gval = Calendar.getInstance(); gval.setTime(date); - modify(gval, field, false); + modify(gval, field, MODIFY_TRUNCATE); return gval.getTime(); } @@ -271,13 +835,14 @@ * or SEMI_MONTH * @return the rounded date (a different object) * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million */ public static Calendar truncate(Calendar date, int field) { if (date == null) { throw new IllegalArgumentException("The date must not be null"); } Calendar truncated = (Calendar) date.clone(); - modify(truncated, field, false); + modify(truncated, field, MODIFY_TRUNCATE); return truncated; } @@ -299,6 +864,7 @@ * is null * @throws ClassCastException if the object type is not a * Date or Calendar + * @throws ArithmeticException if the year is over 280 million */ public static Date truncate(Object date, int field) { if (date == null) { @@ -312,22 +878,160 @@ throw new ClassCastException("Could not truncate " + date); } } + + //----------------------------------------------------------------------- + /** + *

        Ceil this date, leaving the field specified as the most + * significant field.

        + * + *

        For example, if you had the datetime of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

        + * + * @param date the date to work with + * @param field the field from Calendar + * or SEMI_MONTH + * @return the rounded date + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(Date date, int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, MODIFY_CEILING); + return gval.getTime(); + } + /** + *

        Ceil this date, leaving the field specified as the most + * significant field.

        + * + *

        For example, if you had the datetime of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

        + * + * @param date the date to work with + * @param field the field from Calendar + * or SEMI_MONTH + * @return the rounded date (a different object) + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(Calendar date, int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, MODIFY_CEILING); + return ceiled; + } + + /** + *

        Ceil this date, leaving the field specified as the most + * significant field.

        + * + *

        For example, if you had the datetime of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

        + * + * @param date the date to work with, either Date + * or Calendar + * @param field the field from Calendar + * or SEMI_MONTH + * @return the rounded date + * @throws IllegalArgumentException if the date + * is null + * @throws ClassCastException if the object type is not a + * Date or Calendar + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(Object date, int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + //----------------------------------------------------------------------- /** *

        Internal calculation method.

        * * @param val the calendar * @param field the field constant - * @param round true to round, false to truncate + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million */ - private static void modify(Calendar val, int field, boolean round) { + private static void modify(Calendar val, int field, int modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + int millisecs = val.get(Calendar.MILLISECOND); + if (MODIFY_TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + int seconds = val.get(Calendar.SECOND); + if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + int minutes = val.get(Calendar.MINUTE); + if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + boolean roundUp = false; for (int i = 0; i < fields.length; i++) { for (int j = 0; j < fields[i].length; j++) { if (fields[i][j] == field) { //This is our field... we stop looping - if (round && roundUp) { + if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) { if (field == DateUtils.SEMI_MONTH) { //This is a special case that's hard to generalize //If the date is 1, we round up to 16, otherwise @@ -338,6 +1042,18 @@ val.add(Calendar.DATE, -15); val.add(Calendar.MONTH, 1); } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- } else { //We need at add one to this field since the // last number causes us to round up @@ -369,14 +1085,14 @@ } break; case Calendar.AM_PM: - if (fields[i][0] == Calendar.HOUR) { + if (fields[i][0] == Calendar.HOUR_OF_DAY) { //If we're going to drop the HOUR field's value, // we want to do this our own way. - offset = val.get(Calendar.HOUR); + offset = val.get(Calendar.HOUR_OF_DAY); if (offset >= 12) { offset -= 12; } - roundUp = offset > 6; + roundUp = offset >= 6; offsetSet = true; } break; @@ -390,170 +1106,60 @@ roundUp = offset > ((max - min) / 2); } //We need to remove this field - val.add(fields[i][0], -offset); + if (offset != 0) { + val.set(fields[i][0], val.get(fields[i][0]) - offset); + } } throw new IllegalArgumentException("The field " + field + " is not supported"); } - // TODO: Decide whether this code is removed or goes into 2.1 //----------------------------------------------------------------------- - /* - *

        Parses a date string formatted in CVS format.

        - * - * @param dateStr the date to parse - * @return the parsed date - * @throws IllegalArgumentException if the date cannot be parsed - public static Calendar parseCVS(String dateStr) { - if (dateStr == null) { - throw new IllegalArgumentException("The date must not be null"); - } - //Get the symbol names - DateFormatSymbols symbols = new DateFormatSymbols(Locale.ENGLISH); - - //Prep the string to parse - String value = dateStr.toLowerCase().trim(); - - //Get the current date/time - Calendar now = Calendar.getInstance(); - if (value.endsWith(" ago")) { - //If this was a date that was "ago" the current time... - //Strip out the ' ago' part - value = value.substring(0, value.length() - 4); - - //Split the value and unit - int start = value.indexOf(" "); - if (start < 0) { - throw new IllegalArgumentException("Could not find space in between value and unit"); - } - String unit = value.substring(start + 1); - value = value.substring(0, start); - //We support "a week", so we need to parse the value as "a" - int val = 0; - if (value.equals("a") || value.equals("an")) { - val = 1; - } else { - val = Integer.parseInt(value); - } - - //Determine the unit - if (unit.equals("milliseconds") || unit.equals("millisecond")) { - now.add(Calendar.MILLISECOND, -val); - } else if (unit.equals("seconds") || unit.equals("second")) { - now.add(Calendar.SECOND, -val); - } else if (unit.equals("minutes") || unit.equals("minute")) { - now.add(Calendar.MINUTE, -val); - } else if (unit.equals("hours") || unit.equals("hour")) { - now.add(Calendar.HOUR, -val); - } else if (unit.equals("days") || unit.equals("day")) { - now.add(Calendar.DATE, -val); - } else if (unit.equals("weeks") || unit.equals("week")) { - now.add(Calendar.DATE, -val * 7); - } else if (unit.equals("fortnights") || unit.equals("fortnight")) { - now.add(Calendar.DATE, -val * 14); - } else if (unit.equals("months") || unit.equals("month")) { - now.add(Calendar.MONTH, -val); - } else if (unit.equals("years") || unit.equals("year")) { - now.add(Calendar.YEAR, -val); - } else { - throw new IllegalArgumentException("We do not understand that many units ago"); - } - return now; - } else if (value.startsWith("last ")) { - //If this was the last time a certain field was met - //Strip out the 'last ' part - value = value.substring(5); - //Get the current date/time - String[] strings = symbols.getWeekdays(); - for (int i = 0; i < strings.length; i++) { - if (value.equalsIgnoreCase(strings[i])) { - //How many days after Sunday - int daysAgo = now.get(Calendar.DAY_OF_WEEK) - i; - if (daysAgo <= 0) { - daysAgo += 7; - } - now.add(Calendar.DATE, -daysAgo); - return now; - } - } - strings = symbols.getMonths(); - for (int i = 0; i < strings.length; i++) { - if (value.equalsIgnoreCase(strings[i])) { - //How many days after January - int monthsAgo = now.get(Calendar.MONTH) - i; - if (monthsAgo <= 0) { - monthsAgo += 12; - } - now.add(Calendar.MONTH, -monthsAgo); - return now; - } - } - if (value.equals("week")) { - now.add(Calendar.DATE, -7); - return now; - } - throw new IllegalArgumentException("We do not understand that last units"); - } else if (value.equals("yesterday")) { - now.add(Calendar.DATE, -1); - return now; - } else if (value.equals("tomorrow")) { - now.add(Calendar.DATE, 1); - return now; - } - //Try to parse the date a number of different ways - for (int i = 0; i < dateFormats.length; i++) { - try { - Date datetime = dateFormats[i].parse(dateStr); - Calendar cal = Calendar.getInstance(); - cal.setTime(datetime); - return cal; - } catch (ParseException pe) { - //we ignore this and just keep trying - } - } - - throw new IllegalArgumentException("Unable to parse '" + dateStr + "'."); - } - */ - - //----------------------------------------------------------------------- /** - *

        This constructs an Iterator that will - * start and stop over a date range based on the focused - * date and the range style.

        + *

        This constructs an Iterator over each day in a date + * range defined by a focus date and range style.

        * *

        For instance, passing Thursday, July 4, 2002 and a - * RANGE_MONTH_SUNDAY will return an - * Iterator that starts with Sunday, June 30, - * 2002 and ends with Saturday, August 3, 2002. - * - * @param focus the date to work with - * @param rangeStyle the style constant to use. Must be one of the range - * styles listed for the {@link #iterator(Calendar, int)} method. + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

        * - * @return the date iterator - * @throws IllegalArgumentException if the date is null or if - * the rangeStyle is not + *

        This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

        + * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, which always returns Calendar instances + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid */ public static Iterator iterator(Date focus, int rangeStyle) { if (focus == null) { throw new IllegalArgumentException("The date must not be null"); } - GregorianCalendar gval = new GregorianCalendar(); + Calendar gval = Calendar.getInstance(); gval.setTime(focus); return iterator(gval, rangeStyle); } /** - *

        This constructs an Iterator that will - * start and stop over a date range based on the focused - * date and the range style.

        + *

        This constructs an Iterator over each day in a date + * range defined by a focus date and range style.

        * *

        For instance, passing Thursday, July 4, 2002 and a - * RANGE_MONTH_SUNDAY will return an - * Iterator that starts with Sunday, June 30, - * 2002 and ends with Saturday, August 3, 2002. - * + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

        + * + *

        This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

        + * * @param focus the date to work with * @param rangeStyle the style constant to use. Must be one of * {@link DateUtils#RANGE_MONTH_SUNDAY}, @@ -564,6 +1170,7 @@ * {@link DateUtils#RANGE_WEEK_CENTER} * @return the date iterator * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid */ public static Iterator iterator(Calendar focus, int rangeStyle) { if (focus == null) { @@ -638,15 +1245,14 @@ } /** - *

        This constructs an Iterator that will - * start and stop over a date range based on the focused - * date and the range style.

        + *

        This constructs an Iterator over each day in a date + * range defined by a focus date and range style.

        * *

        For instance, passing Thursday, July 4, 2002 and a - * RANGE_MONTH_SUNDAY will return an - * Iterator that starts with Sunday, June 30, - * 2002 and ends with Saturday, August 3, 2002.

        - * + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

        + * * @param focus the date to work with, either * Date or Calendar * @param rangeStyle the style constant to use. Must be one of the range @@ -669,25 +1275,620 @@ throw new ClassCastException("Could not iterate based on " + focus); } } + + /** + *

        Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
        • + *
        + *

        + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(Date date, int fragment) { + return getFragment(date, fragment, Calendar.MILLISECOND); + } + + /** + *

        Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
        • + *
        + *

        + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(Date date, int fragment) { + return getFragment(date, fragment, Calendar.SECOND); + } + + /** + *

        Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
        • + *
        • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
        • + *
        + *

        + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(Date date, int fragment) { + return getFragment(date, fragment, Calendar.MINUTE); + } + + /** + *

        Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
        • + *
        • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
        • + *
        + *

        + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(Date date, int fragment) { + return getFragment(date, fragment, Calendar.HOUR_OF_DAY); + } + + /** + *

        Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

        + * + *

        + *

          + *
        • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
        • + *
        • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
        • + *
        • January 28, 2008 with Calendar.YEAR as fragment will return 28
        • + *
        • February 28, 2008 with Calendar.YEAR as fragment will return 59
        • + *
        • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
        • + *
        + *

        + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(Date date, int fragment) { + return getFragment(date, fragment, Calendar.DAY_OF_YEAR); + } /** + *

        Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
        • + *
        + *

        + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(Calendar calendar, int fragment) { + return getFragment(calendar, fragment, Calendar.MILLISECOND); + } + /** + *

        Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
        • + *
        + *

        + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(Calendar calendar, int fragment) { + return getFragment(calendar, fragment, Calendar.SECOND); + } + + /** + *

        Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
        • + *
        • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
        • + *
        + *

        + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(Calendar calendar, int fragment) { + return getFragment(calendar, fragment, Calendar.MINUTE); + } + + /** + *

        Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

        + * + *

        + *

          + *
        • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
        • + *
        • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
        • + *
        • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
        • + *
        • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
        • + *
        + *

        + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(Calendar calendar, int fragment) { + return getFragment(calendar, fragment, Calendar.HOUR_OF_DAY); + } + + /** + *

        Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

        + * + *

        Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

        + * + *

        Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

        + * + *

        + *

          + *
        • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
        • + *
        • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
        • + *
        • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
        • + *
        • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
        • + *
        • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
        • + *
        + *

        + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(Calendar calendar, int fragment) { + return getFragment(calendar, fragment, Calendar.DAY_OF_YEAR); + } + + /** + * Date-version for fragment-calculation in any unit + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit Calendar field defining the unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(Date date, int fragment, int unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Calendar-version for fragment-calculation in any unit + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit Calendar field defining the unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(Calendar calendar, int fragment, int unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + long millisPerUnit = getMillisPerUnit(unit); + long result = 0; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += (calendar.get(Calendar.DAY_OF_YEAR) * MILLIS_PER_DAY) / millisPerUnit; + break; + case Calendar.MONTH: + result += (calendar.get(Calendar.DAY_OF_MONTH) * MILLIS_PER_DAY) / millisPerUnit; + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += (calendar.get(Calendar.HOUR_OF_DAY) * MILLIS_PER_HOUR) / millisPerUnit; + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += (calendar.get(Calendar.MINUTE) * MILLIS_PER_MINUTE) / millisPerUnit; + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += (calendar.get(Calendar.SECOND) * MILLIS_PER_SECOND) / millisPerUnit; + //$FALL-THROUGH$ + case Calendar.SECOND: + result += (calendar.get(Calendar.MILLISECOND) * 1) / millisPerUnit; + break; + case Calendar.MILLISECOND: + break;//never useful + default: + throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from Calendar + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 2.6 + */ + public static boolean truncatedEquals(Calendar cal1, Calendar cal2, int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 2.6 + */ + public static boolean truncatedEquals(Date date1, Date date2, int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 2.6 + */ + public static int truncatedCompareTo(Calendar cal1, Calendar cal2, int field) { + Calendar truncatedCal1 = truncate(cal1, field); + Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.getTime().compareTo(truncatedCal2.getTime()); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 2.6 + */ + public static int truncatedCompareTo(Date date1, Date date2, int field) { + Date truncatedDate1 = truncate(date1, field); + Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + /** + * Returns the number of millis of a datefield, if this is a constant value + * + * @param unit A Calendar field which is a valid unit for a fragment + * @return number of millis + * @throws IllegalArgumentException if date can't be represented in millisenconds + * @since 2.4 + */ + private static long getMillisPerUnit(int unit) { + long result = Long.MAX_VALUE; + switch (unit) { + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result = MILLIS_PER_DAY; + break; + case Calendar.HOUR_OF_DAY: + result = MILLIS_PER_HOUR; + break; + case Calendar.MINUTE: + result = MILLIS_PER_MINUTE; + break; + case Calendar.SECOND: + result = MILLIS_PER_SECOND; + break; + case Calendar.MILLISECOND: + result = 1; + break; + default: throw new IllegalArgumentException("The unit " + unit + " cannot be represented is milleseconds"); + } + return result; + } + + /** *

        Date iterator.

        */ static class DateIterator implements Iterator { private final Calendar endFinal; private final Calendar spot; + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (not inclusive) + */ DateIterator(Calendar startFinal, Calendar endFinal) { super(); this.endFinal = endFinal; spot = startFinal; spot.add(Calendar.DATE, -1); } + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ public boolean hasNext() { return spot.before(endFinal); } + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ public Object next() { if (spot.equals(endFinal)) { throw new NoSuchElementException(); @@ -696,9 +1897,44 @@ return spot.clone(); } + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ public void remove() { throw new UnsupportedOperationException(); } } - + + //------------------------------------------------------------------------- + // Deprecated int constants + // TODO: Remove in 3.0 + + /** + * Number of milliseconds in a standard second. + * + * @deprecated Use MILLIS_PER_SECOND. This will be removed in Commons Lang 3.0. + */ + public static final int MILLIS_IN_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * + * @deprecated Use MILLIS_PER_MINUTE. This will be removed in Commons Lang 3.0. + */ + public static final int MILLIS_IN_MINUTE = 60 * 1000; + /** + * Number of milliseconds in a standard hour. + * + * @deprecated Use MILLIS_PER_HOUR. This will be removed in Commons Lang 3.0. + */ + public static final int MILLIS_IN_HOUR = 60 * 60 * 1000; + /** + * Number of milliseconds in a standard day. + * + * @deprecated Use MILLIS_PER_DAY. This will be removed in Commons Lang 3.0. + */ + public static final int MILLIS_IN_DAY = 24 * 60 * 60 * 1000; + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DurationFormatUtils.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DurationFormatUtils.java (.../DurationFormatUtils.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/DurationFormatUtils.java (.../DurationFormatUtils.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,202 +1,666 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.time; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.text.StrBuilder; + /** - *

        Duration formatting utilites and constants.

        + *

        Duration formatting utilities and constants. The following table describes the tokens + * used in the pattern language for formatting.

        + * + * + * + * + * + * + * + * + * + *
        characterduration element
        yyears
        Mmonths
        ddays
        Hhours
        mminutes
        sseconds
        Smilliseconds
        * + * @author Apache Software Foundation * @author Apache Ant - DateUtils * @author Stephane Bailliez * @author Stefan Bodewig - * @author Stephen Colebourne * @author Gary Gregory - * @since 2.0 + * @since 2.1 * @version $Id$ */ -class DurationFormatUtils { - // TODO: Make class public once methods can fully select which fields to output +public class DurationFormatUtils { /** - *

        Pattern used with FastDateFormat and SimpleDateFormat for the ISO8601 - * date time extended format used in durations.

        + *

        DurationFormatUtils instances should NOT be constructed in standard programming.

        + * + *

        This constructor is public to permit tools that require a JavaBean instance + * to operate.

        + */ + public DurationFormatUtils() { + super(); + } + + /** + *

        Pattern used with FastDateFormat and SimpleDateFormat + * for the ISO8601 period format used in durations.

        * * @see org.apache.commons.lang.time.FastDateFormat * @see java.text.SimpleDateFormat */ public static final String ISO_EXTENDED_FORMAT_PATTERN = "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.S'S'"; + //----------------------------------------------------------------------- /** - *

        ISO8601 formatter for the date time extended format used in durations, - * with XML Schema durations particularly in mind.

        + *

        Formats the time gap as a string.

        * - *

        This format represents the Gregorian year, month, day, hour, minute, and second components defined - * in � 5.5.3.2 of ISO 8601, respectively. These components are ordered in their significance by their order - * of appearance i.e. as year, month, day, hour, minute, and second.

        + *

        The format used is ISO8601-like: + * H:m:s.S.

        * - *

        The ISO8601 extended format PnYnMnDTnHnMnS, where nY - * represents the number of years, nM the number of months, nD the number of days, - * 'T' is the date/time separator, nH the number of hours, nM the number of minutes and - * nS the number of seconds. The number of seconds can include decimal digits to arbitrary precision.

        + * @param durationMillis the duration to format + * @return the time as a String + */ + public static String formatDurationHMS(long durationMillis) { + return formatDuration(durationMillis, "H:mm:ss.SSS"); + } + + /** + *

        Formats the time gap as a string.

        * - * @see #ISO_EXTENDED_FORMAT_PATTERN - * @see http://www.w3.org/TR/xmlschema-2/#duration + *

        The format used is the ISO8601 period format.

        + * + *

        This method formats durations using the days and lower fields of the + * ISO format pattern, such as P7D6TH5M4.321S.

        + * + * @param durationMillis the duration to format + * @return the time as a String */ - public static final FastDateFormat ISO_EXTENDED_FORMAT = - FastDateFormat.getInstance(ISO_EXTENDED_FORMAT_PATTERN); + public static String formatDurationISO(long durationMillis) { + return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false); + } /** - *

        Get the time gap as a string.

        + *

        Formats the time gap as a string, using the specified format, and padding with zeros and + * using the default timezone.

        * - *

        The format used is ISO8601-like: - * hours:minutes:seconds.milliseconds.

        + *

        This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

        * - * @param millis the duration to format + * @param durationMillis the duration to format + * @param format the way in which to format the duration * @return the time as a String */ - public static String formatISO(long millis) { - int hours, minutes, seconds, milliseconds; - hours = (int) (millis / DateUtils.MILLIS_IN_HOUR); - millis = millis - (hours * DateUtils.MILLIS_IN_HOUR); - minutes = (int) (millis / DateUtils.MILLIS_IN_MINUTE); - millis = millis - (minutes * DateUtils.MILLIS_IN_MINUTE); - seconds = (int) (millis / DateUtils.MILLIS_IN_SECOND); - millis = millis - (seconds * DateUtils.MILLIS_IN_SECOND); - milliseconds = (int) millis; + public static String formatDuration(long durationMillis, String format) { + return formatDuration(durationMillis, format, true); + } - StringBuffer buf = new StringBuffer(32); - buf.append(hours); - buf.append(':'); - buf.append((char) (minutes / 10 + '0')); - buf.append((char) (minutes % 10 + '0')); - buf.append(':'); - buf.append((char) (seconds / 10 + '0')); - buf.append((char) (seconds % 10 + '0')); - buf.append('.'); - if (milliseconds < 10) { - buf.append('0').append('0'); - } else if (milliseconds < 100) { - buf.append('0'); + /** + *

        Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional and + * the timezone may be specified.

        + * + *

        This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

        + * + * @param durationMillis the duration to format + * @param format the way in which to format the duration + * @param padWithZeros whether to pad the left hand side of numbers with 0's + * @return the time as a String + */ + public static String formatDuration(long durationMillis, String format, boolean padWithZeros) { + + Token[] tokens = lexx(format); + + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + int milliseconds = 0; + + if (Token.containsTokenWithValue(tokens, d) ) { + days = (int) (durationMillis / DateUtils.MILLIS_PER_DAY); + durationMillis = durationMillis - (days * DateUtils.MILLIS_PER_DAY); } - buf.append(milliseconds); - return buf.toString(); + if (Token.containsTokenWithValue(tokens, H) ) { + hours = (int) (durationMillis / DateUtils.MILLIS_PER_HOUR); + durationMillis = durationMillis - (hours * DateUtils.MILLIS_PER_HOUR); + } + if (Token.containsTokenWithValue(tokens, m) ) { + minutes = (int) (durationMillis / DateUtils.MILLIS_PER_MINUTE); + durationMillis = durationMillis - (minutes * DateUtils.MILLIS_PER_MINUTE); + } + if (Token.containsTokenWithValue(tokens, s) ) { + seconds = (int) (durationMillis / DateUtils.MILLIS_PER_SECOND); + durationMillis = durationMillis - (seconds * DateUtils.MILLIS_PER_SECOND); + } + if (Token.containsTokenWithValue(tokens, S) ) { + milliseconds = (int) durationMillis; + } + + return format(tokens, 0, 0, days, hours, minutes, seconds, milliseconds, padWithZeros); } /** - *

        Format an elapsed time into a plurialization correct string. - * It is limited only to report elapsed time in minutes and - * seconds and has the following behavior.

        + *

        Formats an elapsed time into a plurialization correct string.

        + * + *

        This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

        + * + * @param durationMillis the elapsed time to report in milliseconds + * @param suppressLeadingZeroElements suppresses leading 0 elements + * @param suppressTrailingZeroElements suppresses trailing 0 elements + * @return the formatted text in days/hours/minutes/seconds + */ + public static String formatDurationWords( + long durationMillis, + boolean suppressLeadingZeroElements, + boolean suppressTrailingZeroElements) { + + // This method is generally replacable by the format method, but + // there are a series of tweaks and special cases that require + // trickery to replicate. + String duration = formatDuration(durationMillis, "d' days 'H' hours 'm' minutes 's' seconds'"); + if (suppressLeadingZeroElements) { + // this is a temporary marker on the front. Like ^ in regexp. + duration = " " + duration; + String tmp = StringUtils.replaceOnce(duration, " 0 days", ""); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 hours", ""); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 minutes", ""); + duration = tmp; + if (tmp.length() != duration.length()) { + duration = StringUtils.replaceOnce(tmp, " 0 seconds", ""); + } + } + } + if (duration.length() != 0) { + // strip the space off again + duration = duration.substring(1); + } + } + if (suppressTrailingZeroElements) { + String tmp = StringUtils.replaceOnce(duration, " 0 seconds", ""); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 minutes", ""); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 hours", ""); + if (tmp.length() != duration.length()) { + duration = StringUtils.replaceOnce(tmp, " 0 days", ""); + } + } + } + } + // handle plurals + duration = " " + duration; + duration = StringUtils.replaceOnce(duration, " 1 seconds", " 1 second"); + duration = StringUtils.replaceOnce(duration, " 1 minutes", " 1 minute"); + duration = StringUtils.replaceOnce(duration, " 1 hours", " 1 hour"); + duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day"); + return duration.trim(); + } + + //----------------------------------------------------------------------- + /** + *

        Formats the time gap as a string.

        + * + *

        The format used is the ISO8601 period format.

        + * + * @param startMillis the start of the duration to format + * @param endMillis the end of the duration to format + * @return the time as a String + */ + public static String formatPeriodISO(long startMillis, long endMillis) { + return formatPeriod(startMillis, endMillis, ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault()); + } + + /** + *

        Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional. + * + * @param startMillis the start of the duration + * @param endMillis the end of the duration + * @param format the way in which to format the duration + * @return the time as a String + */ + public static String formatPeriod(long startMillis, long endMillis, String format) { + return formatPeriod(startMillis, endMillis, format, true, TimeZone.getDefault()); + } + + /** + *

        Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional and + * the timezone may be specified.

        * - *
          - *
        • minutes are not displayed when 0 (ie: - * "45 seconds")
        • . - *
        • seconds are always displayed in plural form (ie - * "0 seconds" or "10 seconds") except - * for 1 (ie "1 second")
        • - *
        + *

        When calculating the difference between months/days, it chooses to + * calculate months first. So when working out the number of months and + * days between January 15th and March 10th, it choose 1 month and + * 23 days gained by choosing January->February = 1 month and then + * calculating days forwards, and not the 1 month and 26 days gained by + * choosing March -> February = 1 month and then calculating days + * backwards.

        + * + *

        For more control, the Joda-Time + * library is recommended.

        * - * @param millis the elapsed time to report in milliseconds - * @return the formatted text in minutes/seconds + * @param startMillis the start of the duration + * @param endMillis the end of the duration + * @param format the way in which to format the duration + * @param padWithZeros whether to pad the left hand side of numbers with 0's + * @param timezone the millis are defined in + * @return the time as a String */ - public static String formatWords( - long millis, - boolean supressLeadingZeroElements, - boolean supressTrailingZeroElements) { - long[] values = new long[4]; - values[0] = millis / DateUtils.MILLIS_IN_DAY; - values[1] = (millis / DateUtils.MILLIS_IN_HOUR) % 24; - values[2] = (millis / DateUtils.MILLIS_IN_MINUTE) % 60; - values[3] = (millis / DateUtils.MILLIS_IN_SECOND) % 60; - String[] fieldsOne = { " day ", " hour ", " minute ", " second" }; - String[] fieldsPlural = { " days ", " hours ", " minutes ", " seconds" }; + public static String formatPeriod(long startMillis, long endMillis, String format, boolean padWithZeros, + TimeZone timezone) { - StringBuffer buf = new StringBuffer(64); - boolean valueOutput = false; + // Used to optimise for differences under 28 days and + // called formatDuration(millis, format); however this did not work + // over leap years. + // TODO: Compare performance to see if anything was lost by + // losing this optimisation. + + Token[] tokens = lexx(format); - for (int i = 0; i < 4; i++) { - long value = values[i]; - if (value == 0) { - // handle zero - if (valueOutput) { - if (supressTrailingZeroElements == false) { - buf.append('0').append(fieldsPlural[i]); + // timezones get funky around 0, so normalizing everything to GMT + // stops the hours being off + Calendar start = Calendar.getInstance(timezone); + start.setTime(new Date(startMillis)); + Calendar end = Calendar.getInstance(timezone); + end.setTime(new Date(endMillis)); + + // initial estimates + int milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND); + int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND); + int minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE); + int hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY); + int days = end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH); + int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH); + int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR); + + // each initial estimate is adjusted in case it is under 0 + while (milliseconds < 0) { + milliseconds += 1000; + seconds -= 1; + } + while (seconds < 0) { + seconds += 60; + minutes -= 1; + } + while (minutes < 0) { + minutes += 60; + hours -= 1; + } + while (hours < 0) { + hours += 24; + days -= 1; + } + + if (Token.containsTokenWithValue(tokens, M)) { + while (days < 0) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + months -= 1; + start.add(Calendar.MONTH, 1); + } + + while (months < 0) { + months += 12; + years -= 1; + } + + if (!Token.containsTokenWithValue(tokens, y) && years != 0) { + while (years != 0) { + months += 12 * years; + years = 0; + } + } + } else { + // there are no M's in the format string + + if( !Token.containsTokenWithValue(tokens, y) ) { + int target = end.get(Calendar.YEAR); + if (months < 0) { + // target is end-year -1 + target -= 1; + } + + while ( (start.get(Calendar.YEAR) != target)) { + days += start.getActualMaximum(Calendar.DAY_OF_YEAR) - start.get(Calendar.DAY_OF_YEAR); + + // Not sure I grok why this is needed, but the brutal tests show it is + if(start instanceof GregorianCalendar) { + if( (start.get(Calendar.MONTH) == Calendar.FEBRUARY) && + (start.get(Calendar.DAY_OF_MONTH) == 29 ) ) + { + days += 1; + } } - } else { - if (supressLeadingZeroElements == false) { - buf.append('0').append(fieldsPlural[i]); - } + + start.add(Calendar.YEAR, 1); + + days += start.get(Calendar.DAY_OF_YEAR); } - } else if (value == 1) { - // one - valueOutput = true; - buf.append('1').append(fieldsOne[i]); - } else { - // other - valueOutput = true; - buf.append(value).append(fieldsPlural[i]); + + years = 0; } + + while( start.get(Calendar.MONTH) != end.get(Calendar.MONTH) ) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + start.add(Calendar.MONTH, 1); + } + + months = 0; + + while (days < 0) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + months -= 1; + start.add(Calendar.MONTH, 1); + } + } - return buf.toString().trim(); + // The rest of this code adds in values that + // aren't requested. This allows the user to ask for the + // number of months and get the real count and not just 0->11. + + if (!Token.containsTokenWithValue(tokens, d)) { + hours += 24 * days; + days = 0; + } + if (!Token.containsTokenWithValue(tokens, H)) { + minutes += 60 * hours; + hours = 0; + } + if (!Token.containsTokenWithValue(tokens, m)) { + seconds += 60 * minutes; + minutes = 0; + } + if (!Token.containsTokenWithValue(tokens, s)) { + milliseconds += 1000 * seconds; + seconds = 0; + } + + return format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros); } + //----------------------------------------------------------------------- /** - *

        DurationFormatUtils instances should NOT be constructed in standard programming.

        + *

        The internal method to do the formatting.

        + * + * @param tokens the tokens + * @param years the number of years + * @param months the number of months + * @param days the number of days + * @param hours the number of hours + * @param minutes the number of minutes + * @param seconds the number of seconds + * @param milliseconds the number of millis + * @param padWithZeros whether to pad + * @return the formatted string + */ + static String format(Token[] tokens, int years, int months, int days, int hours, int minutes, int seconds, + int milliseconds, boolean padWithZeros) { + StrBuilder buffer = new StrBuilder(); + boolean lastOutputSeconds = false; + int sz = tokens.length; + for (int i = 0; i < sz; i++) { + Token token = tokens[i]; + Object value = token.getValue(); + int count = token.getCount(); + if (value instanceof StringBuffer) { + buffer.append(value.toString()); + } else { + if (value == y) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(years), count, '0') : Integer + .toString(years)); + lastOutputSeconds = false; + } else if (value == M) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(months), count, '0') : Integer + .toString(months)); + lastOutputSeconds = false; + } else if (value == d) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(days), count, '0') : Integer + .toString(days)); + lastOutputSeconds = false; + } else if (value == H) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(hours), count, '0') : Integer + .toString(hours)); + lastOutputSeconds = false; + } else if (value == m) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(minutes), count, '0') : Integer + .toString(minutes)); + lastOutputSeconds = false; + } else if (value == s) { + buffer.append(padWithZeros ? StringUtils.leftPad(Integer.toString(seconds), count, '0') : Integer + .toString(seconds)); + lastOutputSeconds = true; + } else if (value == S) { + if (lastOutputSeconds) { + milliseconds += 1000; + String str = padWithZeros + ? StringUtils.leftPad(Integer.toString(milliseconds), count, '0') + : Integer.toString(milliseconds); + buffer.append(str.substring(1)); + } else { + buffer.append(padWithZeros + ? StringUtils.leftPad(Integer.toString(milliseconds), count, '0') + : Integer.toString(milliseconds)); + } + lastOutputSeconds = false; + } + } + } + return buffer.toString(); + } + + static final Object y = "y"; + static final Object M = "M"; + static final Object d = "d"; + static final Object H = "H"; + static final Object m = "m"; + static final Object s = "s"; + static final Object S = "S"; + + /** + * Parses a classic date format string into Tokens * - *

        This constructor is public to permit tools that require a JavaBean instance - * to operate.

        + * @param format to parse + * @return array of Token[] */ - public DurationFormatUtils() { + static Token[] lexx(String format) { + char[] array = format.toCharArray(); + ArrayList list = new ArrayList(array.length); + + boolean inLiteral = false; + StringBuffer buffer = null; + Token previous = null; + int sz = array.length; + for(int i=0; itrue if contained + */ + static boolean containsTokenWithValue(Token[] tokens, Object value) { + int sz = tokens.length; + for (int i = 0; i < sz; i++) { + if (tokens[i].getValue() == value) { + return true; + } + } + return false; + } + + private Object value; + private int count; + + /** + * Wraps a token around a value. A value would be something like a 'Y'. + * + * @param value to wrap + */ + Token(Object value) { + this.value = value; + this.count = 1; + } + + /** + * Wraps a token around a repeated number of a value, for example it would + * store 'yyyy' as a value for y and a count of 4. + * + * @param value to wrap + * @param count to wrap + */ + Token(Object value, int count) { + this.value = value; + this.count = count; + } + + /** + * Adds another one of the value + */ + void increment() { + count++; + } + + /** + * Gets the current number of values represented + * + * @return int number of values represented + */ + int getCount() { + return count; + } + + /** + * Gets the particular value this token represents. + * + * @return Object value + */ + Object getValue() { + return value; + } + + /** + * Supports equality of this Token to another Token. + * + * @param obj2 Object to consider equality of + * @return boolean true if equal + */ + public boolean equals(Object obj2) { + if (obj2 instanceof Token) { + Token tok2 = (Token) obj2; + if (this.value.getClass() != tok2.value.getClass()) { + return false; + } + if (this.count != tok2.count) { + return false; + } + if (this.value instanceof StringBuffer) { + return this.value.toString().equals(tok2.value.toString()); + } else if (this.value instanceof Number) { + return this.value.equals(tok2.value); + } else { + return this.value == tok2.value; + } + } + return false; + } + + /** + * Returns a hashcode for the token equal to the + * hashcode for the token's value. Thus 'TT' and 'TTTT' + * will have the same hashcode. + * + * @return The hashcode for the token + */ + public int hashCode() { + return this.value.hashCode(); + } + + /** + * Represents this token as a String. + * + * @return String representation of the token + */ + public String toString() { + return StringUtils.repeat(this.value.toString(), this.count); + } + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/FastDateFormat.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/FastDateFormat.java (.../FastDateFormat.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/FastDateFormat.java (.../FastDateFormat.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,58 +1,23 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.commons.lang.time; +import java.io.IOException; +import java.io.ObjectInputStream; import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.FieldPosition; @@ -69,6 +34,9 @@ import java.util.Map; import java.util.TimeZone; +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.text.StrBuilder; + /** *

        FastDateFormat is a fast and thread-safe version of * {@link java.text.SimpleDateFormat}.

        @@ -89,14 +57,15 @@ * *

        In addition, the pattern 'ZZ' has been made to represent * ISO8601 full format time zones (eg. +08:00 or -11:00). - * This introduces a minor incompatability with Java 1.4, but at a gain of + * This introduces a minor incompatibility with Java 1.4, but at a gain of * useful functionality.

        * + * @author Apache Software Foundation * @author TeaTrove project * @author Brian S O'Neill * @author Sean Schofield * @author Gary Gregory - * @author Stephen Colebourne + * @author Nikolay Metchev * @since 2.0 * @version $Id$ */ @@ -114,6 +83,13 @@ // So, don't change this code! It works and is very fast. /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** * FULL locale dependent date or time style. */ public static final int FULL = DateFormat.FULL; @@ -130,17 +106,14 @@ */ public static final int SHORT = DateFormat.SHORT; - // package scoped as used by inner class - static final double LOG_10 = Math.log(10); + private static String cDefaultPattern; // lazily initialised by getInstance() - private static String cDefaultPattern; + private static final Map cInstanceCache = new HashMap(7); + private static final Map cDateInstanceCache = new HashMap(7); + private static final Map cTimeInstanceCache = new HashMap(7); + private static final Map cDateTimeInstanceCache = new HashMap(7); + private static final Map cTimeZoneDisplayCache = new HashMap(7); - private static Map cInstanceCache = new HashMap(7); - private static Map cDateInstanceCache = new HashMap(7); - private static Map cTimeInstanceCache = new HashMap(7); - private static Map cDateTimeInstanceCache = new HashMap(7); - private static Map cTimeZoneDisplayCache = new HashMap(7); - /** * The pattern. */ @@ -164,11 +137,11 @@ /** * The parsed rules. */ - private Rule[] mRules; + private transient Rule[] mRules; /** * The estimated maximum length. */ - private int mMaxLengthEstimate; + private transient int mMaxLengthEstimate; //----------------------------------------------------------------------- /** @@ -247,7 +220,52 @@ return format; } + //----------------------------------------------------------------------- /** + *

        Gets a date formatter instance using the specified style in the + * default time zone and locale.

        + * + * @param style date style: FULL, LONG, MEDIUM, or SHORT + * @return a localized standard date formatter + * @throws IllegalArgumentException if the Locale has no date + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateInstance(int style) { + return getDateInstance(style, null, null); + } + + /** + *

        Gets a date formatter instance using the specified style and + * locale in the default time zone.

        + * + * @param style date style: FULL, LONG, MEDIUM, or SHORT + * @param locale optional locale, overrides system locale + * @return a localized standard date formatter + * @throws IllegalArgumentException if the Locale has no date + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateInstance(int style, Locale locale) { + return getDateInstance(style, null, locale); + } + + /** + *

        Gets a date formatter instance using the specified style and + * time zone in the default locale.

        + * + * @param style date style: FULL, LONG, MEDIUM, or SHORT + * @param timeZone optional time zone, overrides time zone of + * formatted date + * @return a localized standard date formatter + * @throws IllegalArgumentException if the Locale has no date + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateInstance(int style, TimeZone timeZone) { + return getDateInstance(style, timeZone, null); + } + /** *

        Gets a date formatter instance using the specified style, time * zone and locale.

        * @@ -264,16 +282,15 @@ if (timeZone != null) { key = new Pair(key, timeZone); } + if (locale == null) { - key = new Pair(key, locale); + locale = Locale.getDefault(); } + key = new Pair(key, locale); + FastDateFormat format = (FastDateFormat) cDateInstanceCache.get(key); if (format == null) { - if (locale == null) { - locale = Locale.getDefault(); - } - try { SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance(style, locale); String pattern = formatter.toPattern(); @@ -287,7 +304,53 @@ return format; } + //----------------------------------------------------------------------- /** + *

        Gets a time formatter instance using the specified style in the + * default time zone and locale.

        + * + * @param style time style: FULL, LONG, MEDIUM, or SHORT + * @return a localized standard time formatter + * @throws IllegalArgumentException if the Locale has no time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getTimeInstance(int style) { + return getTimeInstance(style, null, null); + } + + /** + *

        Gets a time formatter instance using the specified style and + * locale in the default time zone.

        + * + * @param style time style: FULL, LONG, MEDIUM, or SHORT + * @param locale optional locale, overrides system locale + * @return a localized standard time formatter + * @throws IllegalArgumentException if the Locale has no time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getTimeInstance(int style, Locale locale) { + return getTimeInstance(style, null, locale); + } + + /** + *

        Gets a time formatter instance using the specified style and + * time zone in the default locale.

        + * + * @param style time style: FULL, LONG, MEDIUM, or SHORT + * @param timeZone optional time zone, overrides time zone of + * formatted time + * @return a localized standard time formatter + * @throws IllegalArgumentException if the Locale has no time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getTimeInstance(int style, TimeZone timeZone) { + return getTimeInstance(style, timeZone, null); + } + + /** *

        Gets a time formatter instance using the specified style, time * zone and locale.

        * @@ -327,7 +390,58 @@ return format; } + //----------------------------------------------------------------------- /** + *

        Gets a date/time formatter instance using the specified style + * in the default time zone and locale.

        + * + * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT + * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT + * @return a localized standard date/time formatter + * @throws IllegalArgumentException if the Locale has no date/time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateTimeInstance( + int dateStyle, int timeStyle) { + return getDateTimeInstance(dateStyle, timeStyle, null, null); + } + + /** + *

        Gets a date/time formatter instance using the specified style and + * locale in the default time zone.

        + * + * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT + * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT + * @param locale optional locale, overrides system locale + * @return a localized standard date/time formatter + * @throws IllegalArgumentException if the Locale has no date/time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateTimeInstance( + int dateStyle, int timeStyle, Locale locale) { + return getDateTimeInstance(dateStyle, timeStyle, null, locale); + } + + /** + *

        Gets a date/time formatter instance using the specified style and + * time zone in the default locale.

        + * + * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT + * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT + * @param timeZone optional time zone, overrides time zone of + * formatted date + * @return a localized standard date/time formatter + * @throws IllegalArgumentException if the Locale has no date/time + * pattern defined + * @since 2.1 + */ + public static FastDateFormat getDateTimeInstance( + int dateStyle, int timeStyle, TimeZone timeZone) { + return getDateTimeInstance(dateStyle, timeStyle, timeZone, null); + } + /** *

        Gets a date/time formatter instance using the specified style, * time zone and locale.

        * @@ -340,29 +454,27 @@ * @throws IllegalArgumentException if the Locale has no date/time * pattern defined */ - public static synchronized FastDateFormat getDateTimeInstance( - int dateStyle, int timeStyle, TimeZone timeZone, Locale locale) { + public static synchronized FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone, + Locale locale) { Object key = new Pair(new Integer(dateStyle), new Integer(timeStyle)); if (timeZone != null) { key = new Pair(key, timeZone); } - if (locale != null) { - key = new Pair(key, locale); + if (locale == null) { + locale = Locale.getDefault(); } + key = new Pair(key, locale); FastDateFormat format = (FastDateFormat) cDateTimeInstanceCache.get(key); if (format == null) { - if (locale == null) { - locale = Locale.getDefault(); - } - try { - SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale); + SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateTimeInstance(dateStyle, timeStyle, + locale); String pattern = formatter.toPattern(); format = getInstance(pattern, timeZone, locale); cDateTimeInstanceCache.put(key, format); - + } catch (ClassCastException ex) { throw new IllegalArgumentException("No date time pattern for locale: " + locale); } @@ -440,7 +552,7 @@ } /** - *

        Initialise the instance for first use.

        + *

        Initializes the instance for first use.

        */ protected void init() { List rulesList = parsePattern(); @@ -495,7 +607,7 @@ break; case 'y': // year (number) if (tokenLen >= 4) { - rule = UnpaddedNumberField.INSTANCE_YEAR; + rule = selectNumberRule(Calendar.YEAR, tokenLen); } else { rule = TwoDigitYearField.INSTANCE; } @@ -593,7 +705,7 @@ * @return parsed token */ protected String parseToken(String pattern, int[] indexRef) { - StringBuffer buf = new StringBuffer(); + StrBuilder buf = new StrBuilder(); int i = indexRef[0]; int length = pattern.length(); @@ -665,8 +777,8 @@ // Format methods //----------------------------------------------------------------------- /** - *

        Format either a Date or a - * Calendar object.

        + *

        Formats a Date, Calendar or + * Long (milliseconds) object.

        * * @param obj the object to format * @param toAppendTo the buffer to append to @@ -678,20 +790,33 @@ return format((Date) obj, toAppendTo); } else if (obj instanceof Calendar) { return format((Calendar) obj, toAppendTo); + } else if (obj instanceof Long) { + return format(((Long) obj).longValue(), toAppendTo); } else { throw new IllegalArgumentException("Unknown class: " + (obj == null ? "" : obj.getClass().getName())); } } /** + *

        Formats a millisecond long value.

        + * + * @param millis the millisecond value to format + * @return the formatted string + * @since 2.1 + */ + public String format(long millis) { + return format(new Date(millis)); + } + + /** *

        Formats a Date object.

        * * @param date the date to format * @return the formatted string */ public String format(Date date) { - Calendar c = new GregorianCalendar(mTimeZone); + Calendar c = new GregorianCalendar(mTimeZone, mLocale); c.setTime(date); return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString(); } @@ -707,6 +832,19 @@ } /** + *

        Formats a milliseond long value into the + * supplied StringBuffer.

        + * + * @param millis the millisecond value to format + * @param buf the buffer to format into + * @return the specified string buffer + * @since 2.1 + */ + public StringBuffer format(long millis, StringBuffer buf) { + return format(new Date(millis), buf); + } + + /** *

        Formats a Date object into the * supplied StringBuffer.

        * @@ -730,6 +868,7 @@ */ public StringBuffer format(Calendar calendar, StringBuffer buf) { if (mTimeZoneForced) { + calendar.getTime(); /// LANG-538 calendar = (Calendar) calendar.clone(); calendar.setTimeZone(mTimeZone); } @@ -756,7 +895,7 @@ // Parsing //----------------------------------------------------------------------- /** - *

        Parsing not supported.

        + *

        Parsing is not supported.

        * * @param source the string to parse * @param pos the parsing position @@ -814,7 +953,7 @@ } /** - *

        Gets an estimate for the maximum string length that the + *

        Gets an estimate for the maximum string length that the * formatter will produce.

        * *

        The actual formatted length will almost always be less than or @@ -829,7 +968,7 @@ // Basics //----------------------------------------------------------------------- /** - *

        Compare two objects for equality.

        + *

        Compares two objects for equality.

        * * @param obj the object to compare to * @return true if equal @@ -852,9 +991,9 @@ } /** - *

        A suitable hashcode.

        + *

        Returns a hashcode compatible with equals.

        * - * @return a hashcode compatable with equals + * @return a hashcode compatible with equals */ public int hashCode() { int total = 0; @@ -874,21 +1013,54 @@ public String toString() { return "FastDateFormat[" + mPattern + "]"; } + + // Serializing + //----------------------------------------------------------------------- + /** + * Create the object after serialization. This implementation reinitializes the + * transient properties. + * + * @param in ObjectInputStream from which the object is being deserialized. + * @throws IOException if there is an IO issue. + * @throws ClassNotFoundException if a class cannot be found. + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + init(); + } // Rules //----------------------------------------------------------------------- /** *

        Inner class defining a rule.

        */ private interface Rule { + /** + * Returns the estimated lentgh of the result. + * + * @return the estimated length + */ int estimateLength(); + + /** + * Appends the value of the specified calendar to the output buffer based on the rule implementation. + * + * @param buffer the output buffer + * @param calendar calendar to be appended + */ void appendTo(StringBuffer buffer, Calendar calendar); } /** *

        Inner class defining a numeric rule.

        */ private interface NumberRule extends Rule { + /** + * Appends the specified value to the output buffer based on the rule implementation. + * + * @param buffer the output buffer + * @param value the value to be appended + */ void appendTo(StringBuffer buffer, int value); } @@ -898,14 +1070,26 @@ private static class CharacterLiteral implements Rule { private final char mValue; + /** + * Constructs a new instance of CharacterLiteral + * to hold the specified value. + * + * @param value the character literal + */ CharacterLiteral(char value) { mValue = value; } + /** + * {@inheritDoc} + */ public int estimateLength() { return 1; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { buffer.append(mValue); } @@ -917,14 +1101,26 @@ private static class StringLiteral implements Rule { private final String mValue; + /** + * Constructs a new instance of StringLiteral + * to hold the specified value. + * + * @param value the string literal + */ StringLiteral(String value) { mValue = value; } + /** + * {@inheritDoc} + */ public int estimateLength() { return mValue.length(); } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { buffer.append(mValue); } @@ -937,11 +1133,21 @@ private final int mField; private final String[] mValues; + /** + * Constructs an instance of TextField + * with the specified field and values. + * + * @param field the field + * @param values the field values + */ TextField(int field, String[] values) { mField = field; mValues = values; } + /** + * {@inheritDoc} + */ public int estimateLength() { int max = 0; for (int i=mValues.length; --i >= 0; ) { @@ -953,6 +1159,9 @@ return max; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { buffer.append(mValues[calendar.get(mField)]); } @@ -962,22 +1171,34 @@ *

        Inner class to output an unpadded number.

        */ private static class UnpaddedNumberField implements NumberRule { - static final UnpaddedNumberField INSTANCE_YEAR = new UnpaddedNumberField(Calendar.YEAR); - private final int mField; + /** + * Constructs an instance of UnpadedNumberField with the specified field. + * + * @param field the field + */ UnpaddedNumberField(int field) { mField = field; } + /** + * {@inheritDoc} + */ public int estimateLength() { return 4; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(mField)); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { if (value < 10) { buffer.append((char)(value + '0')); @@ -995,18 +1216,32 @@ */ private static class UnpaddedMonthField implements NumberRule { static final UnpaddedMonthField INSTANCE = new UnpaddedMonthField(); - + + /** + * Constructs an instance of UnpaddedMonthField. + * + */ UnpaddedMonthField() { + super(); } + /** + * {@inheritDoc} + */ public int estimateLength() { return 2; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(Calendar.MONTH) + 1); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { if (value < 10) { buffer.append((char)(value + '0')); @@ -1024,6 +1259,12 @@ private final int mField; private final int mSize; + /** + * Constructs an instance of PaddedNumberField. + * + * @param field the field + * @param size size of the output field + */ PaddedNumberField(int field, int size) { if (size < 3) { // Should use UnpaddedNumberField or TwoDigitNumberField. @@ -1033,14 +1274,23 @@ mSize = size; } + /** + * {@inheritDoc} + */ public int estimateLength() { return 4; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(mField)); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { if (value < 100) { for (int i = mSize; --i >= 2; ) { @@ -1053,7 +1303,8 @@ if (value < 1000) { digits = 3; } else { - digits = (int)(Math.log(value) / LOG_10) + 1; + Validate.isTrue(value > -1, "Negative values should not be possible", value); + digits = Integer.toString(value).length(); } for (int i = mSize; --i >= digits; ) { buffer.append('0'); @@ -1069,18 +1320,32 @@ private static class TwoDigitNumberField implements NumberRule { private final int mField; + /** + * Constructs an instance of TwoDigitNumberField with the specified field. + * + * @param field the field + */ TwoDigitNumberField(int field) { mField = field; } + /** + * {@inheritDoc} + */ public int estimateLength() { return 2; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(mField)); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { if (value < 100) { buffer.append((char)(value / 10 + '0')); @@ -1096,18 +1361,31 @@ */ private static class TwoDigitYearField implements NumberRule { static final TwoDigitYearField INSTANCE = new TwoDigitYearField(); - + + /** + * Constructs an instance of TwoDigitYearField. + */ TwoDigitYearField() { + super(); } + /** + * {@inheritDoc} + */ public int estimateLength() { return 2; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(Calendar.YEAR) % 100); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { buffer.append((char)(value / 10 + '0')); buffer.append((char)(value % 10 + '0')); @@ -1119,18 +1397,31 @@ */ private static class TwoDigitMonthField implements NumberRule { static final TwoDigitMonthField INSTANCE = new TwoDigitMonthField(); - + + /** + * Constructs an instance of TwoDigitMonthField. + */ TwoDigitMonthField() { + super(); } + /** + * {@inheritDoc} + */ public int estimateLength() { return 2; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { appendTo(buffer, calendar.get(Calendar.MONTH) + 1); } + /** + * {@inheritDoc} + */ public final void appendTo(StringBuffer buffer, int value) { buffer.append((char)(value / 10 + '0')); buffer.append((char)(value % 10 + '0')); @@ -1143,14 +1434,26 @@ private static class TwelveHourField implements NumberRule { private final NumberRule mRule; + /** + * Constructs an instance of TwelveHourField with the specified + * NumberRule. + * + * @param rule the rule + */ TwelveHourField(NumberRule rule) { mRule = rule; } + /** + * {@inheritDoc} + */ public int estimateLength() { return mRule.estimateLength(); } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { int value = calendar.get(Calendar.HOUR); if (value == 0) { @@ -1159,6 +1462,9 @@ mRule.appendTo(buffer, value); } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, int value) { mRule.appendTo(buffer, value); } @@ -1170,14 +1476,26 @@ private static class TwentyFourHourField implements NumberRule { private final NumberRule mRule; + /** + * Constructs an instance of TwentyFourHourField with the specified + * NumberRule. + * + * @param rule the rule + */ TwentyFourHourField(NumberRule rule) { mRule = rule; } + /** + * {@inheritDoc} + */ public int estimateLength() { return mRule.estimateLength(); } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { int value = calendar.get(Calendar.HOUR_OF_DAY); if (value == 0) { @@ -1186,6 +1504,9 @@ mRule.appendTo(buffer, value); } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, int value) { mRule.appendTo(buffer, value); } @@ -1202,6 +1523,14 @@ private final String mStandard; private final String mDaylight; + /** + * Constructs an instance of TimeZoneNameRule with the specified properties. + * + * @param timeZone the time zone + * @param timeZoneForced if true the time zone is forced into standard and daylight + * @param locale the locale + * @param style the style + */ TimeZoneNameRule(TimeZone timeZone, boolean timeZoneForced, Locale locale, int style) { mTimeZone = timeZone; mTimeZoneForced = timeZoneForced; @@ -1217,6 +1546,9 @@ } } + /** + * {@inheritDoc} + */ public int estimateLength() { if (mTimeZoneForced) { return Math.max(mStandard.length(), mDaylight.length()); @@ -1227,6 +1559,9 @@ } } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { if (mTimeZoneForced) { if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) { @@ -1255,14 +1590,25 @@ final boolean mColon; + /** + * Constructs an instance of TimeZoneNumberRule with the specified properties. + * + * @param colon add colon between HH and MM in the output if true + */ TimeZoneNumberRule(boolean colon) { mColon = colon; } + /** + * {@inheritDoc} + */ public int estimateLength() { return 5; } + /** + * {@inheritDoc} + */ public void appendTo(StringBuffer buffer, Calendar calendar) { int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); @@ -1296,6 +1642,14 @@ private final int mStyle; private final Locale mLocale; + /** + * Constructs an instance of TimeZoneDisplayKey with the specified properties. + * + * @param timeZone the time zone + * @param daylight adjust the style for daylight saving time if true + * @param style the timezone style + * @param locale the timezone locale + */ TimeZoneDisplayKey(TimeZone timeZone, boolean daylight, int style, Locale locale) { mTimeZone = timeZone; @@ -1306,10 +1660,16 @@ mLocale = locale; } + /** + * {@inheritDoc} + */ public int hashCode() { return mStyle * 31 + mLocale.hashCode(); } + /** + * {@inheritDoc} + */ public boolean equals(Object obj) { if (this == obj) { return true; @@ -1336,11 +1696,19 @@ private final Object mObj1; private final Object mObj2; + /** + * Constructs an instance of Pair to hold the specified objects. + * @param obj1 one object in the pair + * @param obj2 second object in the pair + */ public Pair(Object obj1, Object obj2) { mObj1 = obj1; mObj2 = obj2; } + /** + * {@inheritDoc} + */ public boolean equals(Object obj) { if (this == obj) { return true; @@ -1359,12 +1727,18 @@ key.mObj2 == null : mObj2.equals(key.mObj2)); } + /** + * {@inheritDoc} + */ public int hashCode() { return (mObj1 == null ? 0 : mObj1.hashCode()) + (mObj2 == null ? 0 : mObj2.hashCode()); } + /** + * {@inheritDoc} + */ public String toString() { return "[" + mObj1 + ':' + mObj2 + ']'; } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/StopWatch.java =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/StopWatch.java (.../StopWatch.java) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/StopWatch.java (.../StopWatch.java) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,201 +1,342 @@ -/* ==================================================================== - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2002-2003 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowledgement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgement may appear in the software itself, - * if and wherever such third-party acknowledgements normally appear. - * - * 4. The names "The Jakarta Project", "Commons", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.apache.commons.lang.time; /** - *

        StopWatch provides a convenient API for timings.

        + *

        + * StopWatch provides a convenient API for timings. + *

        * - *

        The methods do not protect against inappropriate calls. Thus you - * can call stop before start, resume before suspend or unsplit before split. - * The results are indeterminate in these cases.

        - * - *

        To start the watch, call {@link #start()}. At this point you can:

        + *

        + * To start the watch, call {@link #start()}. At this point you can: + *

        *
          - *
        • {@link #split()} the watch to get the time whilst the watch continues in the - * background. {@link #unsplit()} will remove the effect of the split. At this point, - * these three options are available again.
        • - *
        • {@link #suspend()} the watch to pause it. {@link #resume()} allows the watch - * to continue. Any time between the suspend and resume will not be counted in - * the total. At this point, these three options are available again.
        • - *
        • {@link #stop()} the watch to complete the timing session.
        • + *
        • {@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will + * remove the effect of the split. At this point, these three options are available again.
        • + *
        • {@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the + * suspend and resume will not be counted in the total. At this point, these three options are available again.
        • + *
        • {@link #stop()} the watch to complete the timing session.
        • *
        - * - *

        It is intended that the output methods {@link #toString()} and {@link #getTime()} - * should only be called after stop, split or suspend, however a suitable result will - * be returned at other points.

        - * - * @author Henri Yandell - * @author Stephen Colebourne + * + *

        + * It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop, + * split or suspend, however a suitable result will be returned at other points. + *

        + * + *

        + * NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start, + * resume before suspend or unsplit before split. + *

        + * + *

        + * 1. split(), suspend(), or stop() cannot be invoked twice
        + * 2. unsplit() may only be called if the watch has been split()
        + * 3. resume() may only be called if the watch has been suspend()
        + * 4. start() cannot be called twice without calling reset() + *

        + * + *

        This class is not thread-safe

        + * + * @author Apache Software Foundation * @since 2.0 * @version $Id$ */ public class StopWatch { - + + // running states + private static final int STATE_UNSTARTED = 0; + + private static final int STATE_RUNNING = 1; + + private static final int STATE_STOPPED = 2; + + private static final int STATE_SUSPENDED = 3; + + // split state + private static final int STATE_UNSPLIT = 10; + + private static final int STATE_SPLIT = 11; + /** + * The current running state of the StopWatch. + */ + private int runningState = STATE_UNSTARTED; + + /** + * Whether the stopwatch has a split time recorded. + */ + private int splitState = STATE_UNSPLIT; + + /** * The start time. */ private long startTime = -1; + /** * The stop time. */ private long stopTime = -1; /** - *

        Constructor.

        + *

        + * Constructor. + *

        */ public StopWatch() { + super(); } /** - *

        Start the stopwatch.

        + *

        + * Start the stopwatch. + *

        * - *

        This method starts a new timing session, clearing any previous values.

        + *

        + * This method starts a new timing session, clearing any previous values. + *

        + * + * @throws IllegalStateException + * if the StopWatch is already running. */ public void start() { - stopTime = -1; - startTime = System.currentTimeMillis(); + if (this.runningState == STATE_STOPPED) { + throw new IllegalStateException("Stopwatch must be reset before being restarted. "); + } + if (this.runningState != STATE_UNSTARTED) { + throw new IllegalStateException("Stopwatch already started. "); + } + this.stopTime = -1; + this.startTime = System.currentTimeMillis(); + this.runningState = STATE_RUNNING; } /** - *

        Stop the stopwatch.

        + *

        + * Stop the stopwatch. + *

        * - *

        This method ends a new timing session, allowing the time to be retrieved.

        + *

        + * This method ends a new timing session, allowing the time to be retrieved. + *

        + * + * @throws IllegalStateException + * if the StopWatch is not running. */ public void stop() { - stopTime = System.currentTimeMillis(); + if (this.runningState != STATE_RUNNING && this.runningState != STATE_SUSPENDED) { + throw new IllegalStateException("Stopwatch is not running. "); + } + if (this.runningState == STATE_RUNNING) { + this.stopTime = System.currentTimeMillis(); + } + this.runningState = STATE_STOPPED; } /** - *

        Reset the stopwatch.

        + *

        + * Resets the stopwatch. Stops it if need be. + *

        * - *

        This method clears the internal values to allow the object to be reused.

        + *

        + * This method clears the internal values to allow the object to be reused. + *

        */ public void reset() { - startTime = -1; - stopTime = -1; + this.runningState = STATE_UNSTARTED; + this.splitState = STATE_UNSPLIT; + this.startTime = -1; + this.stopTime = -1; } /** - *

        Split the time.

        + *

        + * Split the time. + *

        * - *

        This method sets the stop time of the watch to allow a time to be extracted. - * The start time is unaffected, enabling {@link #unsplit()} to contine the - * timing from the original start point.

        + *

        + * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected, + * enabling {@link #unsplit()} to continue the timing from the original start point. + *

        + * + * @throws IllegalStateException + * if the StopWatch is not running. */ public void split() { - stopTime = System.currentTimeMillis(); + if (this.runningState != STATE_RUNNING) { + throw new IllegalStateException("Stopwatch is not running. "); + } + this.stopTime = System.currentTimeMillis(); + this.splitState = STATE_SPLIT; } /** - *

        Remove a split.

        + *

        + * Remove a split. + *

        * - *

        This method clears the stop time. The start time is unaffected, enabling - * timing from the original start point to continue.

        + *

        + * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to + * continue. + *

        + * + * @throws IllegalStateException + * if the StopWatch has not been split. */ public void unsplit() { - stopTime = -1; + if (this.splitState != STATE_SPLIT) { + throw new IllegalStateException("Stopwatch has not been split. "); + } + this.stopTime = -1; + this.splitState = STATE_UNSPLIT; } /** - *

        Suspend the stopwatch for later resumption.

        + *

        + * Suspend the stopwatch for later resumption. + *

        * - *

        This method suspends the watch until it is resumed. The watch will not include - * time between the suspend and resume calls in the total time.

        + *

        + * This method suspends the watch until it is resumed. The watch will not include time between the suspend and + * resume calls in the total time. + *

        + * + * @throws IllegalStateException + * if the StopWatch is not currently running. */ public void suspend() { - stopTime = System.currentTimeMillis(); + if (this.runningState != STATE_RUNNING) { + throw new IllegalStateException("Stopwatch must be running to suspend. "); + } + this.stopTime = System.currentTimeMillis(); + this.runningState = STATE_SUSPENDED; } /** - *

        Resume the stopwatch after a suspend.

        + *

        + * Resume the stopwatch after a suspend. + *

        * - *

        This method resumes the watch after it was suspended. The watch will not include - * time between the suspend and resume calls in the total time.

        + *

        + * This method resumes the watch after it was suspended. The watch will not include time between the suspend and + * resume calls in the total time. + *

        + * + * @throws IllegalStateException + * if the StopWatch has not been suspended. */ public void resume() { - startTime += (System.currentTimeMillis() - stopTime); - stopTime = -1; + if (this.runningState != STATE_SUSPENDED) { + throw new IllegalStateException("Stopwatch must be suspended to resume. "); + } + this.startTime += (System.currentTimeMillis() - this.stopTime); + this.stopTime = -1; + this.runningState = STATE_RUNNING; } /** - *

        Get the time on the stopwatch.

        + *

        + * Get the time on the stopwatch. + *

        * - *

        This is either the time between start and latest split, between start - * and stop, or the time between the start and the moment this method is called.

        + *

        + * This is either the time between the start and the moment this method is called, or the amount of time between + * start and stop. + *

        * * @return the time in milliseconds */ public long getTime() { - if (stopTime == -1) { - if (startTime == -1) { - return 0; - } - return (System.currentTimeMillis() - this.startTime); + if (this.runningState == STATE_STOPPED || this.runningState == STATE_SUSPENDED) { + return this.stopTime - this.startTime; + } else if (this.runningState == STATE_UNSTARTED) { + return 0; + } else if (this.runningState == STATE_RUNNING) { + return System.currentTimeMillis() - this.startTime; } - return (this.stopTime - this.startTime); + throw new RuntimeException("Illegal running state has occured. "); } /** - *

        Gets a summary of the time that the stopwatch recorded as a string.

        + *

        + * Get the split time on the stopwatch. + *

        * - *

        The format used is ISO8601-like, - * hours:minutes:seconds.milliseconds.

        + *

        + * This is the time between start and latest split. + *

        * + * @return the split time in milliseconds + * + * @throws IllegalStateException + * if the StopWatch has not yet been split. + * @since 2.1 + */ + public long getSplitTime() { + if (this.splitState != STATE_SPLIT) { + throw new IllegalStateException("Stopwatch must be split to get the split time. "); + } + return this.stopTime - this.startTime; + } + + /** + * Returns the time this stopwatch was started. + * + * @return the time this stopwatch was started + * @throws IllegalStateException + * if this StopWatch has not been started + * @since 2.4 + */ + public long getStartTime() { + if (this.runningState == STATE_UNSTARTED) { + throw new IllegalStateException("Stopwatch has not been started"); + } + return this.startTime; + } + + /** + *

        + * Gets a summary of the time that the stopwatch recorded as a string. + *

        + * + *

        + * The format used is ISO8601-like, hours:minutes:seconds.milliseconds. + *

        + * * @return the time as a String */ public String toString() { - return DurationFormatUtils.formatISO(getTime()); + return DurationFormatUtils.formatDurationHMS(getTime()); } + /** + *

        + * Gets a summary of the split time that the stopwatch recorded as a string. + *

        + * + *

        + * The format used is ISO8601-like, hours:minutes:seconds.milliseconds. + *

        + * + * @return the split time as a String + * @since 2.1 + */ + public String toSplitString() { + return DurationFormatUtils.formatDurationHMS(getSplitTime()); + } + } Index: 3rdParty_sources/commons-lang/org/apache/commons/lang/time/package.html =================================================================== diff -u -r3d0166b43ce990fd9f27c433a1c58cc61085ecf4 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/commons-lang/org/apache/commons/lang/time/package.html (.../package.html) (revision 3d0166b43ce990fd9f27c433a1c58cc61085ecf4) +++ 3rdParty_sources/commons-lang/org/apache/commons/lang/time/package.html (.../package.html) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -1,3 +1,19 @@ +

        @@ -11,5 +27,6 @@

      • StopWatch - a duration timer
      @since 2.0 +

      These classes are immutable (and therefore thread-safe) apart from {@link StopWatch}.

      Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r36ff2c2d4199901f289b1d35e61af2b9a2a93af2 -r6aa36ddefbf750d2b246992fee82df738a66eefa --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 36ff2c2d4199901f289b1d35e61af2b9a2a93af2) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision 6aa36ddefbf750d2b246992fee82df738a66eefa) @@ -11,7 +11,7 @@ Commons HttpClient 3.0 -Commons Lang 2.0 +Commons Lang 2.6 Commons IO 2.1